UNPKG

@maicol07/coloquent

Version:

Library for retrieving model objects from a JSON-API, with a fluent syntax inspired by Laravel Eloquent.

389 lines 13.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Model = void 0; const Builder_1 = require("./Builder"); const Map_1 = require("./util/Map"); const PaginationStrategy_1 = require("./PaginationStrategy"); const php_date_formatter_1 = __importDefault(require("php-date-formatter")); const SaveResponse_1 = require("./response/SaveResponse"); const ToManyRelation_1 = require("./relation/ToManyRelation"); const ToOneRelation_1 = require("./relation/ToOneRelation"); const Reflection_1 = require("./util/Reflection"); const AxiosHttpClient_1 = require("./httpclient/axios/AxiosHttpClient"); class Model { constructor() { this.relations = new Map_1.Map(); this.attributes = new Map_1.Map(); } /** * Get a {@link Builder} instance from a {@link Model} instance * so you can query without having a static reference to your specific {@link Model} * class. */ query() { return this.getConstructor().query(); } /** * Get a {@link Builder} instance from a static {@link Model} * so you can start querying */ static query() { return new Builder_1.Builder(this); } static get(page) { return new Builder_1.Builder(this) .get(page); } static first() { return new Builder_1.Builder(this) .first(); } static find(id) { return new Builder_1.Builder(this) .find(id); } static with(attribute) { return new Builder_1.Builder(this) .with(attribute); } static limit(limit) { return new Builder_1.Builder(this) .limit(limit); } static where(attribute, value) { return new Builder_1.Builder(this) .where(attribute, value); } static orderBy(attribute, direction) { return new Builder_1.Builder(this) .orderBy(attribute, direction); } static option(queryParameter, value) { return new Builder_1.Builder(this) .option(queryParameter, value); } serialize() { let attributes = {}; for (let key in this.attributes.toArray()) { if (this.getConstructor().readOnlyAttributes.indexOf(key) == -1) { attributes[key] = this.attributes.get(key); } } let relationships = {}; for (let key in this.relations.toArray()) { let relation = this.relations.get(key); if (relation instanceof Model) { relationships[key] = this.serializeToOneRelation(relation); } else if (relation instanceof Array && relation.length > 0) { relationships[key] = this.serializeToManyRelation(relation); } } let payload = { data: { type: this.getConstructor().effectiveJsonApiType, attributes, relationships } }; if (this.hasId) { payload['data']['id'] = this.id; } return payload; } serializeRelatedModel(model) { return { type: this.getConstructorOf(model).effectiveJsonApiType, id: model.id }; } serializeToOneRelation(model) { return { data: this.serializeRelatedModel(model), }; } serializeToManyRelation(models) { return { data: models.map((model) => this.serializeRelatedModel(model)) }; } save() { if (!this.hasId) { return this.create(); } let payload = this.serialize(); return this.getConstructor().effectiveHttpClient .patch(this.getConstructor().getJsonApiUrl() + '/' + this.id, payload) .then((response) => { const idFromJson = response.getData().data.id; this.setApiId(idFromJson); return new SaveResponse_1.SaveResponse(response, this.getConstructor(), response.getData()); }, (response) => { throw response; }); } create() { let payload = this.serialize(); return this.getConstructor().effectiveHttpClient .post(this.getConstructor().getJsonApiUrl(), payload) .then((response) => { const idFromJson = response.getData().data.id; this.setApiId(idFromJson); return new SaveResponse_1.SaveResponse(response, this.getConstructor(), response.getData()); }, function (response) { throw response; }); } delete() { if (!this.hasId) { throw new Error('Cannot delete a model with no ID.'); } return this.getConstructor().effectiveHttpClient .delete(this.getConstructor().getJsonApiUrl() + '/' + this.id) .then(function () { }); } /** * @return A {@link Promise} resolving to: * * * the representation of this {@link Model} instance in the API if this {@link Model} has an ID and this ID can * be found in the API too * * `undefined` if this {@link Model} instance has no ID * * `null` if there _is_ an ID, but a {@link Model} with this ID cannot be found in the backend */ fresh() { let model = (new (this.getConstructor())); let builder = model .query() .with(this.getRelationsKeys()); if (this.getApiId()) { return builder .find(this.getApiId()) .then((response) => { let model = response.getData(); return model; }, (response) => { throw response; }); } else { return Promise.resolve(undefined); } } getRelations() { return this.relations.toArray(); } getRelationsKeys(parentRelationName) { let relationNames = []; for (let key in this.relations.toArray()) { let relation = this.getRelation(key); if (parentRelationName) { relationNames.push(parentRelationName + '.' + key); } else { relationNames.push(key); } if (Array.isArray(relation)) { relation.forEach((model) => { relationNames = [...relationNames, ...model.getRelationsKeys(key)]; }); } else if (relation) { relationNames = [...relationNames, ...relation.getRelationsKeys(key)]; } } return relationNames; } /** * The base URL that is used to call the API */ static get effectiveJsonApiBaseUrl() { if (this._effectiveJsonApiBaseUrl === undefined) { if (this.jsonApiBaseUrl === undefined) { throw new Error(`Expected ${this.name} to have static property 'jsonApiBaseUrl' defined`); } this._effectiveJsonApiBaseUrl = this.jsonApiBaseUrl.replace(/\/+$/, ''); } return this._effectiveJsonApiBaseUrl; } static get effectiveJsonApiType() { if (this._effectiveJsonApiType === undefined) { if (this.jsonApiType === undefined) { throw new Error(`Expected ${this.name} to have property expect jsonApiType defined`); } this._effectiveJsonApiType = this.jsonApiType; } return this._effectiveJsonApiType; } static get effectiveEndpoint() { var _a; return ((_a = this.endpoint) !== null && _a !== void 0 ? _a : this.effectiveJsonApiType).replace(/^\/+/, ''); } static getJsonApiUrl() { return `${this.effectiveJsonApiBaseUrl}/${this.effectiveEndpoint}`; } /** * The {@link HttpClient} that is used by Coloquent. Is equal to {@link httpClient} * property unless that one was left undefined, in which case it is an instance * of {@link AxiosHttpClient}. This is a read-only property. */ static get effectiveHttpClient() { var _a; if (this._effectiveHttpClient === undefined) { this._effectiveHttpClient = (_a = this.httpClient) !== null && _a !== void 0 ? _a : new AxiosHttpClient_1.AxiosHttpClient(); } return this._effectiveHttpClient; } /** * @deprecated Use the static method with the same name instead */ getJsonApiType() { return this.getConstructor().effectiveJsonApiType; } /** * @deprecated Use the static property {@link jsonApiBaseUrl} or * {@link effectiveJsonApiBaseUrl} */ getJsonApiBaseUrl() { return this.getConstructor().effectiveJsonApiBaseUrl; } /** * @deprecated Use the static {@link httpClient} to get the one that is * configured, and {@link effectiveHttpClient} to get the one that is */ getHttpClient() { return this.getConstructor().effectiveHttpClient; } populateFromResource(resource) { this.id = resource.id; for (let key in resource.attributes) { this.setAttribute(key, resource.attributes[key]); } } /** * @deprecated Access the static {@link pageSize} property directly */ static getPageSize() { return this.pageSize; } static getPaginationStrategy() { return this.paginationStrategy; } static getPaginationPageNumberParamName() { return this.paginationPageNumberParamName; } static getPaginationPageSizeParamName() { return this.paginationPageSizeParamName; } static getPaginationOffsetParamName() { return this.paginationOffsetParamName; } static getPaginationLimitParamName() { return this.paginationLimitParName; } getRelation(relationName) { return this.relations.get(relationName); } setRelation(relationName, value) { this.relations.set(relationName, value); } getAttributes() { return this.attributes.toArray(); } getAttribute(attributeName) { if (this.isDateAttribute(attributeName)) { return this.getAttributeAsDate(attributeName); } return this.attributes.get(attributeName); } getAttributeAsDate(attributeName) { if (!Date.parse(this.attributes.get(attributeName))) { throw new Error(`Attribute ${attributeName} cannot be cast to type Date`); } return new Date(this.attributes.get(attributeName)); } isDateAttribute(attributeName) { return this.getConstructor().dates.hasOwnProperty(attributeName); } setAttribute(attributeName, value) { if (this.isDateAttribute(attributeName)) { if (!Date.parse(value)) { throw new Error(`${value} cannot be cast to type Date`); } value = Model.getDateFormatter().parseDate(value, this.getConstructor().dates[attributeName]); } this.attributes.set(attributeName, value); } /** * We use a single instance of DateFormatter, which is stored as a static property on Model, to minimize the number * of times we need to instantiate the DateFormatter class. By using this getter a DateFormatter is instantiated * only when it is used at least once. * * @returns DateFormatter */ static getDateFormatter() { if (!this.dateFormatter) { this.dateFormatter = new php_date_formatter_1.default(); } return this.dateFormatter; } getApiId() { return this.id; } setApiId(id) { this.id = id; } hasMany(relatedType, relationName) { if (typeof relationName === 'undefined') { relationName = Reflection_1.Reflection.getNameOfNthMethodOffStackTrace(new Error(), 2); } return new ToManyRelation_1.ToManyRelation(relatedType, this, relationName); } hasOne(relatedType, relationName) { if (typeof relationName === 'undefined') { relationName = Reflection_1.Reflection.getNameOfNthMethodOffStackTrace(new Error(), 2); } return new ToOneRelation_1.ToOneRelation(relatedType, this, relationName); } get hasId() { return this.id !== undefined && this.id !== null && this.id !== ''; } getConstructor() { return this.getConstructorOf(this); } getConstructorOf(model) { return Object.getPrototypeOf(model).constructor; } } exports.Model = Model; /** * The page size */ Model.pageSize = 50; /** * The pagination strategy */ Model.paginationStrategy = PaginationStrategy_1.PaginationStrategy.OffsetBased; /** * The number query parameter name. By default: 'page[number]' */ Model.paginationPageNumberParamName = 'page[number]'; /** * The size query parameter name. By default: 'page[size]' */ Model.paginationPageSizeParamName = 'page[size]'; /** * The offset query parameter name. By default: 'page[offset]' */ Model.paginationOffsetParamName = 'page[offset]'; /** * The limit query parameter name. By default: 'page[limit]' */ Model.paginationLimitParName = 'page[limit]'; Model.readOnlyAttributes = []; Model.dates = {}; //# sourceMappingURL=Model.js.map