@shopify/shopify-api
Version:
Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks
257 lines (253 loc) • 9 kB
JavaScript
'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