ForestAdmin/forest-express

View on GitHub
src/services/schema-file-updater.js

Summary

Maintainability
A
2 hrs
Test Coverage
A
100%
const _ = require('lodash');
const { parameterize } = require('../utils/string');
const { prettyPrint } = require('../utils/json');

class SchemaFileUpdater {
  constructor({ logger, fs }) {
    this.logger = logger;
    this.fs = fs;
  }

  static formatObject(object, attributes) {
    const objectFormated = {};
    const objectOrdered = _.sortBy(
      Object.keys(object),
      (attribute) => attributes.indexOf(attribute),
    );
    _.each(objectOrdered, (attribute) => {
      if (attributes.includes(attribute)) {
        objectFormated[attribute] = object[attribute];
      }
    });
    return objectFormated;
  }

  static setDefaultValueIfNecessary(object, property, value) {
    if (!Object.prototype.hasOwnProperty.call(object, property)) {
      object[property] = value;
    }
  }

  static cleanFields(fields) {
    fields = fields.filter((field) => field.field);
    fields.forEach((field) => {
      if (field.defaultValue === undefined) {
        field.defaultValue = null;
      }
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'type', 'String');
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isRequired', false);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isPrimaryKey', false);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isReadOnly', false);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isSortable', true);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isFilterable', true);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isVirtual', false);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'description', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'reference', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'inverseOf', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'relationships', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'enums', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'validations', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(field, 'integration', null);

      field.validations = field.validations || [];
      field.validations.forEach((validation) => {
        if (validation.message === undefined) {
          validation.message = null;
        }
        SchemaFileUpdater.setDefaultValueIfNecessary(validation, 'value', null);
      });
    });

    return fields;
  }

  static cleanSegments(segments) {
    return segments.filter((segment) => segment.name);
  }

  cleanActions(actions) {
    actions = actions.filter((action) => action.name);
    actions.forEach((action) => {
      if (action.global) {
        this.logger.warn(`REMOVED OPTION: The support for Smart Action "global" option is now removed. Please set "type: 'global'" instead of "global: true" for the "${action.name}" Smart Action.`);
      }

      if (action.type && !_.includes(['bulk', 'global', 'single'], action.type)) {
        this.logger.warn(`Please set a valid Smart Action type ("bulk", "global" or "single") for the "${action.name}" Smart Action.`);
        action.type = null;
      }

      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'endpoint', `/forest/actions/${parameterize(action.name)}`);
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'httpMethod', 'POST');
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'fields', []);
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'redirect', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'baseUrl', null);
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'type', 'bulk');
      SchemaFileUpdater.setDefaultValueIfNecessary(action, 'download', false);

      // NOTICE: Set a position to the Smart Actions fields.
      action.fields = action.fields.filter((field) => field.field);
      _.each(action.fields, (field, position) => {
        field.position = position;

        if (field.defaultValue === undefined) {
          field.defaultValue = null;
        }
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'type', 'String');
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'isRequired', false);
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'description', null);
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'reference', null);
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'enums', null);
        SchemaFileUpdater.setDefaultValueIfNecessary(field, 'widget', null);
      });
    });

    return actions;
  }

  cleanCollection(collection) {
    if (_.isNil(collection.isSearchable)) {
      collection.isSearchable = true;
    }
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'onlyForRelationships', false);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'isVirtual', false);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'isReadOnly', false);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'paginationType', 'page');
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'icon', null);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'nameOld', collection.name);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'integration', null);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'fields', []);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'segments', []);
    SchemaFileUpdater.setDefaultValueIfNecessary(collection, 'actions', []);

    collection.fields = SchemaFileUpdater.cleanFields(collection.fields);
    collection.segments = SchemaFileUpdater.cleanSegments(collection.segments);
    collection.actions = this.cleanActions(collection.actions);
  }

  static formatFields(collection, serializerOptions) {
    collection.fields = collection.fields || [];
    collection.fields = collection.fields.map((field) => {
      const fieldFormatted = SchemaFileUpdater
        .formatObject(field, serializerOptions.fields.attributes);

      fieldFormatted.validations = fieldFormatted.validations.map((validation) =>
        SchemaFileUpdater.formatObject(validation, serializerOptions.validations.attributes));
      return fieldFormatted;
    });
    collection.fields = _.sortBy(collection.fields, ['field', 'type']);
  }

  static formatSegments(collection, serializerOptions) {
    collection.segments = collection.segments || [];
    collection.segments = collection.segments.map((segment) =>
      SchemaFileUpdater.formatObject(segment, serializerOptions.segments.attributes));
    collection.segments = _.sortBy(collection.segments, ['name']);
  }

  static formatActions(collection, serializerOptions) {
    collection.actions = collection.actions || [];
    collection.actions = collection.actions.map((action) => {
      const actionFormatted = SchemaFileUpdater
        .formatObject(action, serializerOptions.actions.attributes);
      actionFormatted.fields = actionFormatted.fields || [];
      actionFormatted.fields = actionFormatted.fields.map((field) =>
        SchemaFileUpdater.formatObject(field, serializerOptions.actions.fields.attributes));

      actionFormatted.hooks = SchemaFileUpdater
        .formatActionHooks(actionFormatted.hooks, action, serializerOptions);

      return actionFormatted;
    });
    collection.actions = _.sortBy(collection.actions, ['name']);
  }

  static formatActionHooks(hooks, action, serializerOptions) {
    hooks = SchemaFileUpdater
      .formatObject(hooks || {}, serializerOptions.actions.hooks.attributes);
    hooks.load = Boolean(action.hooks && (typeof action.hooks.load === 'function'));
    hooks.change = action.hooks
      && action.hooks.change
      && typeof action.hooks.change === 'object'
      ? Object.keys(action.hooks.change)
      : [];
    return hooks;
  }

  update(filename, collections, meta, serializerOptions) {
    collections = collections.map((collection) => {
      this.cleanCollection(collection);
      const collectionFormatted = SchemaFileUpdater
        .formatObject(collection, serializerOptions.attributes);

      SchemaFileUpdater.formatFields(collectionFormatted, serializerOptions);
      SchemaFileUpdater.formatSegments(collectionFormatted, serializerOptions);
      SchemaFileUpdater.formatActions(collectionFormatted, serializerOptions);

      return collectionFormatted;
    });
    collections.sort((collection1, collection2) =>
      collection1.name.localeCompare(collection2.name));

    const schema = { collections, meta };
    this.fs.writeFileSync(filename, prettyPrint(schema));
    return schema;
  }
}

module.exports = SchemaFileUpdater;