TypeError Cannot read property isCompositePrimary of undefined

Expected behavior

Hi there folks, we’re currently using a beforeCreate hook inside the models for one of our collections. The beforeCreate hook looks like this:

image

When I try and insert some units in my development environment then this beforeCreate hook is called and there is no problem. However, when I test an endpoint this hook also gets called and throws a TypeError when it reaches await getter.get(.

I tried digging into this error to find out more:
This function appears to the source of the error:


When I use the development environment the Interface.Schemas.schemas returns an object with all the collection schemas in our database.
When I use the test environment then this Interface.Schemas.schemas returns an object with only some of the collection schemas. So therefore schema becomes undefined.

What is the current behavior?

Failure Logs

TypeError: Cannot read property ‘isCompositePrimary’ of undefined
at ResourceFinder.perform (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/forest-express-sequelize/dist/services/resource-finder.js:45:16)
at ResourceGetter.perform (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/forest-express-sequelize/dist/services/resource-getter.js:15:52)
at RecordGetter.get (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/forest-express-sequelize/node_modules/forest-express/dist/services/exposed/record-getter.js:36:10)
at Function. (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/models/units.js:77:34)
at /home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/lib/hooks.js:130:19
at tryCatcher (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/util.js:16:23)
at Object.gotValue (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/reduce.js:166:18)
at Object.gotAccum (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/reduce.js:155:25)
at Object.tryCatcher (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromiseCtx (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/promise.js:641:10)
at _drainQueueStep (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/async.js:97:12)
at _drainQueue (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/async.js:86:9)
at Async.Object..Async._drainQueues (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/async.js:102:5)
at Async.drainQueues (/home/mitch/Desktop/oak-national-data-tools/lesson-management-system/app/node_modules/sequelize/node_modules/bluebird/js/release/async.js:15:14)
at processTicksAndRejections (node:internal/process/task_queues:96:5)

  at Function.<anonymous> (models/units.js:80:15)

Error logs look like above :point_up:

Please include any relevant log snippets, if necessary.

Context

Please provide any relevant information about your setup.

  • Express Version: “express”: “^4.17.1”,
  • Sequelize Version: “forest-express-sequelize”: “^7.3.1”,
  • Database Dialect: postgres
  • Database Version: postgres:12
  • Project Name: Oak National Academy

Hello @Mitchell_Lloyd,

Thank you for bringing this to us.

Before going further, could you please detail what you mean by “test environment” and “test and endpoint”?
I get that “development environment” is the name of the environment within Forest Admin, am I right?

Hi @Guillaume_Deslandes,
Thankyou for your message of course - in the test environment I’m calling an endpoint that is associated with a Smart Action:


When I’m in development in the Forest Admin UI then I’m just uploading the exact same CSV file expect through the UI:

Thank you for the details.

Here are more thoughts / questions:

When running your tests, do you also run a new Forest agent (maybe on another machine / CI)? Or is it locally with your tests hitting the same host/port?

It could be that the databases used in both cases are not identical and some model missing?

In your initial post you wrote “returns an object with only some of the collection schemas”. Did you identify which models are missing?

Is the model.name value the same in both cases?

Did you ensure testPayload is identical with what is sent by Forest Admin to the agent?

Hi @Guillaume_Deslandes,
So the test and development environment both point to the same local database.
model.name is topics in both cases.
When I run Object.keys on Interface.Schemas.schemas then it evaluates to:
[ 'asset_ingests', 'curriculumDocuments', 'days', 'fieldOfStudy', 'keyStages', 'lesson_video_ids', 'lessons', 'liveLessons', 'liveLiveProgrammeOfStudyUnit', 'liveUnitLessons', 'liveUnits', 'liveVideos', 'paperTiers', 'phases', 'programmeOfStudyUnit', 'programmeOfStudy', 'quizTypes', 'resourceTypes', 'slides', 'teacherFieldOfStudies', 'teacherPhases', 'teacherTherapies', 'teachers', 'therapies', 'therapyUnits', 'timetabledLessons', 'timetabledWeeks', 'timetables', 'topicTypes', 'topics', 'unitLessons', 'units', 'video_ingests', 'videos', 'weeks', 'years' ]
However, when testing it evaluates to:
[ 'asset_ingests', 'curriculumDocuments', 'days', 'fieldOfStudy', 'keyStages', 'lesson_video_ids' ]
which is the first 6 in the previous array.
I did ensure the testPayload is the same as the csv file in development - I checked the base64 encoded string several times as it is the same in both environments.

The forest Smart Action is also defined as follows:

Hi @Mitchell_Lloyd :wave:, it makes me think of an initialization problem.
How do you start your server for your tests?
Are the models correctly imported on the ./middlewares/forestadmin.js file?

To see that, you can log your models like this in the previous file

const chalk = require('chalk');
const path = require('path');
const Liana = require('forest-express-sequelize');
const { objectMapping, connections } = require('../models');

console.log(connections.default.models);

module.exports = async function forestadmin(app) {
  app.use(await Liana.init({
    configDir: path.join(__dirname, '../forest'),
    envSecret: process.env.FOREST_ENV_SECRET,
    authSecret: process.env.FOREST_AUTH_SECRET,
    objectMapping,
    connections,
  }));

  console.log(chalk.cyan('Your admin panel is available here: https://app.forestadmin.com/projects'));
};

If you doesn’t change the ./config/databases.js file and the ./models/index.js file this logs should give us the imported models by your tests.

Cheers @Arnaud_Moncel I’ll take a look and then get back to you

1 Like

Hi @Arnaud_Moncel I tried to log and it appears to give me all the models for the database. I can’t see anything missing when I log connections.default.models. Also I’ve defined my routes as below:
image

I don’t think it is a miss configuration of your route or other, because it is working on your dev right?
We generate the Interface.Schemas.schemas following your models passed in the liana.init function.
And like you said all the models are present.
I wondering if this async process is not full-filled before you run your tests.
That’s why i ask how do you start your serveur before running your tests.
May be a way to think about it here.

Ah yes this fixed the problem. I’m using supertest to test the endpoints. I’m requiring in the express app and the asynchronous call to Liana is pending in the background. At the moment, I’m just delaying the test run by x miliseconds - is there any other way of checking to ensure that Liana has resolved before running the test case.
Thankyou so much for your help :slight_smile:

Yes but it require some code modification on some files, and if I show you the way, your code will no longer respect the Forest Admin standard and i’m not sure it is a good idea for future supports.

BUT I have another way who need less modification.
In your app.js file

const app = express();

app.isForestReady = (() => {
  const deferred = {};
  deferred.promise = new Promise((resolve) => { deferred.resolve = resolve });
  return deferred;
})();

// ForestAdmin code

In the ./middlewares/forestadmin.js file

module.exports = async function forestadmin(app) {
  // ForestAdmin code

  console.log(chalk.cyan('Your admin panel is available here: https://app.forestadmin.com/projects'));

  app.isForestReady.resolve();
};

And in your server.js file

// ForestAdmin imports
const app = require('./app');

// surround all the code below inside an async function
(async () => {
  const port = normalizePort(process.env.PORT || process.env.APPLICATION_PORT || '3310');
  app.set('port', port);

  await app.isForestReady.promise;

  // ForestAdmin code
})();

After that your server listen only when the liana is initialize.
Let me know if that help.