UNPKG

d2-ui

Version:
550 lines (454 loc) 24.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _get = function get(_x6, _x7, _x8) { var _again = true; _function: while (_again) { var object = _x6, property = _x7, receiver = _x8; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x6 = parent; _x7 = property; _x8 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var _libCheck = require('../lib/check'); var _libUtils = require('../lib/utils'); var _ModelDefinitions = require('./ModelDefinitions'); var _ModelDefinitions2 = _interopRequireDefault(_ModelDefinitions); var _Model = require('./Model'); var _Model2 = _interopRequireDefault(_Model); var _ModelCollection = require('./ModelCollection'); var _ModelCollection2 = _interopRequireDefault(_ModelCollection); var _ModelCollectionProperty = require('./ModelCollectionProperty'); var _ModelCollectionProperty2 = _interopRequireDefault(_ModelCollectionProperty); var _libSchemaTypes = require('../lib/SchemaTypes'); var _libSchemaTypes2 = _interopRequireDefault(_libSchemaTypes); var _Filters = require('./Filters'); var _Filters2 = _interopRequireDefault(_Filters); var _ModelBase = require('./ModelBase'); var _config = require('./config'); function createModelPropertyDescriptor(propertiesObject, schemaProperty) { var propertyName = schemaProperty.collection ? schemaProperty.collectionName : schemaProperty.name; var propertyDetails = { // Actual property descriptor properties configurable: false, enumerable: true, get: function get() { return this.dataValues[propertyName]; } }; // Only add a setter for writable properties if (schemaProperty.writable) { propertyDetails.set = function dynamicPropertySetter(value) { // TODO: Objects and Arrays are considered unequal when their data is the same and therefore trigger a dirty if (!(0, _libCheck.isObject)(value) && value !== this.dataValues[propertyName] || (0, _libCheck.isObject)(value)) { this.dirty = true; this[_ModelBase.DIRTY_PROPERTY_LIST].add(propertyName); this.dataValues[propertyName] = value; } }; } if (propertyName) { propertiesObject[propertyName] = propertyDetails; // eslint-disable-line no-param-reassign } } function createPropertiesObject(schemaProperties) { var propertiesObject = {}; var createModelPropertyDescriptorOn = (0, _libUtils.curry)(createModelPropertyDescriptor, propertiesObject); (schemaProperties || []).forEach(createModelPropertyDescriptorOn); return propertiesObject; } function createValidationSetting(validationObject, schemaProperty) { var propertyName = schemaProperty.collection ? schemaProperty.collectionName : schemaProperty.name; var validationDetails = { persisted: schemaProperty.persisted, type: _libSchemaTypes2['default'].typeLookup(schemaProperty.propertyType), required: schemaProperty.required, min: schemaProperty.min, max: schemaProperty.max, owner: schemaProperty.owner, unique: schemaProperty.unique, writable: schemaProperty.writable, constants: schemaProperty.constants, ordered: Boolean(schemaProperty.ordered) }; function getReferenceTypeFrom(property) { if (property.href) { return property.href.split('/').pop(); } return undefined; } // Add a referenceType to be able to get a hold of the reference objects model. if (validationDetails.type === 'REFERENCE' || validationDetails.type === 'COLLECTION' && schemaProperty.itemPropertyType === 'REFERENCE') { validationDetails.referenceType = getReferenceTypeFrom(schemaProperty); } if (propertyName) { validationObject[propertyName] = validationDetails; // eslint-disable-line no-param-reassign } } function createValidations(schemaProperties) { var validationsObject = {}; var createModelPropertyOn = (0, _libUtils.curry)(createValidationSetting, validationsObject); (schemaProperties || []).forEach(createModelPropertyOn); return validationsObject; } function shouldBeModelCollectionProperty(model, models) { return function shouldBeModelCollectionPropertyIterator(modelProperty) { return model && models && model.modelDefinition && model.modelDefinition.modelValidations && model.modelDefinition.modelValidations[modelProperty] && model.modelDefinition.modelValidations[modelProperty].type === 'COLLECTION' && models.hasOwnProperty(model.modelDefinition.modelValidations[modelProperty].referenceType); }; } function getOwnedPropertyJSON(model) { var objectToSave = {}; var ownedProperties = this.getOwnedPropertyNames(); var collectionProperties = model.getCollectionChildrenPropertyNames(); Object.keys(this.modelValidations).forEach(function (propertyName) { if (ownedProperties.indexOf(propertyName) >= 0) { if (model.dataValues[propertyName] !== undefined && model.dataValues[propertyName] !== null) { // Handle collections and plain values different if (collectionProperties.indexOf(propertyName) === -1) { objectToSave[propertyName] = model.dataValues[propertyName]; } else { // Transform an object collection to an array of objects with id properties objectToSave[propertyName] = Array.from(model.dataValues[propertyName].values()).filter(function (value) { return value.id; }).map(function (_ref) { var id = _ref.id; return { id: id }; }); } } } }); return objectToSave; } function isAnUpdate(modelToCheck) { return Boolean(modelToCheck.id); } /** * @class ModelDefinition * * @description * Definition of a Model. Basically this object contains the meta data related to the Model. Like `name`, `apiEndPoint`, `modelValidation`, etc. * It also has methods to create and load Models that are based on this definition. The Data element `ModelDefinition` would be used to create Data Element `Model`s * * Note: ModelDefinition has a property `api` that is used for the communication with the dhis2 api. The value of this * property is an instance of `Api`. */ var ModelDefinition = (function () { function ModelDefinition(modelName, modelNamePlural, modelOptions, properties, validations, attributes, authorities) { _classCallCheck(this, ModelDefinition); (0, _libCheck.checkType)(modelName, 'string'); (0, _libCheck.checkType)(modelNamePlural, 'string', 'Plural'); (0, _libUtils.addLockedProperty)(this, 'name', modelName); (0, _libUtils.addLockedProperty)(this, 'plural', modelNamePlural); (0, _libUtils.addLockedProperty)(this, 'isSharable', modelOptions && modelOptions.shareable || false); (0, _libUtils.addLockedProperty)(this, 'isMetaData', modelOptions && modelOptions.metadata || false); (0, _libUtils.addLockedProperty)(this, 'apiEndpoint', modelOptions && modelOptions.apiEndpoint); (0, _libUtils.addLockedProperty)(this, 'javaClass', modelOptions && modelOptions.klass); (0, _libUtils.addLockedProperty)(this, 'identifiableObject', modelOptions && modelOptions.identifiableObject); (0, _libUtils.addLockedProperty)(this, 'modelProperties', properties); (0, _libUtils.addLockedProperty)(this, 'modelValidations', validations); (0, _libUtils.addLockedProperty)(this, 'attributeProperties', attributes); (0, _libUtils.addLockedProperty)(this, 'authorities', authorities); this.filters = _Filters2['default'].getFilters(this); // TODO: The function getOwnedPropertyJSON should probably not be exposed, perhaps we could have a getJSONForModel(ownedPropertiesOnly=true) method. this.getOwnedPropertyJSON = getOwnedPropertyJSON.bind(this); } _createClass(ModelDefinition, [{ key: 'filter', value: function filter() { return this.clone().filters; } /** * @method create * * @param {Object} [data] Datavalues that should be loaded into the model. * * @returns {Model} Returns the newly created model instance. * * @description * Creates a fresh Model instance based on the `ModelDefinition`. If data is passed into the method that * data will be loaded into the matching properties of the model. * * ```js * dataElement.create({name: 'ANC', id: 'd2sf33s3ssf'}); * ``` */ }, { key: 'create', value: function create(data) { var model = _Model2['default'].create(this); var validations = model.modelDefinition.modelValidations; var models = _ModelDefinitions2['default'].getModelDefinitions(); var dataValues = Object.assign({}, data); if (data) { // Set the data values onto the model directly Object.keys(model).forEach(function (modelProperty) { var referenceType = validations[modelProperty].hasOwnProperty('referenceType') && validations[modelProperty].referenceType || models.hasOwnProperty(modelProperty) && modelProperty; // For collections of objects, create ModelCollectionProperties rather than plain arrays if (referenceType && models.hasOwnProperty(referenceType) && Array.isArray(data[modelProperty])) { dataValues[modelProperty] = _ModelCollectionProperty2['default'].create(model, models[referenceType], data[modelProperty].map(function (d) { return models[referenceType].create(d); })); } model.dataValues[modelProperty] = dataValues[modelProperty]; }); } else { (function () { // Create empty ModelCollectionProperties for models without data. Object.keys(model).filter(shouldBeModelCollectionProperty(model, models)).forEach(function (modelProperty) { var referenceType = model.modelDefinition.modelValidations[modelProperty].referenceType; model.dataValues[modelProperty] = _ModelCollectionProperty2['default'].create(model, models[referenceType], []); }); // When no initial values are provided we are dealing with a new Model. For some properties there are // implicit default values that should be set. DHIS2 has some default values for models that would implicitly // be set when omitting sending a value on POST, we'll set these properties to their default values so they // are reflected in read operations on the model and to make them more transparent. var defaultValues = (0, _config.getDefaultValuesForModelType)(model.modelDefinition.name); var checkForModelProperty = shouldBeModelCollectionProperty(model, models); Object.keys(model).filter(function (modelProperty) { return !checkForModelProperty(modelProperty); }).forEach(function (modelProperty) { model.dataValues[modelProperty] = defaultValues[modelProperty]; }); })(); } return model; } }, { key: 'clone', value: function clone() { var ModelDefinitionPrototype = Object.getPrototypeOf(this); var priorFilters = this.filters.filters; var clonedDefinition = Object.create(ModelDefinitionPrototype); clonedDefinition = (0, _libUtils.copyOwnProperties)(clonedDefinition, this); clonedDefinition.filters = _Filters2['default'].getFilters(clonedDefinition); clonedDefinition.filters.filters = priorFilters.map(function (filter) { return filter; }); return clonedDefinition; } /** * @method get * * @param {String} identifier * @param {Object} [queryParams={fields: ':all'}] Query parameters that should be passed to the GET query. * @returns {Promise} Resolves with a `Model` instance or an error message. * * @description * Get a `Model` instance from the api loaded with data that relates to `identifier`. * This will do an API call and return a Promise that resolves with a `Model` or rejects with the api error message. * * ```js * //Do a get request for the dataElement with given id (d2sf33s3ssf) and print it's name * //when that request is complete and the model is loaded. * dataElement.get('d2sf33s3ssf') * .then(model => console.log(model.name)); * ``` */ }, { key: 'get', value: function get(identifier) { var _this = this; var queryParams = arguments.length <= 1 || arguments[1] === undefined ? { fields: ':all,attributeValues[:all,attribute[id,name,displayName]]' } : arguments[1]; (0, _libCheck.checkDefined)(identifier, 'Identifier'); if (Array.isArray(identifier)) { return this.list({ filter: ['id:in:[' + identifier.join(',') + ']'] }); } // TODO: should throw error if API has not been defined return this.api.get([this.apiEndpoint, identifier].join('/'), queryParams).then(function (data) { return _this.create(data); })['catch'](function (response) { if (response.message) { return Promise.reject(response.message); } return Promise.reject(response); }); } /** * @method list * * @param {Object} [queryParams={fields: ':all'}] Query parameters that should be passed to the GET query. * @returns {Promise} ModelCollection collection of models objects of the `ModelDefinition` type. * * @description * Loads a list of models. * * ```js * // Loads a list of models and prints their name. * dataElement.list() * .then(modelCollection => { * modelCollection.forEach(model => console.log(model.name)); * }); * ``` */ }, { key: 'list', value: function list() { var _this2 = this; var queryParams = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var definedFilters = this.filters.getFilters(); // FIXME: Looks like when specific filters are defined the model.filter() filters are not applied (they should probably be merged) if (!(0, _libCheck.isDefined)(queryParams.filter) && definedFilters.length) { queryParams.filter = definedFilters; // eslint-disable-line no-param-reassign } return this.api.get(this.apiEndpoint, Object.assign({ fields: ':all' }, queryParams)).then(function (responseData) { return _ModelCollection2['default'].create(_this2, responseData[_this2.plural].map(function (data) { return _this2.create(data); }), responseData.pager); }); } /** * @method save * * @param {Model} model The model that should be saved to the server. * @returns {Promise} A promise which resolves when the save was successful * or rejects when it failed. The promise will resolve with the data that is * returned from the server. * * @description * This method is used by the `Model` instances to save the model when calling `model.save()`. * * @note {warning} This should generally not be called directly. */ // TODO: check the return status of the save to see if it was actually successful and not ignored }, { key: 'save', value: function save(model) { var jsonPayload = getOwnedPropertyJSON.bind(this)(model); if (isAnUpdate(model)) { var updateUrl = model.dataValues.href; // Save the existing model return this.api.update(updateUrl, jsonPayload, true); } // Its a new object return this.api.post(this.apiEndpoint, jsonPayload); } /** * @method getOwnedPropertyNames * * @returns {String[]} Returns an array of property names. * * @description * This method returns a list of property names that that are defined * as "owner" properties on this schema. This means these properties are used * when saving the model to the server. * * ```js * dataElement.getOwnedPropertyNames() * ``` */ }, { key: 'getOwnedPropertyNames', value: function getOwnedPropertyNames() { var _this3 = this; return Object.keys(this.modelValidations).filter(function (propertyName) { return _this3.modelValidations[propertyName].owner; }); } /** * @method delete * * @returns {Promise} Returns a promise to the deletion operation * * @description * This method is used by the `Model` instances to delete the model when calling `model.delete()`. * * @note {warning} This should generally not be called directly. */ }, { key: 'delete', value: function _delete(model) { if (model.dataValues.href) { return this.api['delete'](model.dataValues.href); } return this.api['delete']([model.modelDefinition.apiEndpoint, model.dataValues.id].join('/')); } /** * @method createFromSchema * @static * * @param {Object} schema A schema definition received from the web api (/api/schemas) * @param {Object[]} attributes A list of attribute objects that describe custom attributes (/api/attributes) * * @returns {ModelDefinition} Frozen model definition object. * * @description * This method creates a new `ModelDefinition` based on a JSON structure called * a schema. A schema represents the structure of a domain model as it is * required by DHIS. Since these schemas can not be altered on the server from * the modelDefinition is frozen to prevent accidental changes to the definition. * * ```js * ModelDefinition.createFromSchema(schemaDefinition, attributes); * ``` * * @note {info} An example of a schema definition can be found on * https://apps.dhis2.org/demo/api/schemas/dataElement */ }], [{ key: 'createFromSchema', value: function createFromSchema(schema) { var attributes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var ModelDefinitionClass = undefined; (0, _libCheck.checkType)(schema, Object, 'Schema'); if (typeof ModelDefinition.specialClasses[schema.name] === 'function') { ModelDefinitionClass = ModelDefinition.specialClasses[schema.name]; } else { ModelDefinitionClass = ModelDefinition; } return Object.freeze(new ModelDefinitionClass(schema.name, schema.plural, schema, Object.freeze(createPropertiesObject(schema.properties)), Object.freeze(createValidations(schema.properties)), attributes.reduce(function (current, attributeDefinition) { current[attributeDefinition.name] = attributeDefinition; // eslint-disable-line no-param-reassign return current; }, {}), schema.authorities)); } }]); return ModelDefinition; })(); var UserModelDefinition = (function (_ModelDefinition) { _inherits(UserModelDefinition, _ModelDefinition); function UserModelDefinition() { _classCallCheck(this, UserModelDefinition); _get(Object.getPrototypeOf(UserModelDefinition.prototype), 'constructor', this).apply(this, arguments); } _createClass(UserModelDefinition, [{ key: 'get', // TODO: userCredentials should always be included, no matter what the query params, that is currently not the case value: function get(identifier) { var queryParams = arguments.length <= 1 || arguments[1] === undefined ? { fields: ':all,userCredentials[:owner]' } : arguments[1]; return _get(Object.getPrototypeOf(UserModelDefinition.prototype), 'get', this).call(this, identifier, queryParams); } }]); return UserModelDefinition; })(ModelDefinition); var DataSetModelDefinition = (function (_ModelDefinition2) { _inherits(DataSetModelDefinition, _ModelDefinition2); function DataSetModelDefinition() { _classCallCheck(this, DataSetModelDefinition); _get(Object.getPrototypeOf(DataSetModelDefinition.prototype), 'constructor', this).apply(this, arguments); } _createClass(DataSetModelDefinition, [{ key: 'create', value: function create() { var data = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; // Filter out the compulsoryDataElementOperands structure from the retrieved data // This structure does not follow the convention of a typical reference. We can not create a proper // ModelCollection for this collection. var dataClone = Object.keys(data).filter(function (key) { return key !== 'compulsoryDataElementOperands'; }).reduce(function (obj, key) { obj[key] = data[key]; // eslint-disable-line no-param-reassign return obj; }, {}); // Create the model using the usual way of creating a model var model = _get(Object.getPrototypeOf(DataSetModelDefinition.prototype), 'create', this).call(this, dataClone); // Set the compulsoryDataElementOperands onto the dataValues so it will be included during the save operations model.dataValues.compulsoryDataElementOperands = data.compulsoryDataElementOperands; return model; } }]); return DataSetModelDefinition; })(ModelDefinition); ModelDefinition.specialClasses = { user: UserModelDefinition, dataSet: DataSetModelDefinition }; exports['default'] = ModelDefinition; module.exports = exports['default']; //# sourceMappingURL=ModelDefinition.js.map