UNPKG

@shopify/shopify-api

Version:

Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks

257 lines (253 loc) 9 kB
'use strict'; var error = require('../lib/error.js'); var types = require('../lib/clients/types.js'); class Base { static Client; static config; static apiVersion; static resourceNames = []; static primaryKey = 'id'; static customPrefix = null; static readOnlyAttributes = []; static hasOne = {}; static hasMany = {}; static paths = []; static setClassProperties({ Client, config }) { this.Client = Client; this.config = config; } static async baseFind({ session, urlIds, params, requireIds = false, }) { if (requireIds) { const hasIds = Object.entries(urlIds).some(([_key, value]) => value); if (!hasIds) { throw new error.RestResourceError('No IDs given for request, cannot find path'); } } const response = await this.request({ http_method: 'get', operation: 'get', session, urlIds, params, }); return { data: this.createInstancesFromResponse(session, response.body), headers: response.headers, pageInfo: response.pageInfo, }; } static async request({ session, http_method, operation, urlIds, params, body, entity, }) { const client = new this.Client({ session, apiVersion: this.apiVersion, }); const path = this.getPath({ http_method, operation, urlIds, entity }); const cleanParams = {}; if (params) { for (const key in params) { if (params[key] !== null) { cleanParams[key] = params[key]; } } } switch (http_method) { case 'get': return client.get({ path, query: cleanParams }); case 'post': return client.post({ path, query: cleanParams, data: body, type: types.DataType.JSON, }); case 'put': return client.put({ path, query: cleanParams, data: body, type: types.DataType.JSON, }); case 'delete': return client.delete({ path, query: cleanParams }); default: throw new Error(`Unrecognized HTTP method "${http_method}"`); } } static getJsonBodyName() { return this.name.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase(); } static getPath({ http_method, operation, urlIds, entity, }) { let match = null; let specificity = -1; const potentialPaths = []; this.paths.forEach((path) => { if (http_method !== path.http_method || operation !== path.operation || path.ids.length <= specificity) { return; } potentialPaths.push(path); let pathUrlIds = { ...urlIds }; path.ids.forEach((id) => { if (!pathUrlIds[id] && entity && entity[id]) { pathUrlIds[id] = entity[id]; } }); pathUrlIds = Object.entries(pathUrlIds).reduce((acc, [key, value]) => { if (value) { acc[key] = value; } return acc; }, {}); // If we weren't given all of the path's required ids, we can't use it const diff = path.ids.reduce((acc, id) => (pathUrlIds[id] ? acc : acc.concat(id)), []); if (diff.length > 0) { return; } specificity = path.ids.length; match = path.path.replace(/(<([^>]+)>)/g, (_m1, _m2, id) => `${pathUrlIds[id]}`); }); if (!match) { const pathOptions = potentialPaths.map((path) => path.path); throw new error.RestResourceError(`Could not find a path for request. If you are trying to make a request to one of the following paths, ensure all relevant IDs are set. :\n - ${pathOptions.join('\n - ')}`); } if (this.customPrefix) { return `${this.customPrefix}/${match}`; } else { return match; } } static createInstancesFromResponse(session, data) { let instances = []; this.resourceNames.forEach((resourceName) => { const singular = resourceName.singular; const plural = resourceName.plural; if (data[plural] || Array.isArray(data[singular])) { instances = instances.concat((data[plural] || data[singular]).reduce((acc, entry) => acc.concat(this.createInstance(session, entry)), [])); } else if (data[singular]) { instances.push(this.createInstance(session, data[singular])); } }); return instances; } static createInstance(session, data, prevInstance) { const instance = prevInstance ? prevInstance : new this({ session }); if (data) { instance.setData(data); } return instance; } #session; get session() { return this.#session; } constructor({ session, fromData }) { this.#session = session; if (fromData) { this.setData(fromData); } } async save({ update = false } = {}) { const { primaryKey, resourceNames } = this.resource(); const method = this[primaryKey] ? 'put' : 'post'; const data = this.serialize(true); const response = await this.resource().request({ http_method: method, operation: method, session: this.session, urlIds: {}, body: { [this.resource().getJsonBodyName()]: data }, entity: this, }); const flattenResourceNames = resourceNames.reduce((acc, obj) => { return acc.concat(Object.values(obj)); }, []); const matchResourceName = Object.keys(response.body).filter((key) => flattenResourceNames.includes(key)); const body = response.body[matchResourceName[0]]; if (update && body) { this.setData(body); } } async saveAndUpdate() { await this.save({ update: true }); } async delete() { await this.resource().request({ http_method: 'delete', operation: 'delete', session: this.session, urlIds: {}, entity: this, }); } serialize(saving = false) { const { hasMany, hasOne, readOnlyAttributes } = this.resource(); return Object.entries(this).reduce((acc, [attribute, value]) => { if (['#session'].includes(attribute) || (saving && readOnlyAttributes.includes(attribute))) { return acc; } if (attribute in hasMany && value) { acc[attribute] = value.reduce((attrAcc, entry) => { return attrAcc.concat(this.serializeSubAttribute(entry, saving)); }, []); } else if (attribute in hasOne && value) { acc[attribute] = this.serializeSubAttribute(value, saving); } else { acc[attribute] = value; } return acc; }, {}); } toJSON() { return this.serialize(); } request(args) { return this.resource().request(args); } setData(data) { const { hasMany, hasOne } = this.resource(); Object.entries(data).forEach(([attribute, val]) => { if (attribute in hasMany) { const HasManyResource = hasMany[attribute]; this[attribute] = []; val.forEach((entry) => { const obj = new HasManyResource({ session: this.session }); if (entry) { obj.setData(entry); } this[attribute].push(obj); }); } else if (attribute in hasOne) { const HasOneResource = hasOne[attribute]; const obj = new HasOneResource({ session: this.session }); if (val) { obj.setData(val); } this[attribute] = obj; } else { this[attribute] = val; } }); } resource() { return this.constructor; } serializeSubAttribute(attribute, saving) { return attribute.serialize ? attribute.serialize(saving) : this.resource() .createInstance(this.session, attribute) .serialize(saving); } } exports.Base = Base; //# sourceMappingURL=base.js.map