d2-ui
Version:
1,359 lines (1,108 loc) • 195 kB
JavaScript
var d2 = (function () {var d2 =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ((function(modules) {
// Check all modules for deduplicated modules
for(var i in modules) {
if(Object.prototype.hasOwnProperty.call(modules, i)) {
switch(typeof modules[i]) {
case "function": break;
case "object":
// Module can be created from a template
modules[i] = (function(_m) {
var args = _m.slice(1), fn = modules[_m[0]];
return function (a,b,c) {
fn.apply(this, [a,b,c].concat(args));
};
}(modules[i]));
break;
default:
// Module is a copy of another module
modules[i] = modules[modules[i]];
break;
}
}
}
return modules;
}([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.getManifest = getManifest;
exports.getUserSettings = getUserSettings;
exports.init = init;
exports.getInstance = getInstance;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
var _libUtils = __webpack_require__(1);
var _loggerLogger = __webpack_require__(2);
var _loggerLogger2 = _interopRequireDefault(_loggerLogger);
var _modelModels = __webpack_require__(4);
var _modelModels2 = _interopRequireDefault(_modelModels);
var _apiApi = __webpack_require__(7);
var _apiApi2 = _interopRequireDefault(_apiApi);
var _systemSystem = __webpack_require__(9);
var _systemSystem2 = _interopRequireDefault(_systemSystem);
var _i18nI18n = __webpack_require__(30);
var _i18nI18n2 = _interopRequireDefault(_i18nI18n);
var _config = __webpack_require__(31);
var _config2 = _interopRequireDefault(_config);
var _currentUserCurrentUser = __webpack_require__(33);
var _currentUserCurrentUser2 = _interopRequireDefault(_currentUserCurrentUser);
__webpack_require__(12);
var firstRun = true;
var deferredD2Init = _libUtils.Deferred.create();
var preInitConfig = _config2['default'].create();
function getManifest(url) {
var api = new _apiApi2['default']();
api.setBaseUrl('');
var manifestUtilities = {
getBaseUrl: function getBaseUrl() {
return this.activities.dhis.href;
}
};
return api.get('' + url).then(function (manifest) {
return Object.assign({}, manifest, manifestUtilities);
});
}
/**
* @function getUserSettings
*
* @returns {Promise} A promise to the current user settings
*
* @description
* The object that is the result of the promise will have the following properties
* ```js
* {
* "uiLocale": "en" // The users locale, that can be used for translations)
* }
* ```
*/
function getUserSettings() {
var api = _apiApi2['default'].getApi();
if (preInitConfig.baseUrl && firstRun) {
api.setBaseUrl(preInitConfig.baseUrl);
}
return api.get('userSettings');
}
function getModelRequests(api) {
var schemaNames = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
var fieldsForSchemas = ['apiEndpoint,name,authorities,singular,plural,shareable,metadata,klass,identifiableObject,properties[href', 'writable,collection,collectionName,name,propertyType,persisted,required,min,max,ordered,unique,constants', 'owner,itemPropertyType]'].join(',');
var modelRequests = [];
var loadSchemaForName = function loadSchemaForName(schemaName) {
return api.get('schemas/' + schemaName, { fields: fieldsForSchemas });
};
if (schemaNames.length > 0) {
var individualSchemaRequests = schemaNames.map(loadSchemaForName);
var schemasPromise = Promise.all(individualSchemaRequests).then(function (schemas) {
return { schemas: schemas };
});
modelRequests.push(schemasPromise);
} else {
// Used as a source to generate the models.
modelRequests.push(api.get('schemas', { fields: fieldsForSchemas }));
}
// Used to add the dynamic attributes to the models that should have them.
modelRequests.push(api.get('attributes', { fields: ':all,optionSet[:all,options[:all]]', paging: false }));
return modelRequests;
}
/**
* @function init
*
* @param {Object} initConfig Configuration object that will be used to configure to define D2 Setting.
* See the description for more information on the available settings.
* @returns {Promise} A promise that resolves with the intialized d2 object. Which is an object that exposes `model`, `models` and `Api`
*
* @description
* Init function that used to initialise D2. This will load the schemas from the DHIS2 api and configure your D2 instance.
*
* The `config` object that can be passed into D2 can have the following properties:
*
* baseUrl: Set this when the url is something different then `/api`. If you are running your dhis instance in a subdirectory of the actual domain
* for example http://localhost/dhis/ you should set the base url to `/dhis/api`
*
* ```js
* import init from 'd2';
*
* init({baseUrl: '/dhis/api'})
* .then((d2) => {
* console.log(d2.model.dataElement.list());
* });
* ```
*/
function init(initConfig) {
var api = _apiApi2['default'].getApi();
var logger = _loggerLogger2['default'].getLogger();
var config = _config2['default'].create(preInitConfig, initConfig);
var d2 = {
models: undefined,
model: _modelModels2['default'],
Api: _apiApi2['default'],
system: _systemSystem2['default'].getSystem(),
i18n: _i18nI18n2['default'].getI18n()
};
// Process the config in a the config class to keep all config calls together.
_config2['default'].processConfigForD2(config, d2);
// Because when importing the getInstance method in dependencies the getInstance could run before
// init we have to resolve the current promise on first run and for consecutive ones replace the
// old one with a fresh promise.
if (firstRun) {
firstRun = false;
} else {
deferredD2Init = _libUtils.Deferred.create();
}
var modelRequests = getModelRequests(api, config.schemas);
var userRequests = [api.get('me', { fields: ':all,organisationUnits[id],userGroups[id],userCredentials[:all,!user,userRoles[id]' }), api.get('me/authorization'), getUserSettings()];
var systemRequests = [api.get('system/info'), api.get('apps')];
return Promise.all([].concat(_toConsumableArray(modelRequests), userRequests, systemRequests, [d2.i18n.load()])).then(function (res) {
var responses = {
schemas: (0, _libUtils.pick)('schemas')(res[0]),
attributes: (0, _libUtils.pick)('attributes')(res[1]),
currentUser: res[2],
authorities: res[3],
userSettings: res[4],
systemInfo: res[5],
apps: res[6]
};
responses.schemas
// TODO: Remove this when the schemas endpoint is versioned or shows the correct urls for the requested version
// The schemas endpoint is not versioned which will result into the modelDefinitions always using the
// "default" endpoint, we therefore modify the endpoint url based on the given baseUrl.
.map(function (schema) {
schema.apiEndpoint = (0, _libUtils.updateAPIUrlWithBaseUrlVersionNumber)(schema.apiEndpoint, config.baseUrl); // eslint-disable-line no-param-reassign
return schema;
}).forEach(function (schema) {
// Attributes that do not have values do not by default get returned with the data,
// therefore we need to grab the attributes that are attached to this particular schema to be able to know about them
var schemaAttributes = responses.attributes.filter(function (attributeDescriptor) {
var attributeNameFilter = [schema.singular, 'Attribute'].join('');
return attributeDescriptor[attributeNameFilter] === true;
});
if (!Object.prototype.hasOwnProperty.call(d2.models, schema.singular)) {
d2.models.add(_modelModels2['default'].ModelDefinition.createFromSchema(schema, schemaAttributes));
}
});
d2.currentUser = _currentUserCurrentUser2['default'].create(responses.currentUser, responses.authorities, d2.models, responses.userSettings);
d2.system.setSystemInfo(responses.systemInfo);
d2.system.setInstalledApps(responses.apps);
deferredD2Init.resolve(d2);
return deferredD2Init.promise;
})['catch'](function (error) {
logger.error('Unable to get schemas from the api', JSON.stringify(error), error);
deferredD2Init.reject('Unable to get schemas from the DHIS2 API');
return deferredD2Init.promise;
});
}
/**
* @function getInstance
*
* @returns {Promise} A promise to an initialized d2 instance.
*
* @description
* This function can be used to retrieve the `singleton` instance of d2. The instance is being created by calling
* the `init` method.
*
* ```js
* import {init, getInstance} from 'd2';
*
* init({baseUrl: '/dhis2/api/'});
* getInstance()
* .then(d2 => {
* d2.models.dataElement.list();
* // and all your other d2 magic.
* });
* ```
*/
function getInstance() {
return deferredD2Init.promise;
}
// Alias preInitConfig to be able to `import {config} from 'd2';`
/**
* @property config
*
* @description
* Can be used to set config options before initialisation of d2.
*
* ```js
* import {config, init} from 'd2';
*
* config.baseUrl = '/demo/api';
* config.i18n.sources.add('i18n/systemsettingstranslations.properties');
*
* init()
* .then(d2 => {
* d2.system.settings.all()
* .then(systemSettings => Object.keys())
* .then(systemSettingsKey => {
* d2.i18n.getTranslation(systemSettingsKey);
* });
* });
*
* ```
*/
var config = preInitConfig;
exports.config = config;
exports['default'] = {
init: init,
config: config,
getInstance: getInstance,
getUserSettings: getUserSettings,
getManifest: getManifest
};
/***/ },
/* 1 */
/***/ function(module, exports) {
// TODO: Most of these functions should be moved out to d2-utilizr
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
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; }; })();
exports.throwError = throwError;
exports.curry = curry;
exports.addLockedProperty = addLockedProperty;
exports.copyOwnProperties = copyOwnProperties;
exports.pick = pick;
exports.updateAPIUrlWithBaseUrlVersionNumber = updateAPIUrlWithBaseUrlVersionNumber;
exports.customEncodeURIComponent = customEncodeURIComponent;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function throwError(message) {
throw new Error(message);
}
// TODO: Throw an error when `toCurry` is not a function
function curry(toCurry, parameter) {
return function curried() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return toCurry.apply(this, [parameter].concat(args));
};
}
function addLockedProperty(object, name, value) {
var propertyDescriptor = {
enumerable: true,
configurable: false,
writable: false,
value: value
};
Object.defineProperty(object, name, propertyDescriptor);
}
function copyOwnProperties(to, from) {
var key = undefined;
for (key in from) {
if (from.hasOwnProperty(key)) {
to[key] = from[key]; // eslint-disable-line no-param-reassign
}
}
return to;
}
function pick(property) {
return function (item) {
if (item) {
return item[property];
}
return undefined;
};
}
var Deferred = (function () {
function Deferred() {
var _this = this;
_classCallCheck(this, Deferred);
this.promise = new Promise(function (resolve, reject) {
_this.resolve = resolve;
_this.reject = reject;
});
}
_createClass(Deferred, null, [{
key: 'create',
value: function create() {
return new Deferred();
}
}]);
return Deferred;
})();
exports.Deferred = Deferred;
function updateAPIUrlWithBaseUrlVersionNumber(apiUrl, baseUrl) {
if (!baseUrl || !apiUrl) {
return apiUrl;
}
var apiUrlWithVersionRexExp = /api\/(2[3-9])/;
var apiVersionMatch = baseUrl.match(apiUrlWithVersionRexExp);
var baseUrlHasVersion = apiVersionMatch && apiVersionMatch[1];
var apiUrlHasVersion = apiUrl && !apiUrlWithVersionRexExp.test(apiUrl);
if (baseUrlHasVersion && apiUrlHasVersion) {
var version = apiVersionMatch[1];
// Inject the current api version number into the endPoint urls
return apiUrl.replace(/api/, 'api/' + version);
}
return apiUrl;
}
// Define our very own special list of characters that we don't want to encode in the URI
var whitelistURI = ',&$=/;:';
var whitelistURICodes = whitelistURI.split('').map(function (c) {
return encodeURIComponent(c);
});
var whitelistRegExp = new RegExp('(?:' + whitelistURICodes.join('|') + ')', 'g');
/**
* Encode all invalid URI characters, except the ones we've decided we don't want to
*/
function customEncodeURIComponent(uri) {
// return uri;
return encodeURIComponent(uri).replace(whitelistRegExp, decodeURIComponent);
}
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _libCheck = __webpack_require__(3);
var Logger = (function () {
function Logger(logging) {
_classCallCheck(this, Logger);
(0, _libCheck.checkType)(logging, 'object', 'console');
this.logger = logging;
}
_createClass(Logger, [{
key: 'canLog',
value: function canLog(type) {
return !!(type && console && (0, _libCheck.isType)(this.logger[type], 'function'));
}
}, {
key: 'debug',
value: function debug() {
if (this.canLog('debug')) {
for (var _len = arguments.length, rest = Array(_len), _key = 0; _key < _len; _key++) {
rest[_key] = arguments[_key];
}
this.logger.debug.apply(console, rest);
return true;
}
return false;
}
}, {
key: 'error',
value: function error() {
if (this.canLog('error')) {
for (var _len2 = arguments.length, rest = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
rest[_key2] = arguments[_key2];
}
this.logger.error.apply(console, rest);
return true;
}
return false;
}
}, {
key: 'log',
value: function log() {
if (this.canLog('log')) {
for (var _len3 = arguments.length, rest = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
rest[_key3] = arguments[_key3];
}
this.logger.log.apply(console, rest);
return true;
}
return false;
}
}, {
key: 'warn',
value: function warn() {
if (this.canLog('warn')) {
for (var _len4 = arguments.length, rest = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
rest[_key4] = arguments[_key4];
}
this.logger.warn.apply(console, rest);
return true;
}
return false;
}
}], [{
key: 'getLogger',
value: function getLogger() {
var logger = undefined;
// TODO: This is not very clean try to figure out a better way to do this.
try {
// Node version
logger = global.console;
} catch (e) {
// Browser version fallback
/* istanbul ignore next */
logger = window.console;
}
if (this.logger) {
return this.logger;
}
return this.logger = new Logger(logger);
}
}]);
return Logger;
})();
exports['default'] = Logger;
module.exports = exports['default'];
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 3 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.checkDefined = checkDefined;
exports.checkType = checkType;
exports.isType = isType;
exports.isString = isString;
exports.isArray = isArray;
exports.isObject = isObject;
exports.isDefined = isDefined;
exports.isInteger = isInteger;
exports.isNumeric = isNumeric;
exports.contains = contains;
exports.isValidUid = isValidUid;
function checkDefined(value, name) {
if (value !== undefined) {
return true;
}
throw new Error([name || 'Value', 'should be provided'].join(' '));
}
// TODO: Decide if checkType([], 'object') is a 'false' positive
function checkType(value, type, name) {
checkDefined(value, name);
checkDefined(type, 'Type');
if (typeof type === 'function' && value instanceof type || typeof type === 'string' && typeof value === type) {
return true;
}
throw new Error(['Expected', name || value, 'to have type', type].join(' '));
}
// TODO: Log type error?
function isType(value, type) {
function noop() {}
try {
checkType(value, type);
return true;
} catch (e) {
noop();
}
return false;
}
function isString(value) {
return isType(value, 'string');
}
function isArray(value) {
return Array.isArray(value);
}
function isObject(value) {
return isType(value, Object);
}
function isDefined(value) {
return value !== undefined;
}
function isInteger(nVal) {
return typeof nVal === 'number' && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal;
}
// Polyfill for the isInteger function that will be added in ES6
// http://wiki.ecmascript.org/doku.php?id=harmony:number.isinteger
/* istanbul ignore if */
if (!Number.isInteger) {
Number.isInteger = isInteger;
}
function isNumeric(nVal) {
return typeof nVal === 'number' && isFinite(nVal) && nVal - parseFloat(nVal) + 1 >= 0;
}
function contains(item, list) {
var listToCheck = isArray(list) && list || [];
return listToCheck.indexOf(item) >= 0;
}
function isValidUid(value) {
return value && value.length === 11;
}
exports['default'] = {
checkType: checkType,
checkDefined: checkDefined,
isArray: isArray,
isDefined: isDefined,
isInteger: isInteger,
isNumeric: isNumeric,
isString: isString,
isType: isType,
contains: contains,
isValidUid: isValidUid
};
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _ModelBase = __webpack_require__(5);
var _ModelBase2 = _interopRequireDefault(_ModelBase);
var _Model = __webpack_require__(14);
var _Model2 = _interopRequireDefault(_Model);
var _ModelDefinition = __webpack_require__(15);
var _ModelDefinition2 = _interopRequireDefault(_ModelDefinition);
var _ModelDefinitions = __webpack_require__(16);
var _ModelDefinitions2 = _interopRequireDefault(_ModelDefinitions);
var _ModelValidation = __webpack_require__(6);
var _ModelValidation2 = _interopRequireDefault(_ModelValidation);
exports['default'] = {
ModelBase: _ModelBase2['default'],
Model: _Model2['default'],
ModelDefinition: _ModelDefinition2['default'],
ModelDefinitions: _ModelDefinitions2['default'],
ModelValidation: _ModelValidation2['default']
};
module.exports = exports['default'];
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _ModelValidation = __webpack_require__(6);
var _ModelValidation2 = _interopRequireDefault(_ModelValidation);
var _libCheck = __webpack_require__(3);
var _helpersJson = __webpack_require__(13);
var modelValidator = _ModelValidation2['default'].getModelValidation();
var DIRTY_PROPERTY_LIST = Symbol('List to keep track of dirty properties');
exports.DIRTY_PROPERTY_LIST = DIRTY_PROPERTY_LIST;
function hasModelValidationForProperty(model, property) {
return Boolean(model.modelDefinition && model.modelDefinition.modelValidations && model.modelDefinition.modelValidations[property] && Object.prototype.hasOwnProperty.call(model.modelDefinition.modelValidations, property));
}
function updateModelFromResponseStatus(result) {
if (result && result.httpStatus === 'Created' && result && (0, _libCheck.isValidUid)(result.response.uid)) {
this.dataValues.id = result.response.uid;
this.dataValues.href = [this.modelDefinition.apiEndpoint, this.dataValues.id].join('/');
}
this.dirty = false;
this.getDirtyChildren().forEach(function (value) {
if (value.resetDirtyState) {
value.resetDirtyState();
} else {
value.dirty = false; // eslint-disable-line no-param-reassign
}
});
this[DIRTY_PROPERTY_LIST].clear();
return result;
}
/**
* @class ModelBase
*/
var ModelBase = (function () {
function ModelBase() {
_classCallCheck(this, ModelBase);
}
_createClass(ModelBase, [{
key: 'create',
/**
* @method create
*
* @returns {Promise} Returns a promise that resolves when the model has been saved or rejected with the result from
* the `validate()` call.
*
* @definition
* Will save model as a new object to the server using a POST request. This method would generally be used if
* you're creating models with pre-specified IDs. Note that this does not check if the model is marked as dirty.
*/
value: function create() {
var _this = this;
return this.validate().then(function (validationState) {
if (!validationState.status) {
return Promise.reject(validationState);
}
return _this.modelDefinition.saveNew(_this).then(updateModelFromResponseStatus.bind(_this));
});
}
/**
* @method save
*
* @returns {Promise} Returns a promise that resolves when the model has been saved
* or rejects with the result from the `validate()` call.
*
* @description
* Checks if the model is dirty. When the model is dirty it will check if the values of the model are valid by calling
* `validate`. If this is correct it will attempt to save the [Model](#/model/Model) to the api.
*
* ```js
* myModel.save()
* .then((message) => console.log(message));
* ```
*/
}, {
key: 'save',
value: function save(includeChildren) {
var _this2 = this;
if (!this.isDirty(includeChildren)) {
return Promise.reject('No changes to be saved');
}
return this.validate().then(function (validationState) {
if (!validationState.status) {
return Promise.reject(validationState);
}
return _this2.modelDefinition.save(_this2).then(updateModelFromResponseStatus.bind(_this2));
});
}
/**
* @method validate
*
* @returns {Promise} Promise that resolves with an object with a status property that represents if the model
* is valid or not the fields array will return the names of the fields that are invalid.
*
* @description
* This will run the validations on the properties which have validations set. Normally these validations are defined
* through the DHIS2 schema. It will check min/max for strings/numbers etc. Additionally it will
* run model validations against the schema.
*
* ```js
* myModel.validate()
* .then(myModelStatus => {
* if (myModelStatus.status === false) {
* myModelStatus.fields.forEach((fieldName) => console.log(fieldName));
* }
* });
* ```
*/
}, {
key: 'validate',
value: function validate() {
var _this3 = this;
return new Promise(function (resolve, reject) {
var validationMessages = [];
function unique(current, property) {
if (property && current.indexOf(property) === -1) {
current.push(property);
}
return current;
}
function asyncRemoteValidation(model) {
return modelValidator.validateAgainstSchema(model);
}
// Run async validation against the api
asyncRemoteValidation(_this3)['catch'](function (remoteMessages) {
// Errors are ok in this case
if (Array.isArray(remoteMessages)) {
return remoteMessages;
}
return Promise.reject(remoteMessages);
}).then(function (remoteMessages) {
validationMessages = validationMessages.concat(remoteMessages);
var validationState = {
status: remoteMessages.length === 0,
fields: validationMessages.map(function (validationMessage) {
return validationMessage.property;
}).reduce(unique, []),
messages: validationMessages
};
resolve(validationState);
})['catch'](function (message) {
return reject(message);
});
});
}
}, {
key: 'clone',
value: function clone() {
return this.modelDefinition.create((0, _helpersJson.getJSONForProperties)(this, Object.keys(this.modelDefinition.modelValidations)));
}
}, {
key: 'delete',
value: function _delete() {
return this.modelDefinition['delete'](this);
}
}, {
key: 'isDirty',
value: function isDirty() {
var includeChildren = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
if (!(this.dirty || includeChildren === true && this.hasDirtyChildren())) {
return false;
}
return true;
}
}, {
key: 'getDirtyPropertyNames',
value: function getDirtyPropertyNames() {
return Array.from(this[DIRTY_PROPERTY_LIST].values());
}
}, {
key: 'getCollectionChildren',
value: function getCollectionChildren() {
var _this4 = this;
// TODO: Can't be sure that this has a `modelDefinition` property
return Object.keys(this).filter(function (propertyName) {
return _this4[propertyName] && hasModelValidationForProperty(_this4, propertyName) && _this4.modelDefinition.modelValidations[propertyName].owner;
}).map(function (propertyName) {
return _this4[propertyName];
});
}
}, {
key: 'getCollectionChildrenPropertyNames',
value: function getCollectionChildrenPropertyNames() {
var _this5 = this;
return Object.keys(this).filter(function (propertyName) {
return _this5.modelDefinition && _this5.modelDefinition.modelValidations && _this5.modelDefinition.modelValidations[propertyName] && _this5.modelDefinition.modelValidations[propertyName].type === 'COLLECTION';
});
}
}, {
key: 'getDirtyChildren',
value: function getDirtyChildren() {
return this.getCollectionChildren().filter(function (property) {
return property && property.dirty === true;
});
}
}, {
key: 'hasDirtyChildren',
value: function hasDirtyChildren() {
return this.getDirtyChildren().length > 0;
}
}]);
return ModelBase;
})();
exports['default'] = new ModelBase();
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _libCheck = __webpack_require__(3);
var _loggerLogger = __webpack_require__(2);
var _loggerLogger2 = _interopRequireDefault(_loggerLogger);
var _apiApi = __webpack_require__(7);
var _apiApi2 = _interopRequireDefault(_apiApi);
var _helpersJson = __webpack_require__(13);
/**
* @class ModelValidation
*/
var ModelValidation = (function () {
function ModelValidation(providedLogger) {
_classCallCheck(this, ModelValidation);
(0, _libCheck.checkType)(providedLogger, 'object', 'logger (Logger)');
this.logger = providedLogger;
}
/**
* @deprecated
* @method validate
*
* @returns {{status: boolean, messages: Array}} Returns {status: true, messages: []}
*/
_createClass(ModelValidation, [{
key: 'validate',
value: function validate() {
this.logger.warn('Client side model validation is deprecated');
throw new Error('Client side model validation is deprecated');
}
/**
* @method validateAgainstSchema
*
* @param {Model} model The model that should be validated.
* @returns {Array} Returns an array with validation messages if there are any.
*
* @description
* Sends a POST request against the `api/schemas` endpoint to check if the model is valid.
*
* @note {warn} Currently only checks
*/
}, {
key: 'validateAgainstSchema',
value: function validateAgainstSchema(model) {
if (!(model && model.modelDefinition && model.modelDefinition.name)) {
return Promise.reject('model.modelDefinition.name can not be found');
}
function extractValidationViolations(webmessage) {
if (webmessage.response && webmessage.response.errorReports) {
return webmessage.response.errorReports;
}
throw new Error('Response was not a WebMessage with the expected format');
}
var url = 'schemas/' + model.modelDefinition.name;
// TODO: The function getOwnedPropertyJSON should probably not be exposed, perhaps we could have a getJSONForModel(ownedPropertiesOnly=true) method.
return _apiApi2['default'].getApi().post(url, (0, _helpersJson.getOwnedPropertyJSON)(model)).then(function (webMessage) {
if (webMessage.status === 'OK') {
return [];
}
return Promise.reject(webMessage);
})['catch'](extractValidationViolations);
}
/**
* @method getModelValidation
* @static
*
* @returns {ModelValidation} New or memoized instance of `ModelInstance`
*
* @description
* Returns the `ModelValidation` singleton. Creates a new one if it does not yet exist.
* Grabs a logger instance by calling `Logger.getLogger`
*/
}], [{
key: 'getModelValidation',
value: function getModelValidation() {
if (this.modelValidation) {
return this.modelValidation;
}
return this.modelValidation = new ModelValidation(_loggerLogger2['default'].getLogger(console));
}
}]);
return ModelValidation;
})();
exports['default'] = ModelValidation;
module.exports = exports['default'];
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global, process) {'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _libCheck = __webpack_require__(3);
var _libUtils = __webpack_require__(1);
var _systemSystem = __webpack_require__(9);
var _systemSystem2 = _interopRequireDefault(_systemSystem);
__webpack_require__(12);
function getMergeStrategyParam() {
var mergeType = arguments.length <= 0 || arguments[0] === undefined ? 'REPLACE' : arguments[0];
var system = _systemSystem2['default'].getSystem();
if (system.version && Number(system.version.minor) <= 22) {
return 'mergeStrategy=' + mergeType;
}
return 'mergeMode=' + mergeType;
}
function getUrl(baseUrl, url) {
// If we are dealing with an absolute url use that instead
if (new RegExp('^(:?https?:)?//').test(url)) {
return url;
}
var urlParts = [];
if (baseUrl) {
urlParts.push(baseUrl);
}
urlParts.push(url);
return urlParts.join('/').replace(new RegExp('(.(?:[^:]))\/\/+', 'g'), '$1/').replace(new RegExp('\/$'), '');
}
var Api = (function () {
function Api(fetchImpl) {
_classCallCheck(this, Api);
// Optionally provide fetch to the constructor so it can be mocked during testing
if (typeof fetchImpl === 'function') {
this.fetch = fetchImpl.bind(typeof window !== 'undefined' ? window : global);
} else if (typeof fetch !== 'undefined') {
this.fetch = fetch.bind(typeof window !== 'undefined' ? window : global);
} else {
throw new Error('Failed to initialise D2 Api: No fetch implementation is available');
}
this.baseUrl = '/api';
this.defaultFetchOptions = {
mode: 'cors', // requests to different origins fail
credentials: 'include', // include cookies with same-origin requests
cache: 'default', // See https://fetch.spec.whatwg.org/#concept-request-cache-mode,
headers: new Headers()
};
}
_createClass(Api, [{
key: 'get',
value: function get(url, data, options) {
return this.request('GET', getUrl(this.baseUrl, url), data, options);
}
/* eslint-disable complexity */
}, {
key: 'post',
value: function post(url, data) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var requestUrl = getUrl(this.baseUrl, url);
var payload = data;
// Ensure that headers are defined and are treated without case sensitivity
options.headers = new Headers(options.headers || {}); // eslint-disable-line
// Pass data through JSON.stringify, unless options.contentType is 'text/plain' or false (meaning don't process)
// TODO: Deprecated - remove in v26
if (options.contentType) {
// Display a deprecation warning, except during test
if (!process.env || process.env.npm_lifecycle_event !== 'test') {
var e = new Error();
console.warn( // eslint-disable-line
'Deprecation warning: Setting `contentType` for API POST requests is deprecated, and support may ' + 'be removed in the next major release of D2. In stead you may set the `Content-Type` header ' + 'explicitly. If no `Content-Type` header is specified, the browser will try to determine one for ' + 'you.\nRequest:', 'POST', requestUrl, e.stack);
}
options.headers.set('Content-Type', 'text/plain');
delete options.contentType; // eslint-disable-line
} else if (data.constructor.name === 'FormData' && !options.headers.get('Content-Type')) {
options.headers.set('Content-Type', 'multipart/form-data');
payload = data;
} else {
payload = JSON.stringify(data);
}
return this.request('POST', requestUrl, payload, options);
}
/* eslint-enable complexity */
}, {
key: 'delete',
value: function _delete(url, options) {
return this.request('DELETE', getUrl(this.baseUrl, url), undefined, options);
}
}, {
key: 'update',
value: function update(url, data) {
var useMergeStrategy = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
// Since we are currently using PUT to save the full state back, we have to use mergeMode=REPLACE
// to clear out existing values
var urlForUpdate = useMergeStrategy === true ? url + '?' + getMergeStrategyParam() : url;
return this.request('PUT', getUrl(this.baseUrl, urlForUpdate), JSON.stringify(data));
}
/* eslint-disable complexity */
}, {
key: 'request',
value: function request(method, url, data) {
var _this = this;
var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
(0, _libCheck.checkType)(method, 'string', 'Request type');
(0, _libCheck.checkType)(url, 'string', 'Url');
var api = this;
var requestUrl = url;
var query = '';
if (requestUrl.indexOf('?') !== -1) {
query = requestUrl.substr(requestUrl.indexOf('?') + 1);
requestUrl = requestUrl.substr(0, requestUrl.indexOf('?'));
}
// Transfer filter properties from the data object to the query string
if (data && Array.isArray(data.filter)) {
query = '' + query + (query.length ? '&' : '') + 'filter=' + data.filter.join('&filter=');
delete data.filter; // eslint-disable-line no-param-reassign
}
// When using the GET method, transform the data object to query parameters
if (data && method === 'GET') {
Object.keys(data).forEach(function (key) {
query = '' + query + (query.length > 0 ? '&' : '') + key + '=' + data[key];
});
}
function getOptions(mergeOptions, requestData) {
var resultOptions = Object.assign({}, api.defaultFetchOptions, mergeOptions);
var headers = new Headers(mergeOptions.headers || {});
resultOptions.method = method;
// Only set content type when there is data to send
// GET requests and requests without data do not need a Content-Type header
// 0 and false are valid requestData values and therefore should have a content type
if (resultOptions.method === 'GET' || !requestData && requestData !== 0 && requestData !== false) {
headers['delete']('Content-Type');
} else if (requestData) {
// resultOptions.dataType = options.dataType !== undefined ? options.dataType : 'json';
if (!headers.get('Content-Type')) {
headers.set('Content-Type', data.constructor.name === 'FormData' ? 'multipart/form-data' : 'application/json');
}
resultOptions.body = requestData;
}
// Handle the dataType option used by jQuery.ajax, but throw a deprecation warning
// TODO: Remove in 2.26
if (mergeOptions.dataType) {
// Display a deprecation warning, except during test
if (!process.env || process.env.npm_lifecycle_event !== 'test') {
var e = new Error();
console.warn( // eslint-disable-line
'Deprecation warning: Setting `dataType` for API requests is deprecated, and support may be ' + 'removed in the next major release of D2. In stead you should set the `Accept` header ' + 'directly.\nRequest:', resultOptions.method, requestUrl, e.stack);
}
if (mergeOptions.dataType === 'text') {
headers.set('Accept', 'text/plain');
delete resultOptions.dataType;
}
}
resultOptions.headers = headers;
return resultOptions;
}
if (query.length) {
requestUrl = requestUrl + '?' + (0, _libUtils.customEncodeURIComponent)(query);
}
var requestOptions = getOptions(options, options.method === 'GET' ? undefined : data);
// If the provided value is valid JSON, return the parsed JSON object. If not, return the raw value as is.
function parseResponseData(value) {
try {
return JSON.parse(value);
} catch (e) {
return value;
}
}
return new Promise(function (resolve, reject) {
// fetch returns a promise that will resolve with any response received from the server
// It will be rejected ONLY if no response is received from the server, i.e. because there's no internet
_this.fetch(requestUrl, requestOptions).then(function (response) {
// If the request failed, response.ok will be false and response.status will be the status code
if (response.ok) {
response.text().then(function (text) {
return resolve(parseResponseData(text));
});
} else {
response.text().then(function (text) {
if (!process.env || process.env.npm_lifecycle_event !== 'test') {
console.warn( // eslint-disable-line
'API request failed with status ' + response.status + ' ' + response.statusText + '\n', 'Request: ' + requestOptions.method + ' ' + requestUrl);
}
reject(parseResponseData(text));
});
}
})['catch'](function (err) {
// It's not usually possible to get much info about the cause of the error programmatically, but
// the user can check the browser console for more info
if (!process.env || process.env.npm_lifecycle_event !== 'test') {
console.error('Server connection error:', err); // eslint-disable-line
}
reject('Server connection failed for API request: ' + requestOptions.method + ' ' + requestUrl);
});
});
}
/* eslint-enable complexity */
}, {
key: 'setBaseUrl',
value: function setBaseUrl(baseUrl) {
(0, _libCheck.checkTyp