d2-ui
Version:
550 lines (454 loc) • 24.9 kB
JavaScript
'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