UNPKG

js-data-jsonapi

Version:
1,068 lines (1,050 loc) 76.2 kB
/*! * js-data-jsonapi * @version 0.0.0-alpha.21 - Homepage <https://github.com/BlairAllegroTech/js-data-jsonapi> * @author Blair Jacobs * @copyright (c) 2016-2017 Blair Jacobs * @license MIT <https://github.com/BlairAllegroTech/js-data-jsonapi/blob/master/LICENSE> * * @overview JsonApi adapter for js-data. */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("js-data"), require("js-data-http")); else if(typeof define === 'function' && define.amd) define(["js-data", "js-data-http"], factory); else if(typeof exports === 'object') exports["DSJsonApiAdapter"] = factory(require("js-data"), require("js-data-http")); else root["DSJsonApiAdapter"] = factory(root["JSData"], root["DSHttpAdapter"]); })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__) { return /******/ (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); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { "use strict"; var JSDataLib = __webpack_require__(1); var JSDataHttp = __webpack_require__(2); var Helper = __webpack_require__(3); exports.JsonApi = __webpack_require__(4); var HttpNoContent = 204; var JsonApiAdapter = (function () { function JsonApiAdapter(options) { var _this = this; this.DSUtils = JSDataLib['DSUtils']; this.serialize = this.SerializeJsonResponse; this.deserialize = this.DeSerializeJsonResponse; if (options && options.adapter) { this.adapter = (options.adapter); this.DSUtils.deepMixIn(this.defaults, options); } else { var httpAdapter = JSDataHttp; this.adapter = (new httpAdapter(options)); } this.defaults.jsonApi = options.jsonApi || {}; this.DSUtils.deepMixIn(this.defaults.jsonApi, { usePATCH: true, updateRelationships: false }); this.DSUtils.deepMixIn(this.defaults.jsonApi, options.jsonApi); this.adapterGetPath = this.adapter.getPath; this.adapterHTTP = this.adapter.HTTP; this.adapter.getPath = function (method, resourceConfig, id, options) { return _this.getPath(method, resourceConfig, id, options); }; this.adapter.HTTP = function (options) { return _this.HTTP(options); }; } Object.defineProperty(JsonApiAdapter.prototype, "defaults", { get: function () { return this.adapter.defaults; }, enumerable: true, configurable: true }); JsonApiAdapter.prototype.SerializeJsonResponse = function (resourceConfig, attrs, config) { var serializationOptions = new Helper.SerializationOptions(resourceConfig); return Helper.JsonApiHelper.Serialize(serializationOptions, attrs, config); }; JsonApiAdapter.prototype.DeSerializeJsonResponse = function (resourceConfig, response) { var _this = this; if (Helper.JsonApiHelper.ContainsJsonApiContentTypeHeader(response.headers)) { if (response.data.errors) { response.data = Helper.JsonApiHelper.FromJsonApiError(response.data); } else { if (response.status !== HttpNoContent && response.data.data) { if (this.DSUtils.isArray(response.data.data)) { this.DSUtils.forEach(response.data.data, function (item) { if (item.type !== resourceConfig.name && _this.defaults.log) { _this.defaults.log('Warning: Json Api resource name missmatch, ' + 'JsonApi:' + (item.type || 'missing') + ', js-data:', [resourceConfig.name]); item.type = resourceConfig.name; } }); } else { var data = response.data.data; if (data.type !== resourceConfig.name && this.defaults.log) { this.defaults.log('Warning: Json Api resource name missmatch, ' + 'JsonApi:' + (response.data.data['type'] || 'missing') + ', js-data:', resourceConfig.name); data.type = resourceConfig.name; } } } var obj = Helper.JsonApiHelper.DeSerialize(new Helper.SerializationOptions(resourceConfig), response.data); response.data = obj.data; } } return response; }; JsonApiAdapter.prototype.HandleError = function (config, options, error) { return options.deserialize(config, error); }; JsonApiAdapter.prototype.getPath = function (method, resourceConfig, id, options) { if (Helper.JsonApiHelper.ContainsJsonApiContentTypeHeader(this.DSUtils.get(options, 'headers'))) { var item; if (this.DSUtils._sn(id)) { item = resourceConfig.get(id); } else if (this.DSUtils._o(id)) { item = id; } var jsonApiPath = this.DSUtils.get(options, 'jsonApi.jsonApiPath'); if (jsonApiPath) { if (options.params.__jsDataJsonapi) { delete options.params.__jsDataJsonapi; } } else { if (method === 'update') { var metaData = Helper.MetaData.TryGetMetaData(item); if (metaData && metaData.selfLink) { jsonApiPath = metaData.selfLink; } } } var basePath = options.basePath || this.defaults['basePath'] || resourceConfig.basePath; if (jsonApiPath) { if (basePath && jsonApiPath.substr(0, basePath.length) === basePath) { return jsonApiPath; } else { return this.DSUtils.makePath(basePath, jsonApiPath); } } else { return this.adapterGetPath.apply(this.adapter, [method, resourceConfig, id, options]); } } else { return this.adapterGetPath.apply(this.adapter, [method, resourceConfig, id, options]); } }; JsonApiAdapter.prototype.configureSerializers = function (options, locals) { var _this = this; var callOptions = {}; this.DSUtils.deepMixIn(callOptions, this.DSUtils.copy(this.defaults)); this.DSUtils.deepMixIn(callOptions, locals); this.DSUtils.deepMixIn(callOptions, options); callOptions['headers'] = callOptions['headers'] || {}; Helper.JsonApiHelper.AddJsonApiAcceptHeader(callOptions['headers']); Helper.JsonApiHelper.AddJsonApiContentTypeHeader(callOptions['headers']); var serialize = callOptions['serialize'] || this.defaults.serialize; if (serialize) { callOptions['serialize'] = function (resourceConfig, attrs) { return serialize(resourceConfig, _this.serialize(resourceConfig, attrs, callOptions)); }; } else { callOptions['serialize'] = function (resourceConfig, attrs) { return _this.serialize(resourceConfig, attrs, callOptions); }; } var deserialize = callOptions['deserialize'] || this.defaults.deserialize; if (deserialize) { callOptions['deserialize'] = function (resourceConfig, data) { return deserialize(resourceConfig, _this.deserialize(resourceConfig, data)); }; } else { callOptions['deserialize'] = function (resourceConfig, data) { return _this.deserialize(resourceConfig, data); }; } return callOptions; }; JsonApiAdapter.prototype.HTTP = function (options) { var _this = this; return this.adapterHTTP.apply(this.adapter, [options]) .then(function (response) { if (Helper.JsonApiHelper.ContainsJsonApiContentTypeHeader(_this.DSUtils.get(options, 'headers'))) { if (response.status === HttpNoContent && options['method'] && (options['method'] === 'put' || options['method'] === 'patch')) { if (options['data']) { response.status = 200; response['statusText'] = 'Ok'; response.headers = response.headers || {}; Helper.JsonApiHelper.AddJsonApiContentTypeHeader(response.headers); response.data = options['data']; } } } return response; }); }; JsonApiAdapter.prototype.create = function (config, attrs, options) { var _this = this; var localOptions = this.configureSerializers(options); if (attrs[config.idAttribute]) { attrs[config.idAttribute] = attrs[config.idAttribute].toString(); } return this.adapter.create(config, attrs, localOptions).then(null, function (error) { if (_this.defaults.log) { _this.defaults.log('create Failure', error); } return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.destroy = function (config, id, options) { var _this = this; var idString = id.toString(); var localOptions = this.configureSerializers(options); return this.adapter.destroy(config, idString, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.destroyAll = function (config, params, options) { var _this = this; var localOptions = this.configureSerializers(options); return this.adapter.destroyAll(config, params, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.find = function (config, id, options) { var _this = this; var idString = id.toString(); var localOptions = this.configureSerializers(options); return this.adapter.find(config, idString, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.findAll = function (config, params, options) { var _this = this; var localOptions = this.configureSerializers(options); return this.adapter.findAll(config, params, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.update = function (config, id, attrs, options) { var _this = this; var idString = id.toString(); if (attrs[config.idAttribute]) { if (attrs[config.idAttribute].toString() !== idString) { throw new Error('Json Api update expected supplied id and the primary key attribute "' + config.idAttribute + '" to be the same, you may have called update on the wrong id?'); } } else { attrs[config.idAttribute] = idString; } var localOptions = this.configureSerializers(options); if (localOptions.jsonApi.usePATCH === false) { localOptions.jsonApi.updateRelationships = (localOptions.jsonApi.updateRelationships === undefined) ? true : localOptions.jsonApi.updateRelationships; } else { localOptions.method = localOptions.method || 'patch'; localOptions.changes = (localOptions.changes === undefined) ? true : localOptions.changes; } return this.adapter.update(config, idString, attrs, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; JsonApiAdapter.prototype.updateAll = function (config, attrs, params, options) { var _this = this; var localOptions = this.configureSerializers(options); if (localOptions.jsonApi.usePATCH === false) { localOptions.jsonApi.updateRelationships = (localOptions.jsonApi.updateRelationships === undefined) ? true : localOptions.jsonApi.updateRelationships; } else { localOptions.method = localOptions.method || 'patch'; localOptions.changes = (localOptions.changes === undefined) ? true : localOptions.changes; } return this.adapter.updateAll(config, attrs, params, localOptions).then(null, function (error) { return _this.DSUtils.Promise.reject(_this.HandleError(config, localOptions, error)); }); }; return JsonApiAdapter; }()); exports.JsonApiAdapter = JsonApiAdapter; function TryGetMetaData(obj) { return Helper.MetaData.TryGetMetaData(obj); } exports.TryGetMetaData = TryGetMetaData; ; exports.version = { full: '0.0.0-alpha.21', major: parseInt('0', 10), minor: parseInt('0', 10), patch: parseInt('0', 10), alpha: true ? '21' : false, beta: true ? 'false' : false }; //# sourceMappingURL=JsonApiAdapter.js.map /***/ }, /* 1 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_1__; /***/ }, /* 2 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_2__; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { "use strict"; var JSDataLib = __webpack_require__(1); var JsonApi = __webpack_require__(4); exports.JSONAPI_META = '$_JSONAPIMETA_'; var jsonApiContentType = 'application/vnd.api+json'; exports.JSONAPI_RELATED_LINK = 'related'; exports.JSONAPI_PARENT_LINK = 'parent'; exports.JSONAPI_SELF_LINK = 'self'; var jsDataBelongsTo = 'belongsTo'; var jsDataHasMany = 'hasMany'; var jsDataHasOne = 'hasOne'; var DSUTILS = JSDataLib['DSUtils']; var MetaLinkDataImp = (function () { function MetaLinkDataImp(type, url) { this.type = type; this.url = url; } return MetaLinkDataImp; }()); var MetaData = (function () { function MetaData(type) { this.selfType = type; this.selfLink = null; this.isJsonApiReference = true; this.relationships = {}; this.links = {}; this.referenceCount = 0; } MetaData.prototype.WithRelationshipLink = function (relationName, linkType, dataType, url) { this.relationships[relationName] = this.relationships[relationName] || {}; this.relationships[relationName][linkType] = new MetaLinkDataImp(dataType, url); return this; }; MetaData.prototype.WithLink = function (linkName, url, meta) { var link = new MetaLinkDataImp(linkName, url); link.meta = meta; this.links[linkName] = link; return this; }; MetaData.prototype.getLinks = function (linkName) { return this.links[linkName]; }; MetaData.prototype.incrementReferenceCount = function () { this.referenceCount++; return this.referenceCount; }; MetaData.prototype.getRelationshipLink = function (relationName, linkType) { if (this.relationships[relationName]) { return this.relationships[relationName][linkType]; } else { return undefined; } }; MetaData.TryGetMetaData = function (obj) { if (obj) { return DSUTILS.get(obj, exports.JSONAPI_META); } else { return undefined; } }; return MetaData; }()); exports.MetaData = MetaData; var ModelPlaceHolder = (function () { function ModelPlaceHolder(type, id) { this.type = type; this.id = id; if (!type || !id) { throw new Error('Type or Id missing'); } } ModelPlaceHolder.prototype.WithForeignKey = function (keyName, keyValue, keyType) { this.keyName = keyName; this.keyValue = keyValue; this.keyType = keyType; return this; }; return ModelPlaceHolder; }()); var SerializationOptions = (function () { function SerializationOptions(def) { this.resourceDef = def; } Object.defineProperty(SerializationOptions.prototype, "type", { get: function () { return this.resourceDef.name; }, enumerable: true, configurable: true }); ; Object.defineProperty(SerializationOptions.prototype, "idAttribute", { get: function () { return this.resourceDef.idAttribute; }, enumerable: true, configurable: true }); ; SerializationOptions.prototype.relationType = function () { return this.resourceDef['type']; }; ; SerializationOptions.prototype.def = function () { return this.resourceDef; }; SerializationOptions.prototype.getResource = function (resourceName) { var resource = this.resourceDef.getResource(resourceName); return resource ? new SerializationOptions(resource) : null; }; SerializationOptions.prototype.getBelongsToRelation = function (parentType, relationName) { if (this.resourceDef.relations && this.resourceDef.relations.belongsTo) { if (relationName) { return this.resourceDef.relations.belongsTo[parentType]; } else { return this.resourceDef.relations.belongsTo[parentType]; } } return null; }; SerializationOptions.prototype.getParentRelationByLocalKey = function (localKey) { var match = null; if (this.resourceDef.relations && this.resourceDef.relations.belongsTo) { for (var r in this.resourceDef.relations.belongsTo) { if (this.resourceDef.relations.belongsTo[r]) { DSUTILS.forEach(this.resourceDef.relations.belongsTo[r], function (relation) { if (relation.localKey === localKey) { match = relation; return false; } }); } } } return match; }; SerializationOptions.prototype.getParentRelationByLocalField = function (localField) { var match = null; if (this.resourceDef.relations && this.resourceDef.relations.belongsTo) { for (var r in this.resourceDef.relations.belongsTo) { if (this.resourceDef.relations.belongsTo[r]) { DSUTILS.forEach(this.resourceDef.relations.belongsTo[r], function (relation) { if (relation.localField === localField) { match = relation; return false; } }); } } } return match; }; SerializationOptions.prototype.getChildRelation = function (relationType) { var relation = this.getChildRelations(relationType); return (relation && relation[0]) ? relation[0] : null; }; SerializationOptions.prototype.getChildRelationWithLocalField = function (relationType, localFieldName) { var localFieldNameLower = localFieldName.toLowerCase(); var relations = this.getChildRelations(relationType); var match = null; DSUTILS.forEach(relations, function (relation) { if (relation.localField === localFieldName || relation.localField === localFieldNameLower) { match = relation; return false; } }); return match; }; SerializationOptions.prototype.getChildRelationWithForeignKey = function (relationType, foreignKeyName) { var foreignKeyNameLower = foreignKeyName.toLowerCase(); var relations = this.getChildRelations(relationType); var match = null; DSUTILS.forEach(relations, function (relation) { if (relation.foreignKey === foreignKeyName || relation.foreignKey === foreignKeyNameLower) { match = relation; return false; } }); return match; }; SerializationOptions.prototype.enumerateAllChildRelations = function (callback) { DSUTILS.forEach(this.resourceDef.relationList, function (relation, index, source) { if (relation.type === jsDataHasMany || relation.type === jsDataHasOne) { return callback(relation, index, source); } }); }; SerializationOptions.prototype.enumerateAllParentRelations = function (callback) { DSUTILS.forEach(this.resourceDef.relationList, function (relation, index, source) { if (relation.type === jsDataBelongsTo) { return callback(relation, index, source); } }); }; SerializationOptions.prototype.enumerateRelations = function (callback) { DSUTILS.forEach(this.resourceDef.relationList, function (relation, index, source) { return callback(relation, index, source); }); }; SerializationOptions.prototype.getChildRelations = function (relationType) { if (this.resourceDef.relations) { var relationlower_1 = relationType.toLowerCase(); var matches_1 = []; var relationList = this.resourceDef.relationList; DSUTILS.forEach(relationList, function (relation) { if (relation.type === jsDataHasMany || relation.type === jsDataHasOne) { if (relationType === relation.relation) { matches_1.push(relation); } else { if (relationlower_1 === relation.relation) { matches_1.push(relation); LogInfo('Relation Case Insensitive match made of ' + relationType, matches_1); } } } }); return matches_1; } return null; }; SerializationOptions.prototype.getRelationByLocalField = function (relationName) { var relationlower = relationName.toLowerCase(); var relationList = this.resourceDef.relationList; var match = null; DSUTILS.forEach(relationList, function (relation) { if (relation.localField === relationName || relation.localField === relationlower) { match = relation; return false; } }); return match; }; return SerializationOptions; }()); exports.SerializationOptions = SerializationOptions; var DeSerializeResult = (function () { function DeSerializeResult(data, response) { this.data = data; this.response = response; } return DeSerializeResult; }()); function LogInfo(message, data) { if (console) { console.log(message, data); } } function LogWarning(message, data) { if (console) { console.warn(message, data); } } var JsonApiHelper = (function () { function JsonApiHelper() { } JsonApiHelper.MergeMetaData = function (res, data) { if (res) { var dataMeta = MetaData.TryGetMetaData(data); if (!dataMeta) { throw new Error('MergeMetaData failed, target object missing meta data Type:' + res.name); } else { var id = data[res.idAttribute]; var exiting = res.get(id); if (exiting) { var existingMeta = MetaData.TryGetMetaData(exiting); if (existingMeta) { dataMeta.isJsonApiReference = dataMeta.isJsonApiReference || existingMeta.isJsonApiReference; } } } } }; JsonApiHelper.ContainsHeader = function (headers, header, value) { if (headers) { for (var key in headers) { if (key.toLocaleLowerCase() === header.toLocaleLowerCase()) { var h = headers[key]; if (h.toLocaleLowerCase().indexOf(value) > -1) { return true; } } } } return false; }; JsonApiHelper.ContainsJsonApiContentTypeHeader = function (headers) { return JsonApiHelper.ContainsHeader(headers, 'Content-Type', jsonApiContentType); }; JsonApiHelper.AddJsonApiContentTypeHeader = function (headers) { headers['Content-Type'] = jsonApiContentType; }; JsonApiHelper.AddJsonApiAcceptHeader = function (headers) { headers['Accept'] = jsonApiContentType; }; JsonApiHelper.FromJsonApiError = function (response) { var responseObj; if (response.errors) { responseObj = new JsonApi.JsonApiRequest(); if (DSUTILS.isArray(response.errors)) { DSUTILS.forEach(response.errors, function (item) { responseObj.WithError(DSUTILS.deepMixIn(new JsonApi.JsonApiError(), item)); }); } else { responseObj.WithError(DSUTILS.deepMixIn(new JsonApi.JsonApiError(), response.errors)); } } else { responseObj = JsonApiHelper.CreateInvalidResponseError(response); } return responseObj; }; JsonApiHelper.Serialize = function (options, attrs, config) { var _this = this; var result = new JsonApi.JsonApiRequest(); if (DSUTILS.isArray(attrs)) { result.data = new Array(); DSUTILS.forEach(attrs, function (item) { result.WithData(_this.ObjectToJsonApiData(options, item, config)); }); } else { result.data = this.ObjectToJsonApiData(options, attrs, config); } return result; }; JsonApiHelper.DeSerialize = function (options, response) { var _this = this; if (response.data === null) { return new DeSerializeResult([], null); } if (DSUTILS.isArray(response.data)) { if (response.data.length === 0) { return new DeSerializeResult([], null); } } var newResponse = new JsonApi.JsonApiRequest(); if (response.data) { if (DSUTILS.isArray(response.included)) { DSUTILS.forEach(response.included, function (item) { _this.NormaliseLinkFormat(item.links); for (var relation in item.relationships) { if (item.relationships[relation]) { _this.NormaliseLinkFormat(item.relationships[relation].links); var isArray = DSUTILS.isArray(item.relationships[relation].data); item.relationships[relation] = DSUTILS.deepMixIn(new JsonApi.JsonApiRelationship(isArray), item.relationships[relation]); } } newResponse.WithIncluded(DSUTILS.deepMixIn(new JsonApi.JsonApiData('unknown'), item)); }); } if (DSUTILS.isArray(response.data)) { DSUTILS.forEach(response.data, function (item) { _this.NormaliseLinkFormat(item.links); for (var relation in item.relationships) { if (item.relationships[relation]) { _this.NormaliseLinkFormat(item.relationships[relation].links); var isArray = DSUTILS.isArray(item.relationships[relation].data); item.relationships[relation] = DSUTILS.deepMixIn(new JsonApi.JsonApiRelationship(isArray), item.relationships[relation]); } } newResponse.WithData(DSUTILS.deepMixIn(new JsonApi.JsonApiData(''), item)); }); } else { var item_1 = response.data; this.NormaliseLinkFormat(item_1.links); for (var relation in item_1.relationships) { if (item_1.relationships[relation]) { this.NormaliseLinkFormat(item_1.relationships[relation].links); var isArray = DSUTILS.isArray(item_1.relationships[relation].data); item_1.relationships[relation] = DSUTILS.deepMixIn(new JsonApi.JsonApiRelationship(isArray), item_1.relationships[relation]); } } newResponse.WithSingleData(DSUTILS.deepMixIn(new JsonApi.JsonApiData(''), response.data)); } } if (response.links) { this.NormaliseLinkFormat(response.links); for (var link in response.links) { if (response.links[link]) { newResponse.AddLink(link, response.links[link]); } } } var data = {}; var included = {}; var jsDataJoiningTables = {}; if (DSUTILS.isArray(newResponse.data)) { DSUTILS.forEach(newResponse.data, function (item) { data[item.type] = data[item.type] || {}; data[item.type][item.id] = _this.DeserializeJsonApiData(options, item, jsDataJoiningTables); var metaData = MetaData.TryGetMetaData(data[item.type][item.id]); metaData.incrementReferenceCount(); }); } else { var item_2 = newResponse.data; if (item_2) { data[item_2.type] = data[item_2.type] || {}; data[item_2.type][item_2.id] = this.DeserializeJsonApiData(options, item_2, jsDataJoiningTables); var metaData = MetaData.TryGetMetaData(data[item_2.type][item_2.id]); metaData.incrementReferenceCount(); } } JsonApiHelper.AssignLifeTimeEvents(options.def()); DSUTILS.forEach(newResponse.included, function (item) { var includedDef = options.getResource(item.type); included[item.type] = included[item.type] || {}; included[item.type][item.id] = _this.DeserializeJsonApiData(includedDef, item, jsDataJoiningTables); JsonApiHelper.AssignLifeTimeEvents(includedDef.def()); }); var itemSelector = function (item) { var newItem = included[item.type] ? included[item.type][item.id] : (data[item.type] ? data[item.type][item.id] : (jsDataJoiningTables[item.type] ? jsDataJoiningTables[item.type][item.id] : null)); return newItem; }; var toManyPlaceHolderVisitor = function (data) { if (data.constructor === ModelPlaceHolder) { var itemPlaceHolder = data; var newItem = itemSelector(itemPlaceHolder); if (newItem) { if (itemPlaceHolder.keyName) { newItem[itemPlaceHolder.keyName] = itemPlaceHolder.keyValue; } var meta = MetaData.TryGetMetaData(newItem); if (meta.incrementReferenceCount() === 1) { return newItem; } else { return undefined; } } else { var newItem_1 = {}; if (itemPlaceHolder.keyName) { newItem_1[itemPlaceHolder.keyName] = itemPlaceHolder.keyValue; } var itemOptions = options.getResource(itemPlaceHolder.type); var metaData_1 = new MetaData(itemPlaceHolder.type); metaData_1.isJsonApiReference = true; newItem_1[exports.JSONAPI_META] = metaData_1; newItem_1[itemOptions.idAttribute] = itemPlaceHolder.id; JsonApiHelper.AssignLifeTimeEvents(itemOptions.def()); return newItem_1; } } return data; }; var toOnePlaceHolderVisitor = function (data, localField, opt) { var val = data[localField]; if (val && val.constructor === ModelPlaceHolder) { var itemPlaceHolder = val; var newItem = itemSelector(itemPlaceHolder); if (newItem) { var meta = MetaData.TryGetMetaData(newItem); if (meta.incrementReferenceCount() === 1) { return newItem; } else { if (itemPlaceHolder.keyName) { newItem[itemPlaceHolder.keyName] = itemPlaceHolder.keyValue; } else { var relation = opt.getParentRelationByLocalField(localField); if (relation && relation.type === jsDataBelongsTo) { data[relation.localKey] = itemPlaceHolder.id; } } return undefined; } } else { newItem = {}; var itemOptions = options.getResource(itemPlaceHolder.type); if (itemPlaceHolder.keyName) { newItem[itemPlaceHolder.keyName] = itemPlaceHolder.keyValue; } var metaData_2 = new MetaData(itemPlaceHolder.type); metaData_2.isJsonApiReference = true; newItem[exports.JSONAPI_META] = metaData_2; newItem[itemOptions.idAttribute] = itemPlaceHolder.id; JsonApiHelper.AssignLifeTimeEvents(itemOptions.def()); return newItem; } } return data[localField]; }; this.RelationshipVisitor(data, options, toManyPlaceHolderVisitor, toOnePlaceHolderVisitor); this.RelationshipVisitor(included, options, toManyPlaceHolderVisitor, toOnePlaceHolderVisitor); this.RelationshipVisitor(jsDataJoiningTables, options, toManyPlaceHolderVisitor, toOnePlaceHolderVisitor); var registration = function (type) { var typeOptions = options.getResource(type); if (typeOptions) { JsonApiHelper.AssignLifeTimeEvents(typeOptions.def()); } else { throw new Error('Unknow type:' + type); } }; this.DataTypeVisitor(data, registration); this.DataTypeVisitor(included, registration); this.DataTypeVisitor(jsDataJoiningTables, registration); if (data) { var jsDataArray = []; if (DSUTILS.isArray(newResponse.data)) { DSUTILS.forEach(newResponse.data, function (item) { if (data[item.type] && data[item.type][item.id]) { jsDataArray.push(data[item.type][item.id]); } }); return new DeSerializeResult(jsDataArray, newResponse); } else { if (newResponse.data) { var item = newResponse.data; if (data[item.type] && data[item.type][item.id]) { return new DeSerializeResult(data[item.type][item.id], newResponse); } } else { return new DeSerializeResult(null, newResponse); } } } }; JsonApiHelper.RelationshipVisitor = function (data, options, toManyVisitor, toOneVisitor) { if (data) { for (var dataType in data) { if (data[dataType]) { for (var dataId in data[dataType]) { if (data[dataType][dataId]) { var dataObject = data[dataType][dataId]; for (var prop in dataObject) { if (DSUTILS.isArray(dataObject[prop])) { DSUTILS.forEach(dataObject[prop], function (item, index, source) { var result = toManyVisitor(item); source[index] = result; }); for (var i = dataObject[prop].length; i >= 0; i--) { if (!dataObject[prop][i]) { dataObject[prop].splice(i, 1); } } if (dataObject[prop].length === 0) { delete dataObject[prop]; } } else { var opt = options.getResource(dataType); var result = toOneVisitor(dataObject, prop, opt); if (result !== undefined) { dataObject[prop] = result; } else { delete dataObject[prop]; } } } } } } } } }; JsonApiHelper.DataTypeVisitor = function (data, visitor) { for (var dataType in data) { if (dataType) { visitor(dataType); } } }; JsonApiHelper.CreateInvalidResponseError = function (response) { var responseObj = new JsonApi.JsonApiRequest(); var e = new JsonApi.JsonApiError(); e.title = 'Invalid response'; e.detail = 'Response is incorrectly formed: ' + JSON.stringify(response); responseObj.WithError(e); return responseObj; }; JsonApiHelper.ObjectToJsonApiData = function (options, attrs, config) { if (!options.type) { throw new Error('Type required within options'); } var data = new JsonApi.JsonApiData(options.type); if (attrs[options.idAttribute]) { data.WithId(attrs[options.idAttribute]); } if (config.changes && attrs[options.idAttribute]) { var id = attrs[options.idAttribute]; if (options.def().hasChanges(id)) { var changes = options.def().changes(id); DSUTILS.forOwn(changes['added'], function (value, prop) { if (prop !== options.idAttribute && prop !== exports.JSONAPI_META && prop.indexOf('$') < 0) { data.WithAttribute(prop, value); } }); DSUTILS.forOwn(changes['changed'], function (value, prop) { if (prop !== options.idAttribute && prop !== exports.JSONAPI_META && prop.indexOf('$') < 0) { data.WithAttribute(prop, value); } }); DSUTILS.forOwn(changes['removed'], function (value, prop) { if (prop !== options.idAttribute && prop !== exports.JSONAPI_META && prop.indexOf('$') < 0) { data.WithAttribute(prop, null); } }); } } else { DSUTILS.forOwn(attrs, function (value, prop) { if (prop !== options.idAttribute && prop !== exports.JSONAPI_META && prop.indexOf('$') < 0) { data.WithAttribute(prop, value); } }); } if (config.jsonApi.updateRelationships === true) { options.enumerateAllChildRelations(function (relation) { var relatedDef = options.getResource(relation.relation); if (relation.type === jsDataHasMany) { var relationship = new JsonApi.JsonApiRelationship(true); var relatedObjects = DSUTILS.get(attrs, relation.localField); if (relatedObjects) { DSUTILS.forEach(relatedObjects, function (item) { relationship.WithData(relation.relation, item[relatedDef.idAttribute]); }); } data.WithRelationship(relation.localField, relationship); } if (relation.type === jsDataHasOne) { var relationship = null; var relatedObject = DSUTILS.get(attrs, relation.localField); if (relatedObject) { relationship = new JsonApi.JsonApiRelationship(false) .WithData(relation.relation, relatedObject[relatedDef.idAttribute]); } data.WithRelationship(relation.localField, relationship); } return true; }); } if (data.attributes) { options.enumerateRelations(function (relation) { var localKeys = relation.localKey || relation.localKeys; if (localKeys) { if (DSUTILS.get(data.attributes, localKeys)) { delete data.attributes[localKeys]; } } return true; }); } return data; }; JsonApiHelper.DeserializeJsonApiData = function (options, data, joinData) { if (!options) { throw new Error('Missing Serialization Options, indicates possible missing jsData resource: ' + data.type); } var fields = DSUTILS.copy(data.attributes || {}); var metaData = new MetaData(data.type); metaData.isJsonApiReference = false; metaData.selfLink = data.GetSelfLink(); if (data.id) { fields[options.idAttribute] = data.id.toString(); } else { throw new Error('Missing required "id" property in JsonApi response'); } if (data.type) { } else { throw new Error('Missing required "type" property in JsonApi response'); } JsonApiHelper.setParentIds(options, data, fields, metaData); for (var relationName in data.relationships) { if (data.relationships[relationName]) { var relationship = data.relationships[relationName]; var relationshipDef = options.getRelationByLocalField(relationName); if (relationshipDef) { if (relationshipDef.type === jsDataHasMany || relationshipDef.type === jsDataHasOne) { var relatedLink = relationship.FindLinkType(exports.JSONAPI_RELATED_LINK); if (relatedLink) { metaData.WithRelationshipLink(relationshipDef.localField, exports.JSONAPI_RELATED_LINK, relationshipDef.relation, relatedLink); } var selfLink = relationship.FindLinkType(exports.JSONAPI_SELF_LINK); if (selfLink) { metaData.WithRelationshipLink(relationshipDef.localField, exports.JSONAPI_SELF_LINK, relationshipDef.relation, selfLink); } var hasData = (relationship.data && (!DSUTILS.isArray(relationship.data) || (DSUTILS.isArray(relationship.data) && relationship.data.length > 0))); if (hasData) { var joinTableFactory = null; var childRelationType = DSUTILS.isArray(relationship.data) ? relationship.data[0].type : relationship.data.type; relationshipDef = options.getChildRelationWithLocalField(childRelationType, relationName); if (!relationshipDef) { if (options.def().meta && options.def().meta[relationName]) { var joinMetaData = options.def().meta[relationName]; var joiningTypeResourceDef = options.getResource(joinMetaData.joinType); relationshipDef = options.getChildRelationWithLocalField(joinMetaData.joinType, relationName); if (relationshipDef.type !== jsDataHasMany) { throw new Error('Expected relationship Named:' + relationName + 'on type:' + options.type + ' with many-to-many meta data to be a to many relationship.'); } var joiningTableChildRelation = joiningTypeResourceDef.getBelongsToRelation(joinMetaData.type); if (!joiningTableChildRelation || !joiningTableChildRelation[0]) { throw new Error('Expected Many-To-Many Joining table to have a "belongsTo" relation of type:' + joinMetaData.type + ' as defined in meta data of type:' + data.type); } var joinState = { idAttribute: joiningTypeResourceDef.idAttribute, type: joiningTypeResourceDef.type, dataLocalField: relationshipDef.localField, dataForeignKey: relationshipDef.foreignKey, joinTypeDef: joiningTypeResourceDef }; joinTableFactory = function (foreignRecord, relationshipLink) { var pk = (data.type > foreig