Smart Relationship with Smart collection - isFilterable not working

The fields "mandatory"Preformatted text not work , in my smart collection

Project name: .Sergic
Team name: gestion
Environment name: Développement
Agent (forest package) name & version: 9
Database type: Postgres
Recent changes made on your end if any: upgrade to v9

Hello Soufiane,

Could you please tell us more about the configuration of your smart collection?

Filters on smart collections need to be handled by your code, because they don’t directly rely on sequelize like other collections.

So my assumption here is that this particular filter is not correctly handled by the customization code that is present on your project to list records.

collection('missingContractCategories', {
    isSearchable: true,
  actions: [],
  fields: [
    {
    field: 'id',
    type: 'String'
  },{
    field: 'placeReference',
    type: 'String',
     isFilterable: true
  }, {
    field: 'display_name',
    type: 'String',
    isFilterable: true,
   
  }, {
    field: 'mandatory',
    type: 'Boolean',
    isFilterable: true,
   // isSortable: true,
    
      //  filter: async ({ condition, where }) => {
      // const positive = condition.operator === 'equal' && !!condition.value || condition.operator === 'not_equal' && !condition.value
      //    console.log("🚀 ~ file: missing-contract-categories.js:35 ~ filter: ~ positive:", positive)
      //    return { mandatory: positive };
      // }
    
    }, {
    field: 'state',
    isFilterable: true,
    // type: 'String',
      type: 'Enum',
     enums: ['active',
        'canceled',
        'pending_cancellation',
        'pending',
        'completed',
        'archived'
      ],
      isFilterable: true,
    },
router.get('/missingContractCategories', permissionMiddlewareCreator.list(), (request, response, next) => {
  
  const recordSerializer = new RecordSerializer({ name: 'missingContractCategories' });


  // Get query parameters information
  const pageSize = Number(request.query.page.size) || 20;
  const page = Number(request.query.page.number) || 1;
  const from = (page - 1) * pageSize;
  const to = from + pageSize;
  //let search = request.query.search;
  let sort = request.query.sort;
  let filters;
  try {
    filters = request.query?.filters && JSON.parse(request.query.filters);
    console.log("🚀 ~ file: missing-contract-categories.js:25 ~ router.get ~ request.query?.filters:", request.query?.filters)
  } catch (e) {
    filters = undefined;
  }
  //console.log("🚀 ~ file: missing-contract-categories.js ~ line 33 ~ router.get ~ filters", filters)
  axios.get(
    `${API_URL}/forest_admin/missing_categories`, {
    headers: {
      'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
      'X-CURRENT-USER-EMAIL': request.user.email
    }
  }).then(async res => {
    const dataToSend = [];
    res.data.map(item => {
      const placeReference = item.sergic_id_full;
      item.missing_categories.map(categorie => { 
        const cat = {
          id: `${item.id}-${categorie.id}`,
          placeReference: placeReference,
          display_name: categorie.display_name,
          mandatory: categorie.mandatory,
          state: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.state : '' ,
          reference: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.reference : '',
          end_date: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.end_date : null,
          origin_date: categorie.last_maintenance_contract ? categorie.last_maintenance_contract .origin_date : null,
          amount: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.amount: '',
          repartition_key_id: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.repartition_key_id: '',
          automatic_renewal: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.automatic_renewal : '',
          notice_in_month: categorie.last_maintenance_contract ? categorie.last_maintenance_contract.notice_in_month: ''

        };
         dataToSend.push(cat);

      });
    });
     
    response.send({

      ...(await recordSerializer.serialize(dataToSend.slice(from, to))),
      meta: { count: dataToSend.length }
    });
  }).catch(err => {
    if (err.response?.data?.error?.message) {
      response.status(400).send(err.response.data.error.message);
    } else {
      response.status(400).send('Erreur inattendue, veuillez contacter votre administrateur.');
    }
  });
});

Thanks for sharing the code.

It seems that this code contains the beginning of a support for filter. If you want filters to work on the frontend, the logic has to be implemented in your route handler, the same way it is done for search.

The query parameter filter contains the JSON representation of a filter:

// If there is only 1 filter, the filter is an object such as:
{
  "field":"mandatory",
  // Supported operators depend on the field's type
  "operator":"equal",
  "value":"true"
}

// If there are multiple filters, it contains a representation
// of an aggregation of filters
{
  // Can be "and" or "or"
  "aggregator":"and",
  "conditions":[
    {"field":"mandatory","operator":"equal","value":"bar"}, 
    {"field":"foo","operator":"equal","value":"bar"}
  ]
}

The frontend only allows to select one level of aggregations.

Thank you for your feedback, The filter still doesn’t work; it is normal that field “mandatory” in forestadmin-shema.json , “isFilterable” is false ?

I’ve made the test locally, by just setting isFilterable on a field, it correctly switches the value in the schema.

If it’s not the case on your side, could you please check the value of NODE_ENV on your agent? If the value is set to production, the schema is not generated at startup.

It seems that there is a bug on our side though, because activating the value of isFilterable does not change the behavior on the app. I’m investigating this problem.

scripts": {
“dev”: “SET NODE_ENV=development&& nodemon ./server.js”,
“staging”: “NODE_ENV=staging node ./server.js”,
“prod”: “NODE_ENV=production node ./server.js”
}

If you launch the script dev, does it correctly generates the schema?

No, the shema is not correctly generated

yndic-one> nodemon ./server.js
[nodemon] 2.0.20
[nodemon] to restart at any time, enter rs
[nodemon] watching path(s): .
[nodemon] watching extensions: js,mjs,json
[nodemon] starting node ./server.js
No env file present for the current environment: undefined
Falling back to .env config
┌───────────────────────────────────┐
│ New version of nodemon available! │
│ Current Version: 2.0.20 │
│ Latest Version: 3.0.3 │
└───────────────────────────────────┘

Is the value of NODE_ENV set in your .env file?

Can you console.log the value of process.env.NODE_ENV in your server.js file?

I can see from your command line that you start your server with nodemon ./server.js whereas you should start it with npm run dev or yarn dev in order to correctly set environment variables before running the server.

:rocket: ~ process.env.NODE_ENV: development in server.js file

.env.development: development
.env.staging: staging
.env.production: production

Ok, weird. Can you share me the lines of logs that are written in the console when starting your agent?

Forest Admin’s code should logs some lines indicating what is done regarding the schema (if it’s generated, if it is sent to our servers).

\syndic-one-rebirth> npm run dev

syndic-one-rebirth@0.0.1 dev
SET NODE_ENV=development&& nodemon ./server.js

[nodemon] 2.0.15
[nodemon] to restart at any time, enter rs
[nodemon] watching path(s): .
[nodemon] watching extensions: js,mjs,json
[nodemon] starting node ./server.js
Your application is listening on port 3310.
[forest] :deciduous_tree::deciduous_tree::deciduous_tree: Checking need for apimap update…

Don’t you have anything after this line?

Your admin panel is available here: https://app.forestadmin.com/Sergic/Dev/Gestion
[forest] :deciduous_tree::deciduous_tree::deciduous_tree: No change in apimap, nothing sent to Forest.

Ok, if I take a look at what we have on our side, I can see that isFilterable is correctly set to true on your schema for the field mandatory in the collection missingContractCategories.

You have another field named mandatory in the collection categories (first to come in the schema), with isFilterable = false.

So I would say that the schema is correctly updating on your side.

Now the remaining issue comes from our side, with the filters not being correctly displayed when changing the property isFilterable on fields. A fix is on its way.

After the release of the fix, you’ll have to set isFilterable: false in your field’s declaration, start the server for the schema to be sent, then switch back the value to true and start the server again.

Thanks, when is the release of the fix ?

We’re working on the fix, sorry for the delay.

In the meantime, I think you can apply a workaround to fix this issue:

  1. Comment the whole declaration of the field mandatory
  2. Start your server to send the schema, then stop your server
  3. Add this field back in your collection
  4. Start your server again.

It should do the trick, unblock you and give us some time to release and correctly test the fix.

Let me know if it works for you.

The fix is now live.

Make sure to make at least one modification that will change the schema, this way the schema will be sent again and the property isFilterable will be correctly be updated in your app.