Reference key display issue

I created a smart field and defined it as a reference field

I create a new record in another collection, linking it with one of the key records (loaded with hooks/load)
Screenshot 2021-01-04 181745

If i go in the summary view of the newly created record, everything looks fine
Screenshot 2021-01-04 181817

BUT, if i display the records WITHIN its parent (related data)

It the records collection, everything is displayed normally

The “get” method for the smart reference field is not even asynchronous:

get: repartitionKey => ${repartitionKey.letter} - ${repartitionKey.displayName} - ${repartitionKey.feesTotal}

(removed some code for example purpose)

Expected behavior

Reference key fields displayed as in the collection

Actual behavior

Some data are not retrieved.

Hi @JeremyV :wave: thank you to your feedback.
Can you give me more information about the repartionKey model,
and the entire code of the get method please.

repartitionKey Model:

const RepartitionKeys = sequelize.define('repartitionKeys', {
    letter: {      type: DataTypes.STRING,    },
    displayName: {      type: DataTypes.STRING,    },
    feesTotal: {      type: DataTypes.INTEGER,    },
    createdAt: {      type: DataTypes.DATE,    },
    updatedAt: {      type: DataTypes.DATE,    },
    sergicId: {      type: DataTypes.STRING,    },
  }, {
    tableName: 'repartition_keys',
    underscored: true,
    schema: process.env.DATABASE_SCHEMA,
  });

get method:

get: repartitionKey => repartitionKey && repartitionKey.letter && repartitionKey.displayName && repartitionKey.feesTotal ? `${repartitionKey.letter} - ${repartitionKey.displayName} - ${repartitionKey.feesTotal}` : `${repartitionKey.letter} - ${repartitionKey.displayName}` 

Hi,

Just in case, are you sure that you are using the last releases?
I raised the same issue recently, and it was solved in the last release.

Best

Which package should I upgrade ? I upgraded forest-express and forest-express-sequelize quite recently.

I had to update to the latest ones:

  • forest-express-sequelize 6.6.3
  • forest-express 7.9.4

The versions of forest-express-sequelize from 6.6.1 to 6.6.2 have problems with smart fields.

we have the same versions.

"forest-express": "^7.9.4",
"forest-express-sequelize": "^6.6.3"

The smart fields used as reference keys do display correctly, except in the specific context of related data.

I’m not able to reproduce … I think I’m missing information.
Maybe a small video record of your workflow should be help.

I think i got it : the object attributes’names are truncated to 6 characters.

dataValues: {
    id: '24',
    letter: 'A',
    displa: 'Chauffage central',
    feesTo: 2000,
    ...
  }

but i call

repartitionKey.letter
repartitionKey.displayName
repartitionKey.feesTotal

Hello @JeremyV,

Indeed this is a bit weird. Do you have an idea why the keys are truncated to 6 characters?

Did you try with displa and feesTo?

Let me know :slight_smile:

Surprisingly it doesn’t work when i call displa and feesTo.

But by doing this, the display in the collection and the summary view (which require displayName and FeesTotal) won’t work anymore.

In a nutshell :

  • when the route
    /forest/integrationBudgets/12/relationships/integrationBudgetIntegrationBudgetEntries is called, the attributes are truncated.
  • when the route /forest/integrationBudgetEntries/22 is called, the attributes aren’t truncated and displayed as normal
  • when the route /forest/integrationBudgetEntries is called, everything is displayed normally

Indeed, this is surprising.

Could you share with us your models declaration, especially the one for integrationBudgetIntegrationBudgetEntries?

Also the app.js would be very helpful in order to see your middlewares. Maybe we could understand why the keys are truncated to 6 characters.

Can we also see the code of your hooks/load?

Did you customize anything?

Thank you,

const IntegrationBudgets = sequelize.define('integrationBudgets', {
    partnerComment: {      type: DataTypes.STRING,    },
    refusalComment: {      type: DataTypes.STRING,    },
    state: {      type: DataTypes.ENUM(['initializing', 'waiting', 'accepted', 'refused']),},
    createdAt: {      type: DataTypes.DATE,    },
    updatedAt: {      type: DataTypes.DATE,    },
  }, {
    tableName: 'integration_budgets',
    underscored: true,
    schema: process.env.DATABASE_SCHEMA,
  });

  IntegrationBudgets.associate = (models) => {
    IntegrationBudgets.belongsTo(models.places, {
      foreignKey: {
        name: 'placeIdKey',
        field: 'place_id',
      },
      as: 'place',
    });
    IntegrationBudgets.belongsTo(models.integrations, {
      foreignKey: {
        name: 'placeIdKey',
        field: 'place_id',
      },
      as: 'integration',
    });
    IntegrationBudgets.hasMany(models.integrationBudgetEntries, {
      foreignKey: {
        name: 'integrationBudgetIdKey',
        field: 'integration_budget_id',
      },
      as: 'integrationBudgetIntegrationBudgetEntries',
    });
  };
  const IntegrationBudgetEntries = sequelize.define('integrationBudgetEntries', {
    displayName: {      type: DataTypes.STRING,    },
    amount: {      type: DataTypes.INTEGER,    },
    createdAt: {      type: DataTypes.DATE,    },
    updatedAt: {      type: DataTypes.DATE,    },
  }, {
    tableName: 'integration_budget_entries',
    underscored: true,
    schema: process.env.DATABASE_SCHEMA,
  });

  IntegrationBudgetEntries.associate = (models) => {
    IntegrationBudgetEntries.belongsTo(models.repartitionKeys, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKey',
    });
    IntegrationBudgetEntries.belongsTo(models.integrationBudgets, {
      foreignKey: {
        name: 'integrationBudgetIdKey',
        field: 'integration_budget_id',
      },
      as: 'integrationBudget',
    });
  };
  return IntegrationBudgetEntries;
};

The /forest/integration-budget-entries.js file is untouched.
The /forest/integration-budgets.js is as following, 2 smart actions :

collection('integrationBudgets', {
  actions: [{
    name: 'Demander validation budget',
    type: 'single',
  }, {
    name: 'Ajouter ligne budget',
    type: 'single',
    fields: [{
      field: 'Clef de repartition',
      type: 'Enum',
      enums: []
    }, {
      field: 'Libelle',
      type: 'String',
    }, {
      field: 'Montant',
      type: 'Number'
    }],
    hooks: {
      load: ({ fields, record }) => models.repartitionKeys.findAll({
        where: { place_id: record.place.id }
      }).then(repartitionKeys => {
        fields['Clef de repartition'].enums = repartitionKeys.map(repartitionKey => `${repartitionKey.letter} - ${repartitionKey.displayName} - ${repartitionKey.feesTotal}`);
        return fields;
      })
    }
  }],
  fields: [],
  segments: [],
});

In order to associate the budget entry to the repartition key, i retrieve in the smart action route the repartitionKeyId, as follows :

router.post('/actions/ajouter-ligne-budget', permissionMiddlewareCreator.smartAction(), (request, response) => {
  const recordCreator = new RecordCreator(models.integrationBudgetEntries);
  const data = request.body.data.attributes.values;
  const repartitionKeyAttributes = data['Clef de repartition'].split(' - ');

  models.repartitionKeys.findOne({
    where: {
      letter: repartitionKeyAttributes[0],
      display_name: repartitionKeyAttributes[1],
      fees_total: repartitionKeyAttributes[2]
    }
  }).then(repartitionKey => {
    const integration_budget_id = request.body.data.attributes.ids[0];

    const body = {
      integration_budget_entry: {
        integration_budget_id,
        display_name: data.Libelle,
        repartition_key_id: repartitionKey.id,
        amount: data.Montant
      }
    };

    axios.post(`${API_URL}/oauth/token`, {
      'scope': process.env.SCOPE,
      'client_id': process.env.CLIENT_ID,
      'client_secret': process.env.CLIENT_SECRET,
      'grant_type': process.env.GRANT_TYPE
    }).then(authenticationResponse => {
      const accessToken = authenticationResponse.data.access_token;
      axios.post(`${API_URL}/forest_admin/integration_budget_entries`, body, {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'X-CURRENT-USER-EMAIL': request.user.email
        },
      }).then(async res => {
        response.send(await recordCreator.serialize(res.data));
      }).catch(err => {
        response.status(400).send(err.response.data.error.message);
      });
    }).catch(authenticationError => {
      response.status(authenticationError.response.status).send(authenticationError.message);
    });
  });
});

… but it has nothing to do with the data display in itself.

I didn’t customize any of the previously mentioned GET routes.

I have full GET access (directly via sequelize), I need to call APIs for any POST/PUT/DELETE call.

Thank you @JeremyV,

Could you also share your model for repartitionKeys please?
I think it will help us more :slight_smile:

@Guillaume_Cisco you already have it above.

Relations are :

RepartitionKeys.associate = (models) => {

    RepartitionKeys.belongsTo(models.places, {
      foreignKey: {
        name: 'placeIdKey',
        field: 'place_id',
      },
      as: 'place',
    });

    RepartitionKeys.belongsTo(models.integrations, {
      foreignKey: {
        name: 'placeIdKey',
        field: 'place_id',
      },
      as: 'integration',
    });

    RepartitionKeys.hasMany(models.accountPlaceEntries, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyAccountPlaceEntries',
    });

    RepartitionKeys.hasMany(models.generalMeetings, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyGeneralMeetings',
    });

    RepartitionKeys.hasMany(models.parcelRepartitionKeys, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyParcelRepartitionKeys',
    });

    RepartitionKeys.hasMany(models.resolutions, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyResolutions',
    });

    RepartitionKeys.hasMany(models.buildingRepartitionKeys, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyBuildingRepartitionKeys',
    });

    RepartitionKeys.hasMany(models.identityRepartitionKeys, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyIdentityRepartitionKeys',
    });

    RepartitionKeys.hasMany(models.contractRepartitionKeys, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyContractRepartitionKeys',
    });

    RepartitionKeys.hasMany(models.decisions, {
      foreignKey: {
        name: 'repartitionKeyIdKey',
        field: 'repartition_key_id',
      },
      as: 'repartitionKeyDecisions',
    });
  };
  return RepartitionKeys;
};

Sorry, forgot to include it.

const express = require('express');
const requireAll = require('require-all');
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const cors = require('cors');
const jwt = require('express-jwt');
const morgan = require('morgan');
const { errorHandler, logger } = require('forest-express');
const {
  ensureAuthenticated,
  PUBLIC_ROUTES,
} = require('forest-express-sequelize');

const app = express();

app.use(morgan('tiny'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

let allowedOrigins = [/\.forestadmin\.com$/];

if (process.env.CORS_ORIGINS) {
  allowedOrigins = allowedOrigins.concat(process.env.CORS_ORIGINS.split(','));
}

app.use(cors({
  origin: allowedOrigins,
  allowedHeaders: ['Authorization', 'X-Requested-With', 'Content-Type'],
  maxAge: 86400, // NOTICE: 1 day
  credentials: true,
}));

app.use(jwt({
  secret: process.env.FOREST_AUTH_SECRET,
  credentialsRequired: false,
}));

app.use('/forest', (request, response, next) => {
  if (PUBLIC_ROUTES.includes(request.url)) {
    return next();
  }
  return ensureAuthenticated(request, response, next);
});

requireAll({
  dirname: path.join(__dirname, 'routes'),
  recursive: true,
  resolve: (Module) => app.use('/forest', Module),
});

requireAll({
  dirname: path.join(__dirname, 'middlewares'),
  recursive: true,
  resolve: (Module) => Module(app),
});

app.use(errorHandler({ logger }));

module.exports = app;

@Guillaume_Cisco do you need the content of the /middlewares folder ?

Hello @JeremyV :wave:

I’m taking care of the issue from now one. I have every information I need to investigate. I’m currently trying to reproduce.

I will keep you in touch as soon as I have something :slight_smile:

Steve

1 Like