Source: argos-sdk/src/Models/_SDataModelBase.js

import declare from 'dojo/_base/declare';
import SDataStore from '../Store/SData';
import Deferred from 'dojo/Deferred';
import all from 'dojo/promise/all';
import when from 'dojo/when';
import string from 'dojo/string';
import utility from '../Utility';
import _ModelBase from './_ModelBase';
import MODEL_TYPES from './Types';

/**
 * @class argos.Models._SDataModelBase
 */
const __class = declare('argos.Models.SDataModelBase', [_ModelBase], /** @lends argos.Models.SDataModelBase# */{
  queryModels: null,
  ModelType: MODEL_TYPES.SDATA,

  _getQueryModelByName: function _getQueryModelByName(name) {
    if (!this.queryModels) {
      console.warn('No query Models defined');// eslint-disable-line
    }

    const results = this.queryModels.filter(model => model.name === name);
    return results[0];
  },
  init: function init() {
    this.inherited(arguments);

    if (!this.queryModels) {
      this.queryModels = this._createCustomizedLayout(this.createQueryModels(), 'queryModel');
      this.queryModels.forEach((queryModel) => {
        queryModel.querySelect = this._createCustomizedLayout(queryModel.querySelect, `${queryModel.name}/querySelect`);
        queryModel.queryInclude = this._createCustomizedLayout(queryModel.queryInclude, `${queryModel.name}/queryInclude`);
      });
    }
  },
  createQueryModels: function createQueryModels() {
    return [];
  },
  getOptions: function getOptions(options) {
    const tempOptions = {};
    if (options) {
      if (options.select) tempOptions.select = options.select;
      if (options.include) tempOptions.include = options.include;
      if (options.orderBy) tempOptions.orderBy = options.orderBy;
      if (options.contractName) tempOptions.contractName = options.contractName;
      if (options.resourceKind) tempOptions.resourceKind = options.resourceKind;
      if (options.resourceProperty) tempOptions.resourceProperty = options.resourceProperty;
      if (options.resourcePredicate) tempOptions.resourcePredicate = options.resourcePredicate;
      if (options.queryArgs) tempOptions.queryArgs = options.queryArgs;
      if (options.start) tempOptions.start = options.start;
      if (options.count) tempOptions.count = options.count;
    }

    return tempOptions;
  },
  getId: function getId(options) {
    return options && (options.id || options.key);
  },
  getPicklists: function getPicklists() {
    return App.picklistService.requestPicklistsFromArray(this.picklists);
  },
  buildQueryExpression: function _buildQueryExpression(query, options) {
    const passed = options && (options.query || options.where);
    return passed ? query ? '(' + utility.expand(this, passed) + ') and (' + query + ')' : '(' + utility.expand(this, passed) + ')' : query;// eslint-disable-line
  },
  createStore: function createStore(type, service) {
    const app = this.get('app');
    const config = this;
    const typedConfig = this._getQueryModelByName(type);

    return new SDataStore({
      service: service || app.getService(false),
      contractName: config.contractName,
      resourceKind: config.resourceKind,

      resourceProperty: typedConfig.resourceProperty,
      resourcePredicate: typedConfig.resourcePredicate,
      include: typedConfig.queryInclude,
      select: typedConfig.querySelect,
      where: typedConfig.queryWhere,
      queryArgs: typedConfig.queryArgs,
      orderBy: typedConfig.queryOrderBy,

      itemsProperty: config.itemsProperty,
      idProperty: config.idProperty,
      labelProperty: config.labelProperty,
      entityProperty: config.entityProperty,
      versionProperty: config.versionProperty,
      scope: this,
    });
  },
  insertEntry: function insertEntry(entry, options) {
    const store = this.createStore('detail');
    return store.add(entry, options);
  },
  updateEntry: function updateEntry(entry, options) {
    const store = this.createStore('edit');
    const def = new Deferred();
    if (!store) {
      throw new Error('No store set.');
    }
    this.validate(entry).then(() => {
      store.put(entry, options).then((result) => {
        this.onEntryUpdated(result, entry);
        def.resolve(result);
      }, (err) => {
        def.reject(err);
      });
    }, (err) => {
      def.reject(err);
    });
    return def.promise;
  },
  onEntryUpdated: function onEntryUpdated(result, orginalEntry) { // eslint-disable-line
  },
  /**
   * If an entry is valid, validate should return a promise that resolves to true. If the entry is not valid,
   * validate should return a reject promise with the error message.
   * @param entry
   * @returns Promise
   */
  validate: function validate(entry) {
    const def = new Deferred();
    if (entry) {
      def.resolve(true);
    }

    def.reject('The entry is null or undefined.');
    return def.promise;
  },
  getEntry: function getEntry(entityId, options) {
    let queryResults;
    let relatedRequests;
    const queryModelName = (options && options.queryModelName) ? options.queryModelName : 'detail';
    const store = this.createStore(queryModelName);
    const def = new Deferred();
    const includeRelated = (options && options.includeRelated) ? options.includeRelated : false;
    const queryOptions = this.getOptions(options);
    if (store) {
      relatedRequests = [];
      queryResults = store.get(entityId, queryOptions);
      when(queryResults, function(relatedFeed) { // eslint-disable-line
        const entry = queryResults.results[0];
        if (includeRelated) {
          relatedRequests = this.getRelatedRequests(entry);
        }
        if (relatedRequests.length > 0) {
          all(relatedRequests).then(
              (relatedResults) => {
                this.applyRelatedResults(entry, relatedResults);
                all(this.getPicklists()).then(
                  () => def.resolve(entry),
                  err => def.reject(err));
              },
              (err) => {
                def.reject(err);
              });
        } else {
          def.resolve(entry);
        }
      }.bind(this), (err) => {
        def.reject(err);
      });

      return def.promise;
    }
  },
  getEntries: function getEntries(query, options) {
    const queryModelName = (options && options.queryModelName) ? options.queryModelName : 'list';
    const def = new Deferred();
    const store = this.createStore(queryModelName);
    const queryOptions = this.getOptions(options);
    const queryExpression = this.buildQueryExpression(query, options);

    const queryResults = store.query(queryExpression, queryOptions);
    if (options && options.returnQueryResults) {
      return queryResults;
    }
    when(queryResults, (entities) => {
      def.resolve(entities);
    }, (err) => {
      def.reject(err);
    });
    return def.promise;
  },
  getRelatedRequests: function getRelatedRequests(entry) {
    const self = this;
    const requests = [];
    this.relationships.forEach((rel) => {
      let request = null;
      if (!rel.disabled) {
        request = self.getRelatedRequest(entry, rel);
        if (request) {
          requests.push(request);
        }
      }
    });
    return requests;
  },
  getRelatedRequest: function getRelatedRequest(entry, relationship, options) {
    let queryOptions;
    let queryResults;
    const def = new Deferred();
    const model = App.ModelManager.getModel(relationship.relatedEntity, MODEL_TYPES.SDATA);
    if (model) {
      queryOptions = this.getRelatedQueryOptions(entry, relationship, options);
      if (queryOptions) {
        queryResults = model.getEntries(null, queryOptions);
        when(queryResults, (entities) => {
          const results = {
            entityName: model.entityName,
            entityDisplayName: model.entityDisplayName,
            entityDisplayNamePlural: model.entityDisplayNamePlural,
            relationship,
            count: entities.length,
            entities,
          };
          def.resolve(results);
        }, (err) => {
          def.reject(err);
        });
        return def.promise;
      }
    }
  },
  getRelatedQueryOptions: function getRelatedQueryOptions(entry, relationship, options) {
    let parentDataPath;
    let relatedDataPath;
    let optionsTemp = options;

    if (!optionsTemp) {
      optionsTemp = {};
    }

    const queryOptions = {
      count: (optionsTemp.count) ? optionsTemp.count : null,
      start: (optionsTemp.start) ? optionsTemp.start : null,
      where: (optionsTemp.where) ? optionsTemp.where : null,
      sort: (optionsTemp.orderBy) ? optionsTemp.orderBy : null,
      queryModelName: (relationship.queryModelName) ? relationship.queryModelName : 'detail',
    };

    if (relationship.parentProperty) {
      parentDataPath = (relationship.parentDataPath) ? relationship.parentDataPath : relationship.parentProperty;
      if (relationship.parentPropertyType && (relationship.parentPropertyType === 'object')) {
        parentDataPath = `${relationship.parentProperty}.$key`;
      }
    } else {
      parentDataPath = this.idProperty;
    }

    if (relationship.relatedProperty) {
      relatedDataPath = (relationship.relatedDataPath) ? relationship.relatedDataPath : relationship.relatedProperty;
      if (relationship.relatedPropertyType && (relationship.relatedPropertyType === 'object')) {
        relatedDataPath = `${relationship.relatedProperty}.Id`;
      }
    } else {
      relatedDataPath = 'Id';
    }

    const relatedValue = utility.getValue(entry, parentDataPath);
    const where = "${0} eq '${1}'";
    if (!relatedValue) {
      return null;
    }
    queryOptions.where = string.substitute(where, [relatedDataPath, relatedValue]);
    if (relationship.where) {
      if (typeof relationship.where === 'function') {
        queryOptions.where = relationship.where.apply(this, [entry]);
      } else {
        queryOptions.where = `${queryOptions.where} and ${relationship.where}`;
      }
    }
    return queryOptions;
  },
  applyRelatedResults: function applyRelatedResults(entry, relatedResults) {
    const relatedEntities = [];
    relatedResults.forEach((result) => {
      relatedEntities.push(result);
    });
    entry.$relatedEntities = relatedEntities;
  },
});

export default __class;