@skylinedynamics/sd-angular-jsonapi
Version:
Skyline Dynamics Angular 5+, fluent Implementation for JSONAPI conform requests
1,689 lines (1,680 loc) • 53.1 kB
JavaScript
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