papir
Version:
Rest API Modelling library
1,340 lines (1,138 loc) • 73.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _prop = _interopRequireDefault(require("./prop"));
var _query = _interopRequireDefault(require("./query"));
var _axios = _interopRequireDefault(require("axios"));
var _util = require("../services/util");
/**
* Endpoint
*/
var Endpoint = function Endpoint(endpoint, controller) {
var _this = this;
var apiSlug = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var predefined = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var config = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
(0, _classCallCheck2["default"])(this, Endpoint);
/**
* Public Scope
*/
var accessor = this;
/**
* Public / Reserved Variables
*/
/**
* Get last fetched raw value
*/
accessor.raw = null;
/**
* If endpoint is list related, children is saved here
*/
accessor.children = [];
/**
* Default arguments added to requests
*/
accessor.args = {
fetch: [],
save: [],
custom: [],
create: [],
batch: [],
upload: [],
remove: [],
get: [],
post: [],
put: [],
patch: [],
"delete": [],
head: [],
trace: [],
connect: [],
options: []
/**
* Shared Variables
*/
};
accessor.shared = {
storage: null,
// Storage is free to be used for anything on app level
// Default Config (config level 0 - greater is stronger)
config: {
multiple: false,
batchIdentifier: 'batch',
post: {
keepNull: false
}
},
api: null,
defaultApi: apiSlug,
map: null,
endpoint: endpoint,
controller: controller,
requester: controller,
predefined: predefined,
accessor: accessor,
reserved: ['loading', 'loaders', // Property Requesters
'fetch', 'custom', 'save', 'create', 'batch', 'clear', 'upload', 'remove', // Custom Requesters
'get', 'post', 'put', 'patch', 'delete', 'head', 'trace', 'connect', 'options', // Property Methods
'args', 'query', 'set', 'clone', 'changes', 'props', 'shared', 'identifier', 'identifiers', 'removeIdentifiers', 'reverseMapping', // Accessor Variables
'children', 'raw', 'headers', 'invalids', 'exchange', 'sort', 'reserved']
/**
* Private Variables
*/
};
var cancelers = {
fetch: null,
save: null,
create: null,
remove: null,
upload: null,
batch: null
/**
* Public / Reserved Variable Names
* @warning Can not be used as a property name in models
*/
};
accessor.loading = false;
accessor.loaders = [];
accessor.invalids = {}; // Reserved properties from Server is stored here
accessor.headers = {
mapped: {},
unmapped: {} // @note - Related to Properties
/**
* Private Methods
* ---------------
* Initialization
*/
};
var init = function init() {
var accessor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this;
/**
* If an Endpoint object was passed instead of string, copy values to this endpoint before resolving constructors
*/
if (typeof accessor.shared.endpoint !== 'string' && typeof accessor.shared.endpoint !== 'undefined') {
accessor.shared.controller = accessor.shared.endpoint.shared.controller; // Replace controller
accessor.shared.requester = accessor.shared.endpoint.shared.requester; // Replace requester
// Replace defaultApi only if no apiSlug was given
if (accessor.shared.defaultApi === null) {
accessor.shared.defaultApi = accessor.shared.endpoint.shared.defaultApi;
}
accessor.args = (0, _util.clone)({}, accessor.shared.endpoint.args);
accessor.set(accessor.shared.endpoint, false); // Replace props
accessor.shared.config = accessor.shared.endpoint.shared.config; // Replace config
accessor.shared.endpoint = accessor.shared.endpoint.shared.endpoint; // Replace endpoint string
}
/**
* Map Resolver
*/
var resolveMap = function resolveMap() {
var map = null;
try {
map = accessor.shared.api.mappings[accessor.shared.endpoint];
if (typeof map !== 'undefined' && typeof map.config !== 'undefined' && map.config.constructor === Object) {
// Mapped Config (config level 1 - greater is stronger)
accessor.shared.config = Object.assign((0, _util.clone)({}, accessor.shared.config), map.config);
}
} catch (e) {
console.error(e);
}
return map;
};
/**
* Resolve Requester
*/
if (typeof accessor.shared.controller.apis !== 'undefined') {
accessor.shared.defaultApi = accessor.shared.defaultApi === null ? accessor.shared.controller["default"] : accessor.shared.defaultApi;
accessor.shared.api = accessor.shared.controller.apis[accessor.shared.defaultApi];
accessor.shared.requester = accessor.shared.api.requester;
accessor.shared.map = resolveMap(); // Custom Config (config level 2 - greater is stronger)
accessor.shared.config = Object.assign((0, _util.clone)({}, accessor.shared.config), config);
accessor.shared.buildProps(accessor.shared.map, accessor.shared.predefined);
} else {
console.error('No apis is hooked to Controller', accessor.shared.controller);
accessor.shared.controller = null;
}
};
/**
* Shared Methods
*/
/**
* Build mapped / predefined properties
*/
accessor.shared.buildProps = function () {
var map = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.map;
var predefined = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.shared.predefined;
if (map !== null && typeof map !== 'undefined' && typeof map.props !== 'undefined') {
try {
Object.keys(map.props).reduce(function (prev, key) {
if (!accessor.reserved(key) && typeof accessor[key] === 'undefined') {
accessor[map.props[key]] = new _prop["default"](accessor, map.props[key], null);
} else if (key === 'invalids' && typeof accessor.invalids[key] === 'undefined') {
accessor.invalids[map.props[key]] = new _prop["default"](accessor, map.props[key], null);
}
}, {});
} catch (error) {
console.error('Error in property mapping for api ' + accessor.shared.defaultApi);
console.error(map.props);
}
}
try {
Object.keys(predefined).reduce(function (prev, key) {
if (!accessor.reserved(key) && typeof accessor[key] === 'undefined') {
accessor[key] = new _prop["default"](accessor, key, predefined[key]);
} else if (!accessor.reserved(key) && typeof accessor[key] !== 'undefined') {
accessor[key].value = predefined[key];
accessor[key].changed(false);
} else if (accessor.reserved(key) && typeof accessor.invalids[key] === 'undefined') {
accessor.invalids[key] = new _prop["default"](accessor, key, predefined[key]);
} else {
accessor.invalids[key].value = predefined[key];
accessor.invalids[key].changed(false);
}
}, {});
} catch (error) {
console.error('Error in predefined properties');
console.error(predefined);
}
if (map !== null && typeof map !== 'undefined' && typeof map.identifier !== 'undefined' && map.identifier !== null && map.identifier !== '') {
var mappedIdentifier = map.identifier;
if (typeof map.props !== 'undefined' && typeof map.props[map.identifier] !== 'undefined') {
mappedIdentifier = map.props[map.identifier];
}
if (!accessor.reserved(mappedIdentifier) && typeof accessor[mappedIdentifier] !== 'undefined') {
accessor.identifier = accessor[mappedIdentifier];
} else if (accessor.reserved(mappedIdentifier) && typeof accessor.invalids[mappedIdentifier] !== 'undefined') {
accessor.identifier = accessor.invalids[mappedIdentifier];
} else if (!accessor.reserved(mappedIdentifier)) {
accessor.identifier = accessor[mappedIdentifier] = new _prop["default"](accessor, mappedIdentifier);
} else {
accessor.identifier = accessor.invalids[mappedIdentifier] = new _prop["default"](accessor, mappedIdentifier);
}
} else {
accessor.identifier = null;
}
};
/**
* Url Resolver
*/
accessor.shared.resolveUrl = function () {
var endpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.endpoint;
var map = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.shared.map;
var api = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : accessor.shared.api;
var args = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
var batch = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var base = api !== null && typeof api.base !== 'undefined' ? api.base : ''; // Remove last slash if any from base
if (base.length > 0 && base[base.length - 1] === '/') {
base = base.slice(0, -1);
}
var path = endpoint; // If mapping is set
if (map !== null && typeof map !== 'undefined') {
path = map.endpoint; // Add slash to path if missing
if (path.length > 0 && path[0] !== '/') {
path = '/' + path;
}
} // Resolve Identifiers. Ex.: {id} or {/parentId} etc...
var identifiers = accessor.identifiers(path);
Object.keys(identifiers).reduce(function (prev, key) {
var slash = identifiers[key].slash;
var hook = identifiers[key].hook; // Resolve mapping
if (map !== null && typeof map !== 'undefined' && typeof map.props !== 'undefined') {
key = typeof map.props[key] !== 'undefined' ? map.props[key] : key;
} // Replace hook with value from mapped prop
if (!accessor.reserved(key) && typeof accessor[key] !== 'undefined' && accessor[key].value !== null && (batch || key !== accessor.shared.config.batchIdentifier)) {
path = path.replace(hook, (slash ? '/' : '') + accessor[key].value);
} else if (accessor.reserved(key) && typeof accessor.invalids[key] !== 'undefined' && accessor[key].value !== null && (batch || key !== accessor.shared.config.batchIdentifier)) {
path = path.replace(hook, (slash ? '/' : '') + accessor.invalids[key].value);
} else {
path = path.replace(hook, '');
}
}, {});
while (path.indexOf('//') !== -1) {
path = path.replace('//', '/');
}
var url = base + path;
if (map !== null && typeof map !== 'undefined' && typeof map.params !== 'undefined' && map.params.constructor === Array) {
if (args !== null) {
args = args.concat(map.params);
} else {
args = map.params;
}
} // Add Query Arguments
if (args !== null) {
if (url.indexOf('?') === -1) {
url += '?';
} else if (url[url.length - 1] !== '?' && url[url.length - 1] !== '&') {
url += '&';
}
for (var i = 0, l = args.length; i < l; i++) {
var arg = args[i];
url += arg.key + '=' + arg.value + '&';
}
if (url[url.length - 1] === '&' || url[url.length - 1] === '?') {
url = url.slice(0, -1);
}
}
return url;
};
/**
* Start Loader
*/
var startLoader = function startLoader(loadSlug) {
accessor.loading = true;
return accessor.loaders.push(loadSlug);
};
/**
* Stop Loader
*/
var stopLoader = function stopLoader(loadSlug) {
var index = accessor.loaders.indexOf(loadSlug);
if (index !== -1) {
accessor.loaders.splice(index, 1);
accessor.loading = accessor.loaders.length > 0;
}
return accessor.loaders;
};
/**
* Handle Cancelation of Running Requests
*/
accessor.shared.handleCancellation = function (cancellation) {
if (cancellation !== null) {
cancellation();
}
return {
promise: new Promise(function (resolve) {
cancellation = resolve;
}),
cancellation: cancellation
};
};
/**
* Handle Mapping
*/
accessor.shared.handleMapping = function (response) {
var key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var batch = arguments.length > 2 ? arguments[2] : undefined;
var multiple = arguments.length > 3 ? arguments[3] : undefined;
var map = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : accessor.shared.map;
var conf = (0, _util.clone)({}, accessor.shared.config);
return new Promise(function (resolve, reject) {
var resolved = false;
var data = response.data; // Raw from server
var headers = response.headers; // In lowercase
try {
var parsed = data;
var isObjOrArray = parsed.constructor === Object || parsed.constructor === Array;
if (!isObjOrArray) {
parsed = JSON.parse(parsed);
}
if (typeof parsed !== 'undefined' && parsed !== null && isObjOrArray) {
if (!batch && !multiple) {
// Parse Data
response = accessor.set(parsed, false, true, key);
} else if (batch && map !== null && typeof map !== 'undefined') {
if (parsed.constructor === Object) {
var match = 0;
var hasBatch = map.batch !== null && typeof map.batch !== 'undefined';
if (hasBatch) {
Object.keys(map.batch).reduce(function (prev, key) {
if (typeof parsed[map.batch[key]] !== 'undefined') {
match++;
}
}, {});
} // If response has batch mapping keys, resolve by keys
if (match > 0) {
var deleteKey = typeof map.batch["delete"] !== 'undefined' && map.batch["delete"] !== null ? map.batch["delete"] : 'delete'; // Exchange all without delete
Object.keys(parsed).reduce(function (prev, method) {
// Exchange updated
if (method !== deleteKey) {
for (var i = 0, l = parsed[method].length; i < l; i++) {
var child = parsed[method][i];
var _endpoint = new Endpoint(accessor, accessor.shared.controller, accessor.shared.defaultApi, Object.assign(child, accessor.shared.predefined), Object.assign(conf, {
multiple: false
}));
accessor.exchange(_endpoint);
}
} else {
// Remove deleted
for (var _i = 0, _l = parsed[method].length; _i < _l; _i++) {
var _child = parsed[method][_i];
var _endpoint2 = new Endpoint(accessor, accessor.shared.controller, accessor.shared.defaultApi, Object.assign(_child, accessor.shared.predefined), Object.assign(conf, {
multiple: false
}));
accessor.exchange(_endpoint2, true, false, true);
}
}
}, {});
} else {
// If response has no keys mapped in batch, expect one instance
var _endpoint3 = new Endpoint(accessor, accessor.shared.controller, accessor.shared.defaultApi, Object.assign(parsed, accessor.shared.predefined), Object.assign(conf, {
multiple: false
}));
accessor.exchange(_endpoint3);
}
} else {
// If response is array expect multiple instances
for (var i = 0, l = parsed.length; i < l; i++) {
var obj = parsed[i];
var _endpoint4 = new Endpoint(accessor, accessor.shared.controller, accessor.shared.defaultApi, Object.assign(obj, accessor.shared.predefined), Object.assign(conf, {
multiple: false
}));
accessor.exchange(_endpoint4);
}
}
} else if (multiple) {
if (response.config.method.toLowerCase() === 'get') {
accessor.children = [];
}
for (var _i2 = 0, _l2 = parsed.length; _i2 < _l2; _i2++) {
var child = parsed[_i2];
var _endpoint5 = new Endpoint(accessor, accessor.shared.controller, accessor.shared.defaultApi, Object.assign(child, accessor.shared.predefined), Object.assign(conf, {
multiple: false
}));
if (response.config.method.toLowerCase() === 'get') {
accessor.children.push(_endpoint5);
} else {
if (!accessor.exchange(_endpoint5)) {
accessor.children.push(_endpoint5);
}
}
}
} // Parse Headers
if (key === null) {
Object.keys(headers).reduce(function (prev, key) {
if (map !== null && typeof map !== 'undefined' && typeof map.headers !== 'undefined' && typeof map.headers[key] !== 'undefined') {
accessor.headers.mapped[map.headers[key]] = headers[key];
} else {
accessor.headers.unmapped[key] = headers[key];
}
}, {});
}
}
resolved = true;
resolve(response);
} catch (error) {} // Not valid JSON, go to next parser
// @todo - Add additional parsers. Ex. xml
if (!resolved) {
reject(new Error({
error: 'Invalid Data',
message: 'Could not parse data from response',
data: data,
response: response
}));
}
});
};
/**
* Handle Request Error Catching
*/
accessor.shared.handleError = function (error) {
if (_axios["default"].isCancel(error)) {// Manually cancelled
} else if (error.response) {// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// error.response.data
// error.response.status
// error.response.headers
} else if (error.request) {// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
// error.request
} else {} // Something happened in setting up the request that triggered an Error
// error.message
// error.config
return error;
};
/**
* Handle Request Success Response
*/
accessor.shared.handleSuccess = function (response) {
var replace = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var batch = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var map = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : accessor.shared.map;
var multiple = map !== null && typeof map !== 'undefined' && typeof map.multiple !== 'undefined' && map.multiple || accessor.shared.config.multiple;
return new Promise(function (resolve, reject) {
if (replace) {
accessor.shared.handleMapping(response, key, batch, multiple).then(function (results) {
resolve(results);
})["catch"](function (error) {
reject(error);
});
} else {
resolve(response);
}
});
};
/**
* Exchange endpoint in accessor.children with match from input
* @returns Endpoint (exchanged) | Endpoint.children (On Remove) | false (If no match found)
*/
accessor.exchange = function (endpoint)
/* , map = accessor.shared.map */
{
var add = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var reliable = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var remove = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
// @note - This could be more heavy and alot slower
var smartFind = function smartFind(endpoint) {
// Reliable.
// Check for Creation Identifier match.
var exchange = resolveCreationIdentifier(endpoint); // Reliable.
// Incoming needs all existing props (No differ).
// Existing needs all incoming props (No differ).
if (typeof exchange === 'undefined') {
exchange = findExactMatch(endpoint);
} // Not reliable, but could be usable anyways
if (!reliable && typeof exchange === 'undefined') {
// @todo - Add resolveByIndex (find response index by request index) method and make it optional in config
// Unreliable.
// Incoming needs all existing props (No differ).
// Existing could have more props.
if (typeof exchange === 'undefined') {
exchange = findExactExistingMatch(endpoint);
} // Unreliable.
// Existing needs all incoming props (No differ).
// Incoming could have more props.
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findExactIncomingMatch(endpoint);
} // Unreliable.
// Incoming needs all unchanged props (No differ).
// Existing could have more props.
// Incoming could have more props.
// Existing changes is replaced by incoming changes (Also differed).
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findExactUnchangedMatch(endpoint);
} // Unreliable.
// Incoming needs all changed props (No differ).
// Existing could have more props.
// Incoming could have more props.
// Existing props is replaced by incoming props (Also differed) if not changed.
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findExactChangedMatch(endpoint);
} // Unreliable.
// Incoming props which matches existing (No differ).
// Existing could have more props.
// Incoming could have more props.
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findIncomingMatch(endpoint);
} // Unreliable.
// Incoming props which matches unchanged existing (No differ).
// Existing could have more props.
// Incoming could have more props.
// Existing changes is replaced by incoming changes (Also differed).
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findUnchangedMatch(endpoint);
} // Unreliable.
// Incoming props which matches changed existing (No differ).
// Existing could have more props.
// Incoming could have more props.
// Existing props is replaced by incoming props (Also differed) if not changed.
// Extra incoming props are added to existing props.
if (typeof exchange === 'undefined') {
exchange = findChangedMatch(endpoint);
}
}
return exchange;
}; // Resolve Creation Identifier
var resolveCreationIdentifier = function resolveCreationIdentifier(endpoint) {
var match;
for (var i = 0, l = accessor.children.length; i < l; i++) {
var child = accessor.children[i];
if (child.shared.map !== null && typeof child.shared.map !== 'undefined' && typeof child.shared.map.creationIdentifier !== 'undefined' && typeof child.shared.creationIdentifier !== 'undefined' && child.shared.creationIdentifier !== '') {
var identifier = child.shared.map.creationIdentifier;
var prop = identifier.split('=')[0];
if (!endpoint.reserved(prop)) {
if (typeof endpoint[prop] !== 'undefined' && typeof endpoint[prop].value !== 'undefined' && JSON.stringify(endpoint[prop].value).indexOf(child.shared.creationIdentifier) !== -1) {
if (!endpoint.reserved(child.identifier.key)) {
child.identifier.value = endpoint[child.identifier.key].value;
} else {
child.identifier.value = endpoint.invalids[child.identifier.key].value;
}
match = child;
}
} else {
if (typeof endpoint.invalids[prop] !== 'undefined' && typeof endpoint.invalids[prop].value !== 'undefined' && JSON.stringify(endpoint.invalids[prop].value).indexOf(child.shared.creationIdentifier) !== -1) {
if (!endpoint.reserved(child.identifier.key)) {
child.identifier.value = endpoint[child.identifier.key].value;
} else {
child.identifier.value = endpoint.invalids[child.identifier.key].value;
}
match = child;
}
}
}
}
return match;
}; // Find exact match by all props (Reliable)
var findExactMatch = function findExactMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
if (typeof endpointProps[key] !== 'undefined') {
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// If unchanged props doesnt match, set match to false
match = false;
}
} else {
match = false;
}
}, {});
Object.keys(endpointProps).reduce(function (prev, key) {
if (typeof props[key] === 'undefined') {
match = false;
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find exact match by all exisiting props (Reliable)
var findExactExistingMatch = function findExactExistingMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
if (typeof endpointProps[key] !== 'undefined') {
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// If unchanged props doesnt match, set match to false
match = false;
}
} else {
match = false;
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find exact match by unchanged props (Less Reliable - Requires incoming props to exist)
var findExactUnchangedMatch = function findExactUnchangedMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var changes = child.changes();
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
// If not changed prop
if (typeof changes[key] === 'undefined') {
// If new child has same prop
if (typeof endpointProps[key] !== 'undefined') {
// If they do not match
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// Skip this
match = false;
} // If incoming prop doesnt exist
} else {
// Skip this
match = false;
}
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find exact match by changed props (Less Reliable - Requires incoming props to exist)
var findExactChangedMatch = function findExactChangedMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var changes = child.changes();
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
// If changed prop
if (typeof changes[key] !== 'undefined') {
// If new child has same prop
if (typeof endpointProps[key] !== 'undefined') {
// If they do not match
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// Skip this
match = false;
} // If incoming prop doesnt exist
} else {
// Skip this
match = false;
}
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find match by unchanged props (Less Reliable - Doesnt require incoming props to exist)
var findUnchangedMatch = function findUnchangedMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var changes = child.changes();
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
// If not changed prop
if (typeof changes[key] === 'undefined') {
// If new child has same prop
if (typeof endpointProps[key] !== 'undefined') {
// If they do not match
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// Skip this
match = false;
}
}
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find match by changed props (Less Reliable - Doesnt require incoming props to exist)
var findChangedMatch = function findChangedMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var changes = child.changes();
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(props).reduce(function (prev, key) {
// If changed prop
if (typeof changes[key] !== 'undefined') {
// If new child has same prop
if (typeof endpointProps[key] !== 'undefined') {
// If they do not match
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// Skip this
match = false;
}
}
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find exact match by incoming props (Less Reliable - Requires existing props to have all incoming props)
var findExactIncomingMatch = function findExactIncomingMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(endpointProps).reduce(function (prev, key) {
if (typeof props[key] !== 'undefined') {
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// If props are unique
match = false;
}
} else {
// If existing doesnt have all incoming props
match = false;
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
}; // Find match by incoming props (Less Reliable - Doesnt require existing props to have all incoming props)
var findIncomingMatch = function findIncomingMatch(endpoint) {
var exchange = accessor.children.find(function (child) {
if (child.identifier === null || child.identifier.value === null) {
var props = child.props();
var endpointProps = endpoint.props();
var match = true; // Expect match
Object.keys(endpointProps).reduce(function (prev, key) {
if (typeof props[key] !== 'undefined') {
if (JSON.stringify(props[key]) !== JSON.stringify(endpointProps[key])) {
// If props are unique
match = false;
}
}
}, {});
return match;
} else {
return false;
}
});
return exchange;
};
var exchange;
if (endpoint.identifier !== null) {
exchange = accessor.children.find(function (child) {
return typeof child.identifier !== 'undefined' && typeof endpoint.identifier !== 'undefined' && child.identifier !== null && child.identifier.value === endpoint.identifier.value;
});
if (typeof exchange === 'undefined' || exchange === false) {
exchange = smartFind(endpoint);
}
} else {
exchange = smartFind(endpoint);
}
if (typeof exchange !== 'undefined' && !remove) {
// Handle Exchange
return exchange.set(endpoint, false);
} else if (!remove) {
// If no match found but add by force, push to children
if (add) {
accessor.children.push(endpoint);
}
return false;
} else if (typeof exchange !== 'undefined') {
// Handle Remove
var index = accessor.children.indexOf(exchange);
if (index !== -1) {
accessor.children.splice(index, 1);
}
return accessor.children;
} else {
return false;
}
};
/**
* Make Any Request
*/
accessor.shared.makeRequest = function (canceler, method) {
var apiSlug = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : accessor.shared.defaultApi;
var args = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
var data = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
var upload = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
var conf = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
var promise = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : new Promise(function (resolve) {
return resolve();
});
var batch = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : false;
// Custom Request Config (config level 3 - greater is stronger)
conf = Object.assign((0, _util.clone)({}, accessor.shared.config), conf);
if (canceler !== false) {
var cancelHandler = accessor.shared.handleCancellation(cancelers[canceler]);
cancelers[canceler] = cancelHandler.cancellation;
promise = cancelHandler.promise;
}
return new Promise(function (resolve, reject) {
// startLoader(method)
var api = accessor.shared.controller !== null && apiSlug !== null ? accessor.shared.controller.apis[apiSlug] : accessor.shared.api;
accessor.shared.requester[method.toLowerCase()](accessor.shared.resolveUrl(accessor.shared.endpoint, accessor.shared.map, api, args, batch), promise, data, upload, conf).then(function (response) {
accessor.raw = response; // stopLoader(method)
resolve(response);
})["catch"](function (error) {
// stopLoader(method)
reject(accessor.shared.handleError(error));
});
});
};
accessor.shared.identifier = function () {
var identifier = null;
if (accessor.identifier !== null && typeof accessor.identifier !== 'undefined' && accessor.identifier.key !== null) {
if (!accessor.reserved(accessor.identifier.key)) {
identifier = accessor[accessor.identifier.key];
} else {
identifier = accessor.invalids[accessor.identifier.key];
}
}
return identifier;
};
/**
* Public / Reserved Method Names
* @warning Can not be used as a property name in models
* ---------------
* Query builder (Create arguments, and make endpoints default fetch method available afterwards)
*/
accessor.query = function () {
return new _query["default"](accessor);
};
/**
* Request Fetch @note - Related to Properties
*/
accessor.fetch = function () {
var apiSlug = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.defaultApi;
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.args.fetch;
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var perform = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
return new Promise(function (resolve, reject) {
var loadSlug = 'fetch';
startLoader(loadSlug);
accessor.shared.makeRequest(loadSlug, 'GET', apiSlug, args, null, false, {
perform: perform
}).then(function (response) {
accessor.shared.handleSuccess(response, replace).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
});
};
/**
* Request Save @note - Saves all changed Properties
* @apiSlug Use custom api by slug
* @args Custom arguments as object (key: value)
* @replace replace all properties in endpoint from response
* @create Attempt to create if save fails (Ex.: if no id provided to endpoint)
*/
accessor.save = function () {
var apiSlug = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.defaultApi;
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.args.save;
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var create = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var perform = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var map = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : accessor.shared.map;
if (map !== null && typeof map !== 'undefined' && typeof map.multiple !== 'undefined' && map.multiple || accessor.shared.config.multiple) {
return accessor.batch({
create: create
}, apiSlug, args, replace, map);
} else {
return new Promise(function (resolve, reject) {
var loadSlug = 'save';
startLoader(loadSlug);
var identifier = accessor.shared.identifier();
if (identifier !== null && identifier.value === null) {
accessor.create(apiSlug, args, replace, true, perform).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
} else {
accessor.shared.makeRequest(loadSlug, 'PUT', apiSlug, args, accessor.removeIdentifiers(accessor.reverseMapping(accessor.changes(false, false, true))), false, {
perform: perform
}).then(function (response) {
accessor.shared.handleSuccess(response, replace).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
})["catch"](function (error) {
// If could not save, try create
if (create) {
accessor.create(apiSlug, args, replace, true, perform).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
} else {
stopLoader(loadSlug);
reject(error);
}
});
}
});
}
};
/**
* Request Create @note - Saves all Properties
*/
accessor.create = function () {
var apiSlug = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.defaultApi;
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.args.create;
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var save = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var perform = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var map = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : accessor.shared.map;
if (map !== null && typeof map !== 'undefined' && typeof map.multiple !== 'undefined' && map.multiple || accessor.shared.config.multiple) {
return accessor.batch({
save: save
}, apiSlug, args, replace, perform, map);
} else {
return new Promise(function (resolve, reject) {
var withEmpty = accessor.removeIdentifiers(accessor.reverseMapping());
var data = {};
if (!accessor.shared.config.post.keepNull) {
Object.keys(withEmpty).reduce(function (prev, key) {
if (withEmpty[key] !== null) {
data[key] = withEmpty[key];
}
}, {});
} else {
data = withEmpty;
}
var loadSlug = 'create';
startLoader(loadSlug);
accessor.shared.makeRequest(loadSlug, 'POST', apiSlug, args, data, false, {
perform: perform
}).then(function (response) {
accessor.shared.handleSuccess(response, replace).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
});
}
};
/**
* Request Remove @note - Related to Properties
*/
accessor.remove = function () {
var apiSlug = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : accessor.shared.defaultApi;
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.args.remove;
var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var perform = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var map = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : accessor.shared.map;
if (map !== null && typeof map !== 'undefined' && typeof map.multiple !== 'undefined' && map.multiple || accessor.shared.config.multiple) {
return accessor.batch({
save: false,
create: false,
"delete": true
}, apiSlug, args, replace, map);
} else {
return new Promise(function (resolve, reject) {
var loadSlug = 'remove';
startLoader(loadSlug);
accessor.shared.makeRequest(loadSlug, 'DELETE', apiSlug, args, null, false, {
perform: perform
}).then(function (response) {
if (typeof response !== 'undefined') {
accessor.shared.handleSuccess(response, replace).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
} else {
resolve(accessor);
}
})["catch"](function (error) {
stopLoader(loadSlug);
if (error.response && error.response.status === 410) {
// Already deleted (Gone)
resolve(accessor);
} else {
reject(error);
}
});
});
}
};
/**
* Request Upload @note - Related to Properties
* @note: batch upload not yet supported
*/
accessor.upload = function (file) {
var apiSlug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.shared.defaultApi;
var args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : accessor.args.upload;
var replace = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var perform = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var method = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'POST';
var map = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
return new Promise(function (resolve, reject) {
var loadSlug = 'upload';
startLoader(loadSlug);
accessor.shared.makeRequest(loadSlug, method, apiSlug, args, file, true, {
perform: perform
}).then(function (response) {
if (map) {
accessor.shared.handleSuccess(response, replace).then(function () {
stopLoader(loadSlug);
resolve(accessor);
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
} else {
stopLoader(loadSlug);
resolve(response);
}
})["catch"](function (error) {
stopLoader(loadSlug);
reject(error);
});
});
};
/**
* Request Batch @note - Updates all children
*/
accessor.batch = function () {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var apiSlug = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : accessor.shared.defaultApi;
var args = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : accessor.args.batch;
var replace = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
var perform = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var map = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : accessor.shared.map;
options = Object.assign({
create: true,
save: true,
"delete": false,
merge: false,
// Merge with parent props if any (usually there is none)
limit: 100,
// Split requests into limited amount of children
from: 0 // Exclude children before index from request
}, options);
var data = {}; // Handle create
if (options.create) {
var hook = map !== null && typeof map !== 'undefined' && typeof map.batch !== 'undefined' && typeof map.batch.create !== 'undefined' ? map.batch.create : 'create';
data[hook] = [];
for (var i = 0, l = accessor.children.length; i < l; i++) {
var child = accessor.children[i];
if (i >= options.from && i < options.from + options.limit) {
// Create Creation Identifier
if (child.identifier === null || child.identifier.value === null) {
(function () {
if (child.shared.map !== null && typeof child.shared.map !== 'undefined' && typeof child.shared.map.creationIdentifier !== 'undefined' && child.shared.map.creationIdentifier !== '') {
var identifier = child.shared.map.creationIdentifier;
var prop = identifier.split('=')[0];
var val = identifier.substring(prop.length + 1); // Resolve mapping
if (typeof child.shared.map.props !== 'undefined' && typeof child.shared.map.props[prop] !== 'undefined') {
prop = child.shared.map.props[prop];
} // Check if property exist and make reference to prop
if (!child.reserved(prop) && typeof child[prop] !== 'undefined') {
prop = child[prop];
} else if (child.reserved(prop) && typeof child.invalids[prop] !== 'undefined') {
prop = child.invalids[prop];
} else {
// Create prop if not exist
if (!child.reserved(prop)) {
prop = child[prop] = new _prop["default"](child, prop);
} else {
prop = child.invalids[prop] = new _prop["default"](child, prop);
}
} // Generate creation identifier
var id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 15);
child.shared.creationIdentifier = id;
if (val.length > 0) {
val = JSON.parse(val.replace('identifier', id));
if (prop.value === null) {
prop.value = val;
} else {
if (prop.value.constructor ===