UNPKG

@skylinedynamics/sd-angular-jsonapi

Version:

Skyline Dynamics Angular 5+, fluent Implementation for JSONAPI conform requests

1,689 lines (1,680 loc) 53.1 kB
import { Injectable, EventEmitter, NgModule } from '@angular/core'; import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http'; import * as pluralize_ from 'pluralize'; import pluralize___default, { } from 'pluralize'; import { find, each, filter, findLast, findIndex, sortBy, every, includes, chunk, nth, slice, tail, take, takeRight, indexOf, clone, map } from 'lodash'; import { Observable } from 'rxjs/Observable'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const removeSlashes = (value) => { return value.replace(/\//gmi, ''); }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class JSONAPIError extends Error { /** * @param {?=} message */ constructor(message) { super(message); Object.setPrototypeOf(this, new.target.prototype); } } /** * @record */ class JSONAPIErrorResponse { /** * @param {?} error */ constructor(error) { for (const /** @type {?} */ attribute in error) { this[attribute] = error[attribute]; } } } class JSONAPIErrorBag { /** * @param {?} errors */ constructor(errors) { this.errors = []; for (const /** @type {?} */ error of errors) { this.errors.push(new JSONAPIErrorResponse(error)); } } /** * @return {?} */ list() { return this.errors; } } /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const JSON_API_ERROR_MESSAGES = { PAGINATE_AND_LIMIT_CONFLICT: 'Cannot paginate and limit at the same time', NO_PAGINATION_STRATEGY_SET: 'No Pagination Strategy has been set', NO_PAGINATION_OPTIONS_SET: 'No Pagination Options have been set', CANNOT_CREATE_FROM_EXISTING: 'Cannot create new resource from existing. Use update instead', CANNOT_DELETE_WITHOUT_ID: 'Cannot delete resource without ID', CANNOT_UPDATE_WITHOUT_ID: 'Cannot update resource without ID', NO_SELF_LINK: 'Cannot find self URL', ALREADY_INITIALIZED: 'Tried to initialize but already is initialized', NO_ENTITY_MANAGER: 'Tried to initialize resource but has no connection to EntityManager', UNEXPECTED_FILTERS_TYPE: 'Expected Filters to be of type Array, found ', UNEXPECTED_RELATIONSHIPS_TYPE: 'Expected Relationships to be of type Array, found ', UNEXPECTED_FIELDS_FILTERS_TYPE: 'Expected Fields to be of type Array, found ', UNEXPECTED_SORT_FILTERS_TYPE: 'Expected Sorters to be of type Array, found ', NOT_A_RESOURCE: 'Not a resource: ', NOT_AN_INSTANCE_OF: 'Cannot add element to collection as it is not an instance of ', NO_INDEX_OR_ELEMENT: 'Must provide either an element or index to remove', NO_BASE_URL_SET: 'No BaseURL set, ignoring request', INVALID_URL_APPENDIX: 'Invalid URL Appendix provided', RESOURCE_NOT_REGISTERED: 'Resource not found or not registered', CANNOT_SORT_ON_UNDEFINED_ATTRIBUTE: 'Cannot sort on undefined attributes', NOT_A_VALID_RELATIONSHIP: 'Not a valid relationship: ' }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const pluralize = pluralize_; class SDAngularJsonAPIRequestService { /** * @param {?} http */ constructor(http) { this.http = http; this.config = { baseUrl: '', version: '', paginationStrategy: null, paginationOptions: null }; this.resources = []; } /** * @param {?} config * @return {?} */ configure(config) { Object.assign(this.config, config); return this; } /** * @param {?} url * @return {?} */ setBaseUrl(url) { this.config.baseUrl = url; return this; } /** * @param {?} version * @return {?} */ setVersion(version) { this.config.version = version; return this; } /** * @param {?} strategy * @return {?} */ setPaginationStrategy(strategy) { this.config.paginationStrategy = strategy; return this; } /** * @return {?} */ paginationStrategySet() { return !!this.config.paginationStrategy; } /** * @return {?} */ getPaginationStrategy() { return this.config.paginationStrategy; } /** * @param {?} options * @return {?} */ setPaginationOptions(options) { this.config.paginationOptions = options; return this; } /** * @return {?} */ paginationOptionsSet() { return !!this.config.paginationOptions; } /** * @return {?} */ getPaginationOptions() { return this.config.paginationOptions; } /** * @param {?} name * @param {?} instance * @return {?} */ registerResource(name, instance) { name = pluralize(name).toLowerCase(); if (!find(this.resources, { type: name })) { this.resources.push({ type: name, instance: instance }); } return this; } /** * @param {?} name * @return {?} */ getResourceInstance(name) { // TODO add proper type return return find(this.resources, { type: name }); } /** * @param {?} resource * @param {?=} data * @param {?=} appendToUrl * @param {?=} requestType * @return {?} */ send(resource, data = {}, appendToUrl = [], requestType = 'get') { if (!this.config.baseUrl || this.config.baseUrl.length <= 0) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_BASE_URL_SET); } return new Observable((observer) => { this.http.request(requestType, this.constructUrl(resource, appendToUrl), { body: data }).subscribe((success) => { observer.next(success); }, (errorResponse) => { if (errorResponse.error && errorResponse.error.errors) { observer.error(new JSONAPIErrorBag(errorResponse.error.errors)); } else { observer.error(errorResponse); } }); }); } /** * @param {?} url * @param {?=} data * @param {?=} requestType * @return {?} */ sendWithUrl(url, data = {}, requestType = 'get') { return new Observable((observer) => { this.http.request(requestType, url, { body: data }).subscribe((success) => { observer.next(success); }, (errorResponse) => { if (errorResponse.error && errorResponse.error.errors) { observer.error(new JSONAPIErrorBag(errorResponse.error.errors)); } else { observer.error(errorResponse); } }); }); } /** * @param {?} resource * @param {?=} appendToUrl * @return {?} */ constructUrl(resource, appendToUrl = []) { let /** @type {?} */ baseUrl = this.config.baseUrl; let /** @type {?} */ url = ''; if (baseUrl.charAt(baseUrl.length - 1) === '/') { baseUrl = baseUrl.slice(0, baseUrl.length - 1); } if (this.config.version && this.config.version.length > 0) { const /** @type {?} */ version = removeSlashes(this.config.version); url = `${baseUrl}/${version}/${resource}`; } else { url = `${baseUrl}/${resource}`; } if (appendToUrl && appendToUrl.length > 0) { if (typeof appendToUrl === 'string') { if (appendToUrl.charAt(0) === '?') { url += removeSlashes(appendToUrl); } else { url += '/' + removeSlashes(appendToUrl); } } else if (Array.isArray(appendToUrl)) { url += '/' + each(appendToUrl, (el) => { return removeSlashes(el); }).join('/'); url = url.replace(/\/\?/gmi, '?'); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.INVALID_URL_APPENDIX); } } return url; } } SDAngularJsonAPIRequestService.decorators = [ { type: Injectable }, ]; /** @nocollapse */ SDAngularJsonAPIRequestService.ctorParameters = () => [ { type: HttpClient, }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class Collection { /** * @param {?} data * @param {?} included * @param {?} type * @param {?} manager */ constructor(data, included, type, manager) { this.elements = []; this._copy = null; this._links = null; this.setManager(manager); this.setIncluded(included); this.setType(type); this.setLinks(data.links); data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } if (data && Array.isArray(data) && data.length > 0) { this.resource = data[0].type; } this.setData(data); } /** * @param {?} manager * @return {?} */ setManager(manager) { this._manager = manager; } /** * @param {?} included * @return {?} */ setIncluded(included) { this._included = included; } /** * @param {?} type * @return {?} */ setType(type) { this.type = type; } /** * @param {?} links * @return {?} */ setLinks(links) { this._links = links ? links : null; } /** * @param {?} data * @return {?} */ setData(data) { this.original = data; for (const /** @type {?} */ element of data) { let /** @type {?} */ className = element.type.charAt(0).toUpperCase() + element.type.slice(1); className = pluralize___default.singular(className); const /** @type {?} */ elementInstance = new (/** @type {?} */ (this.type)); elementInstance.manager = this._manager; elementInstance.initialize(); elementInstance.setData(Object.assign(element.attributes, { id: element.id })); elementInstance.attributes = Object.keys(element.attributes); elementInstance._isLoaded = true; elementInstance.resource = element.type; elementInstance._links = element.links; for (const /** @type {?} */ name in element.relationships) { if (element.relationships.hasOwnProperty(name)) { elementInstance.addRelationship(name, element.relationships[name].links, element.relationships[name].data); if (element.relationships[name].data) { elementInstance.hasLoadedRelationship(name); const /** @type {?} */ elementData = filter(this._included, { type: name }); each(elementData, (elData) => { elementInstance.associateRelationshipData(name, elData); }); } } } this.elements.push(elementInstance); } this._copy = this.elements; } /** * @return {?} */ zero() { this.elements = []; this._copy = []; return this; } /** * @return {?} */ reset() { this.elements = this._copy; return this; } /** * @return {?} */ nextPage() { if (this.hasNext()) { this._manager.request.sendWithUrl(this._links.next).subscribe((data) => { this.setLinks(data.links); data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } this.zero().setData(data); }); } } /** * @return {?} */ hasNext() { return !!(this._links && this._links.next); } /** * @return {?} */ previousPage() { if (this.hasPrevious()) { this._manager.request.sendWithUrl(this._links.prev).subscribe((data) => { this.setLinks(data.links); data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } this.zero().setData(data); }); } } /** * @return {?} */ hasPrevious() { return !!(this._links && this._links.prev); } /** * @return {?} */ firstPage() { if (this.hasFirstLink()) { this._manager.request.sendWithUrl(this._links.first).subscribe((data) => { this.setLinks(data.links); data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } this.zero().setData(data); }); } } /** * @return {?} */ hasFirstLink() { return !!(this._links && this._links.first); } /** * @return {?} */ lastPage() { if (this.hasLastLink()) { this._manager.request.sendWithUrl(this._links.last).subscribe((data) => { this.setLinks(data.links); data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } this.zero().setData(data); }); } } /** * @return {?} */ hasLastLink() { return !!(this._links && this._links.last); } /** * @return {?} */ refresh() { if (this.hasSelfLink()) { this._manager.request.sendWithUrl(this._links.self).subscribe((data) => { data = data.data ? data.data : []; if (!Array.isArray(data)) { data = [data]; } this.zero().setData(data); }); } } /** * @return {?} */ reload() { this.refresh(); } /** * @return {?} */ hasSelfLink() { return !!(this._links.self); } /** * @param {?} index * @return {?} */ delete(index) { if (Array.isArray(index)) { return new Observable((observer) => { for (const /** @type {?} */ i of index) { this._deleteElementAt(i).subscribe((success) => observer.next(success), (error) => observer.next(error)); } }); } else { return this._deleteElementAt(index); } } /** * @param {?} index * @return {?} */ _deleteElementAt(index) { const /** @type {?} */ resource = this.elements[index]; if (resource) { return resource.delete(); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NOT_A_RESOURCE + resource); } } /** * @param {?} id * @return {?} */ deleteById(id) { id = id.toString(); const /** @type {?} */ resource = find(this.elements, { id }); if (resource) { return resource.delete(); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NOT_A_RESOURCE + resource); } } /** * @return {?} */ deleteAll() { return new Observable((observer) => { for (const /** @type {?} */ resource of this.elements) { resource.delete().subscribe((success) => { observer.next(success); }, (error) => { observer.error(error); }); } }); } /** * @param {?=} asOriginal * @return {?} */ all(asOriginal = false) { if (asOriginal) { return this._copy; } return this.elements; } /** * @param {?=} asOriginal * @return {?} */ get(asOriginal = false) { if (asOriginal) { return this._copy; } return this.elements; } /** * @return {?} */ first() { if (this.elements.length > 0) { return this.elements[0]; } else { return {}; } } /** * @return {?} */ last() { if (this.elements.length > 0) { return this.elements[this.elements.length - 1]; } else { return {}; } } /** * @param {?} predicate * @return {?} */ filter(predicate) { this.elements = filter(this.elements, predicate); return this; } /** * @param {?} predicate * @return {?} */ find(predicate) { this.elements = find(this.elements, predicate); return this; } /** * @param {?} predicate * @param {?=} index * @return {?} */ findLast(predicate, index = this.elements.length - 1) { this.elements = findLast(this.elements, index, predicate); return this; } /** * @param {?} predicate * @param {?=} fromIndex * @return {?} */ findIndex(predicate, fromIndex = 0) { return findIndex(this.elements, predicate, fromIndex); } /** * @param {?} iteratees * @return {?} */ sortBy(iteratees) { this.elements = sortBy(this.elements, iteratees); return this; } /** * @param {?} callback * @return {?} */ each(callback) { this.elements = each(this.elements, callback); return this; } /** * @param {?} callback * @return {?} */ forEach(callback) { this.elements = each(this.elements, callback); return this; } /** * @param {?} predicate * @return {?} */ every(predicate) { return every(this.elements, predicate); } /** * @param {?} value * @param {?=} fromIndex * @return {?} */ includes(value, fromIndex = 0) { return includes(this.elements, value, fromIndex); } /** * @param {?=} size * @return {?} */ chunk(size = 1) { this.elements = chunk(this.elements, size); return this; } /** * @param {?} value * @param {?=} fromIndex * @return {?} */ indexOf(value, fromIndex = 0) { return indexOf(this.elements, value, fromIndex); } /** * @param {?=} index * @return {?} */ nth(index = 0) { return nth(this.elements, index); } /** * @param {?=} index * @return {?} */ at(index = 0) { return nth(this.elements, index); } /** * @param {?=} index * @return {?} */ atIndex(index = 0) { return nth(this.elements, index); } /** * @param {?=} start * @param {?=} end * @return {?} */ slice(start = 0, end = this.elements.length) { this.elements = slice(this.elements, start, end); return this; } /** * @param {?} element * @return {?} */ add(element) { if (!(element instanceof this.type)) { throw new Error(JSON_API_ERROR_MESSAGES.NOT_AN_INSTANCE_OF + this.type.name); } else { this.elements.push(element); this._copy.push(element); } return this; } /** * @param {?=} element * @param {?=} index * @return {?} */ remove(element, index) { if (!element && !index) { throw new Error(JSON_API_ERROR_MESSAGES.NO_INDEX_OR_ELEMENT); } const /** @type {?} */ copyIndex = this._copy.indexOf(element); if (copyIndex >= 0) { this._copy.splice(copyIndex, 1); } const /** @type {?} */ elementIndex = this.elements.indexOf(element); if (elementIndex >= 0) { this.elements.splice(elementIndex, 1); } return this; } /** * @return {?} */ tail() { this.elements = tail(this.elements); return this; } /** * @param {?=} amount * @return {?} */ take(amount = 1) { this.elements = take(this.elements, amount); return this; } /** * @param {?=} amount * @return {?} */ takeRight(amount = 1) { this.elements = takeRight(this.elements, amount); return this; } } /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class EntityManager { /** * @param {?} request */ constructor(request) { this.request = request; this.entities = []; } /** * @param {?} config * @return {?} */ configure(config) { if (config) { this.request.configure(config); } return this; } /** * @param {?} url * @return {?} */ setBaseUrl(url) { this.request.setBaseUrl(url); return this; } /** * @param {?} url * @return {?} */ baseUrl(url) { this.request.setBaseUrl(url); return this; } /** * @param {?} version * @return {?} */ setVersion(version) { this.request.setVersion(version); return this; } /** * @param {?} version * @return {?} */ version(version) { this.request.setVersion(version); return this; } /** * @param {?} strategy * @return {?} */ setPaginationStrategy(strategy) { this.request.setPaginationStrategy(strategy); return this; } /** * @param {?} strategy * @return {?} */ paginationStrategy(strategy) { this.request.setPaginationStrategy(strategy); return this; } /** * @return {?} */ getPaginationStrategy() { return this.request.getPaginationStrategy(); } /** * @return {?} */ paginationStrategySet() { return this.request.paginationStrategySet(); } /** * @param {?} options * @return {?} */ setPaginationOptions(options) { this.request.setPaginationOptions(options); return this; } /** * @param {?} options * @return {?} */ paginationOptions(options) { this.request.setPaginationOptions(options); return this; } /** * @return {?} */ getPaginationOptions() { return this.request.getPaginationOptions(); } /** * @return {?} */ paginationOptionsSet() { return this.request.paginationOptionsSet(); } /** * @template R * @param {?} resourceType * @param {?=} config * @return {?} */ createResource(resourceType, config) { if (config) { this.configure(config); } const /** @type {?} */ resource = new resourceType(); resource.manager = this; resource.initialize(); this.entities.push(resource); return resource; } /** * @param {?} index * @return {?} */ refreshByIndex(index) { const /** @type {?} */ resource = this.entities[index]; if (resource) { resource.refresh(); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.RESOURCE_NOT_REGISTERED); } } /** * @param {?} index * @return {?} */ reloadByIndex(index) { this.refreshByIndex(index); } /** * @param {?} id * @return {?} */ refreshById(id) { const /** @type {?} */ resource = find(this.entities, { id }); if (resource) { resource.refresh(); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.RESOURCE_NOT_REGISTERED); } } /** * @param {?} id * @return {?} */ reloadById(id) { this.refreshById(id); } } EntityManager.decorators = [ { type: Injectable }, ]; /** @nocollapse */ EntityManager.ctorParameters = () => [ { type: SDAngularJsonAPIRequestService, }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const pluralize$1 = pluralize_; /** * @param {?=} res * @return {?} */ function JSONAPI(res) { return (target) => { const /** @type {?} */ original = target; /** * @param {?} constructor * @param {?} args * @return {?} */ function construct(constructor, args) { const /** @type {?} */ c = function () { return constructor.apply(this, args); }; c.prototype = constructor.prototype; return new c(); } if (!res) { res = pluralize$1(original.name.toLowerCase()); } const /** @type {?} */ f = function (...args) { if (args && Array.isArray(args) && args.length > 0) { args = [Object.assign(args[0], { resource: res }), args.splice(0, 1)]; } else { args = [{ resource: res }]; } return construct(original, args); }; f.prototype = original.prototype; f.prototype.resource = res; return f; }; } /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * @abstract */ class JSONAPIResource { /** * @param {?=} data * @param {?=} manager */ constructor(data, manager) { this._limit = null; this._paginate = { // TODO should be typed currentPage: 0, size: 1 }; this._doPaginate = false; this._related = []; this._filters = []; this._sortBy = []; this._fields = []; this._isLoaded = false; this._isNew = false; this._hasUpdate = false; this._forInitialization = null; this._initialized = false; this._loadRelationships = []; this._loadedRelationships = []; this._liveRelationships = new EventEmitter(); this._links = null; this.manager = null; this.resource = null; this.id = null; this.attributes = []; this.onUpdated = new EventEmitter(); this.onDeleted = new EventEmitter(); this.onCreated = new EventEmitter(); this.onRefreshed = new EventEmitter(); if (manager) { this.manager = manager; } if (data && this.manager) { this.setData(data); } else if (data && !this.manager) { this._forInitialization = data; } else { } } /** * @return {?} */ initialize() { if (!this._initialized) { if (this._forInitialization && this.manager) { this.setData(this._forInitialization); } else { if (!this.manager) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_ENTITY_MANAGER); } } this._initialized = true; this._forInitialization = null; } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.ALREADY_INITIALIZED); } } /** * @param {?=} data * @return {?} */ setData(data) { if (data) { Object.assign(this, data); } this.manager.request.registerResource(this.constructor.name, this.constructor); this._liveRelationships.subscribe((relationship) => { this[relationship] = () => { const /** @type {?} */ relatedData = find(this._related, { name: relationship }); let /** @type {?} */ resourceInstance = this.manager.request.getResourceInstance(relatedData.name); if (resourceInstance) { if (relatedData) { return new Collection(relatedData, null, resourceInstance.instance, this.manager); } else { return new Collection([], null, resourceInstance.instance, this.manager); } } else { return relatedData; } }; }); } /** * @return {?} */ refresh() { if (this.hasSelfLink() || this.hasRelatedLink()) { const /** @type {?} */ url = this.hasSelfLink() ? this._links.self : this._links.related; this.manager.request.sendWithUrl(url).subscribe((success) => { // TODO should be typed const /** @type {?} */ data = success.data; if (data) { for (const /** @type {?} */ attribute in this.attributes) { if (data.hasOwnProperty(attribute)) { this[attribute] = data[attribute]; } } this.onRefreshed.emit(/** @type {?} */ (this)); } }, (error) => { // TODO error handling this.onRefreshed.emit(/** @type {?} */ (false)); }); } } /** * @return {?} */ hasSelfLink() { return !!(this._links && this._links.self); } /** * @return {?} */ hasRelatedLink() { return !!(this._links && this._links.related); } /** * @return {?} */ reload() { this.refresh(); } /** * @param {?} resource * @param {?} links * @param {?} data * @return {?} */ addRelationship(resource, links, data) { this._related.push(/** @type {?} */ ({ name: resource, links, data })); return this; } /** * @param {?} resources * @return {?} */ addRelationships(resources) { for (const /** @type {?} */ resource of resources) { this.addRelationship(resource.name, resource.links, resource.data); } return this; } /** * @param {?} name * @return {?} */ hasLoadedRelationship(name) { this._loadedRelationships.push(name); return this; } /** * @param {?} name * @param {?} data * @param {?=} callback * @return {?} */ associateRelationshipData(name, data, callback) { const /** @type {?} */ instanceName = this.manager.request.getResourceInstance(name); if (instanceName) { const /** @type {?} */ relationship = find(this._related, (relatedElement) => { return relatedElement.name === name; }); const /** @type {?} */ relationshipData = find(relationship.data, { id: data.id }); if (relationshipData && data && data.attributes) { Object.assign(relationshipData, data.attributes, { instance: new (/** @type {?} */ (instanceName.instance))(relationshipData) }, { attributes: data.attributes }); } this.emitLiveRelationship(name); if (typeof callback === 'function') { callback(this); } } else { this.findRelationshipData(name, data); this.emitLiveRelationship(name); if (typeof callback === 'function') { callback(this); } } } /** * @param {?} name * @param {?} data * @return {?} */ findRelationshipData(name, data) { const /** @type {?} */ relationship = find(this._related, (relatedElement) => { return relatedElement.name === name; }); const /** @type {?} */ relationshipData = find(relationship.data, { id: data.id }); if (relationshipData && data && data.attributes) { Object.assign(relationshipData, data.attributes); return relationshipData; } } /** * @param {?} name * @return {?} */ emitLiveRelationship(name) { this._liveRelationships.emit(name); } /** * @param {?=} fields * @return {?} */ all(fields) { if (fields) { this.only(fields); } return new Observable((observer) => { const /** @type {?} */ url = this.getURLAppendix(); this.manager.request.send(this.resource, {}, url).subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); } else { observer.next(new Collection([], [], this.constructor, this.manager)); } }, (error) => { observer.error(error); }); }); } /** * @param {?=} id * @param {?=} fields * @return {?} */ get(id, fields) { if (!id) { return this.all(fields); } else { if (fields) { this.only(fields); } return new Observable((observer) => { const /** @type {?} */ url = [id.toString(), this.getURLAppendix()]; this.manager.request.send(this.resource, {}, url).subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); } else { observer.next(new Collection([], [], this.constructor, this.manager)); } }, (error) => { observer.error(error); }); }); } } /** * @param {?} id * @return {?} */ find(id) { return new Observable((observer) => { const /** @type {?} */ url = [id.toString(), this.getURLAppendix()]; this.manager.request.send(this.resource, {}, url).subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); } else { observer.next(new Collection([], [], this.constructor, this.manager)); } }, (error) => { observer.error(error); }); }); } /** * @return {?} */ update() { if (this._isLoaded) { if (this.canUpdate()) { const /** @type {?} */ url = [this.id.toString()]; const /** @type {?} */ attributes = {}; for (const /** @type {?} */ a of this.attributes) { if (a !== 'id') { attributes[a] = this[a]; } } const /** @type {?} */ data = { // TODO request should be typed data: { attributes: attributes, type: this.resource, id: this.id } }; return new Observable((observer) => { this.manager.request.send(this.resource, data, url, 'patch').subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); this.onUpdated.emit(true); } else { observer.next(new Collection([], [], this.constructor, this.manager)); } }, (error) => { observer.error(error); this.onUpdated.emit(false); }); }); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.CANNOT_UPDATE_WITHOUT_ID); } } else { const /** @type {?} */ attributes = {}; for (const /** @type {?} */ a of this.attributes) { if (a !== 'id') { attributes[a] = this[a]; } } const /** @type {?} */ data = { // TODO request should be typed data: { attributes: attributes, type: this.resource } }; return new Observable((observer) => { this.manager.request.send(this.resource, data, [], 'post').subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); this.onCreated.emit(true); } else { observer.next(new Collection([], [], this.constructor, this.manager)); this.onCreated.emit(false); } }, (error) => { observer.error(error); }); }); } } /** * @return {?} */ canUpdate() { return !!this.id; } /** * @return {?} */ delete() { if (this.canDelete()) { if (this._links && this._links.self) { return new Observable((observer) => { this.manager.request.sendWithUrl(this._links.self, {}, 'delete').subscribe((success) => { if (success && success.data) { observer.next(new Collection(success, success.included, this.constructor, this.manager)); this.onDeleted.emit(true); } else { observer.next(new Collection([], [], this.constructor, this.manager)); this.onDeleted.emit(false); } }, (error) => { observer.error(error); }); }); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_SELF_LINK); } } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.CANNOT_DELETE_WITHOUT_ID); } } /** * @return {?} */ canDelete() { return !!this.id; } /** * @param {?} data * @param {?=} autoSave * @return {?} */ create(data, autoSave = true) { if (this._isLoaded) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.CANNOT_CREATE_FROM_EXISTING); } if (data) { this.attributes = Object.keys(data); for (const /** @type {?} */ attribute of this.attributes) { this[attribute] = data[attribute]; } } return autoSave ? /** @type {?} */ (this.save()) : /** @type {?} */ (this); } /** * @param {?} data * @return {?} */ new(data) { return this.create(data, false); } /** * @return {?} */ save() { return this.update(); } /** * @param {?} relationships * @return {?} */ load(relationships) { const /** @type {?} */ loadRelationship = (related) => { const /** @type {?} */ relationship = find(this._related, { name: related }); if (relationship && relationship.links && relationship.links.related) { const /** @type {?} */ url = relationship.links.related; this.manager.request.sendWithUrl(url).subscribe((success) => { if (success && success.data && Array.isArray(success.data)) { for (const /** @type {?} */ result of success.data) { this.associateRelationshipData(related, result, (ref) => { ref.onUpdated.emit(true); }); } } else { // checking if the returned result is a one-to-one relationship, // in this case we can deliver the data immediately if (success && success.data && success.data.attributes) { this.associateRelationshipData(related, success.data, (ref) => { ref.onUpdated.emit(true); }); } } }, (error) => { // TODO error handling }); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NOT_A_VALID_RELATIONSHIP + relationships); } }; if (Array.isArray(relationships)) { for (const /** @type {?} */ relationship of relationships) { loadRelationship(relationship); } } else { loadRelationship(relationships); } } /** * @param {?=} pageSize * @param {?=} startAtPage * @return {?} */ paginate(pageSize = 1, startAtPage = 0) { if (this._limit) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.PAGINATE_AND_LIMIT_CONFLICT); } if (this.manager.paginationStrategySet() && this.manager.paginationOptionsSet()) { this._paginate.size = pageSize; this._paginate.currentPage = startAtPage; this._doPaginate = true; return this; } else { if (!this.manager.paginationStrategySet()) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_PAGINATION_STRATEGY_SET); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_PAGINATION_OPTIONS_SET); } } } /** * @param {?=} limit * @return {?} */ limit(limit = 1) { if (this._doPaginate) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.PAGINATE_AND_LIMIT_CONFLICT); } if (this.manager.paginationStrategySet() && this.manager.paginationOptionsSet()) { this._limit = limit; return this; } else { if (!this.manager.paginationStrategySet()) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_PAGINATION_STRATEGY_SET); } else { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.NO_PAGINATION_OPTIONS_SET); } } } /** * @param {?=} amount * @return {?} */ take(amount = 1) { return this.limit(amount); } /** * @param {?} related * @return {?} */ with(related) { if (!Array.isArray(related)) { this._loadRelationships.push(related); } else { this._loadRelationships.push(...related); } return this; } /** * @param {?} fields * @return {?} */ only(fields) { this._fields = fields; return this; } /** * @param {?} fields * @return {?} */ fields(fields) { return this.only(fields); } /** * @param {?} attribute * @param {?} value * @return {?} */ filter(attribute, value) { if (!Array.isArray(this._filters)) { this._filters = []; } this._filters.push({ attribute, value }); return this; } /** * @param {?} filters * @return {?} */ filters(filters) { if (!Array.isArray(this._filters)) { this._filters = []; } for (const /** @type {?} */ filter$$1 of filters) { this._filters.push({ attribute: filter$$1[0], value: filter$$1[1] }); } return this; } /** * @param {?} element * @return {?} */ isSortQuery(element) { return (typeof element === 'object') && element.on && element.order; } /** * @param {?} direction * @param {?} attribute * @return {?} */ sortBy(direction, attribute) { this._sortBy.push({ dir: direction, on: attribute }); return this; } /** * @return {?} */ constructFilters() { if (!Array.isArray(this._filters)) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.UNEXPECTED_FILTERS_TYPE + typeof this._filters); } const /** @type {?} */ filterQuery = []; for (const /** @type {?} */ filter$$1 of this._filters) { filterQuery.push(`filter[${filter$$1.attribute}]=${filter$$1.value}`); } return filterQuery; } /** * @return {?} */ constructRelationships() { if (!Array.isArray(this._loadRelationships)) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.UNEXPECTED_RELATIONSHIPS_TYPE + typeof this._loadRelationships); } if (this._loadRelationships.length > 0) { let /** @type {?} */ relationshipQuery = 'include='; for (const /** @type {?} */ relationship of this._loadRelationships) { relationshipQuery += relationship; } return relationshipQuery; } else { return ''; } } /** * @return {?} */ constructFieldsFilter() { if (!Array.isArray(this._fields)) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.UNEXPECTED_FIELDS_FILTERS_TYPE + typeof this._fields); } if (this._fields.length > 0) { let /** @type {?} */ fieldsFilterQuery = `fields[${this.resource}]`; for (const /** @type {?} */ field of this._fields) { fieldsFilterQuery += `,${field}`; } return fieldsFilterQuery.slice(1); } else { return ''; } } /** * @return {?} */ constructLimitQuery() { if (this._limit) { if (this.manager.getPaginationStrategy() === 'page') { const /** @type {?} */ pageNumberAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageNumberAttribute; const /** @type {?} */ pageSizeAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageSizeAttribute; return `page[${pageNumberAttribute}]=0&page[${pageSizeAttribute}]=${this._limit}`; } else if (this.manager.getPaginationStrategy() === 'offset') { const /** @type {?} */ pageOffsetAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageOffsetAttribute; const /** @type {?} */ pageLimitAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageLimitAttribute; return `page[${pageOffsetAttribute}]=0&page[${pageLimitAttribute}]=${this._limit}`; } else { const /** @type {?} */ pageCursorAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageCursorAttribute; const /** @type {?} */ pageLimitAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageLimitAttribute; return `page[${pageCursorAttribute}]=0&page[${pageLimitAttribute}]=${this._limit}`; } } return ''; } /** * @return {?} */ constructPaginationQuery() { if (this._doPaginate) { if (this.manager.getPaginationStrategy() === 'page') { const /** @type {?} */ pageNumberAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageNumberAttribute; const /** @type {?} */ pageSizeAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageSizeAttribute; return `page[${pageNumberAttribute}]=${this._paginate.currentPage}&page[${pageSizeAttribute}]=${this._paginate.size}`; } else if (this.manager.getPaginationStrategy() === 'offset') { const /** @type {?} */ pageOffsetAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageOffsetAttribute; const /** @type {?} */ pageLimitAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageLimitAttribute; return `page[${pageOffsetAttribute}]=${this._paginate.currentPage}&page[${pageLimitAttribute}]=${this._paginate.size}`; } else { const /** @type {?} */ pageCursorAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageCursorAttribute; const /** @type {?} */ pageLimitAttribute = (/** @type {?} */ (this.manager.getPaginationOptions())).pageLimitAttribute; return `page[${pageCursorAttribute}]=${this._paginate.currentPage}&page[${pageLimitAttribute}]=${this._paginate.size}`; } } return ''; } /** * @return {?} */ constructSortFilter() { if (this._sortBy) { let /** @type {?} */ sortQuery = 'sort='; if (!Array.isArray(this._sortBy)) { throw new JSONAPIError(JSON_API_ERROR_MESSAGES.UNEXPECTED_SORT_FILTERS_TYPE + typeof this._sortBy); } sortQuery += map(e