UNPKG

@tolinax/ayoune

Version:

aYOUne - Business as a Service

268 lines (233 loc) 10.1 kB
import axios from 'axios'; import {loadMetadata} from "./helpers/loadMetadata"; import debug from "debug"; import {groupBy} from "lodash"; import aYOUneAPIClient from "./helpers/axiosClient"; import {aYOUneApiResponseWrapper} from "./helpers/aYOUneAPIResponseWrapper"; import {IAPIResult} from "@tolinax/ayoune-interfaces"; const dbg = debug('ayoune:sdk'); type aYOUneResponseFormats = 'json' | 'xml' | 'yaml' | 'csv' | 'csv-payload' | 'table' type aYOUneActions = 'exec' | 'run' | string interface aYOUneOptions { responseFormat: aYOUneResponseFormats; } interface aYOUneAPIFunctions { [key: string]: (...args: any[]) => Promise<IAPIResult>; } interface IaYOUneQueryHelpers<T> { lean: () => this; yaml: () => this; exec: () => Promise<T>; [key: string]: any; } interface aYOUneModule { list: { [key: string]: (...args: any[]) => Promise<any>; }; get: { [key: string]: (...args: any[]) => Promise<any>; }; getOne: { [key: string]: (...args: any[]) => Promise<any>; }; update: { [key: string]: (...args: any[]) => Promise<any>; }; copy: { [key: string]: (...args: any[]) => Promise<any>; }; updateMany: { [key: string]: (...args: any[]) => Promise<any>; }; delete: { [key: string]: (...args: any[]) => Promise<any>; }; deleteMany: { [key: string]: (...args: any[]) => Promise<any>; }; token: string; fetchByUrl: (url: string) => aYOUneApiResponseWrapper } export interface IaYOUne { fetchByUrl: (url: string) => Promise<aYOUneApiResponseWrapper> // Method-based access get: (resource: string, id?: string) => IaYOUneQueryHelpers<aYOUneApiResponseWrapper> list: (resource: string) => IaYOUneQueryHelpers<aYOUneApiResponseWrapper> create: (resource: string, data: any) => IaYOUneQueryHelpers<aYOUneApiResponseWrapper> update: (resource: string, id: string, data: any) => IaYOUneQueryHelpers<aYOUneApiResponseWrapper> delete: (resource: string, id: string) => IaYOUneQueryHelpers<aYOUneApiResponseWrapper> [key: string]: aYOUneModule | aYOUneActions | string | (() => void) | ((...args: any[]) => Promise<aYOUneApiResponseWrapper>) | any; } export class aYOUne implements IaYOUne { private readonly token: string; private resourceToModuleMap: { [resource: string]: string } = {}; constructor(token: string = process.env.AYOUNE_TOKEN as string) { dbg('Booting aYOUne SDK') if (!token) { throw Error('Token missing. Either provide it in the constructor or provide the ENV variable AYOUNE_TOKEN') } this.token = token this.initializeModules() dbg('aYOUne SDK successfully initialized') } private _queryParams: { [key: string]: string } = {}; [key: string]: aYOUneModule | string | any; get queryParams(): { [p: string]: string } { return this._queryParams; } set queryParams(value: { [p: string]: string }) { this._queryParams = value; } async fetchByUrl(url: string): Promise<aYOUneApiResponseWrapper> { const response = await aYOUneAPIClient.get(url, {headers: {Authorization: `Bearer ${this.token}`}}); return new aYOUneApiResponseWrapper(response.data, this as any); } private initializeModules() { const metadata = loadMetadata(); const modules = groupBy(metadata, 'module'); for (const moduleName of Object.keys(modules)) { dbg(`Building [${moduleName}]`); this[moduleName] = {}; for (const el of modules[moduleName]) { // Store resource to module mapping for method-based access this.resourceToModuleMap[el.plural.toLowerCase()] = el.module; this[moduleName][el.plural.toLowerCase()] = { get: (...args: any) => this.generateMethod( 'get', el.plural.toLowerCase(), el.module )(...args), list: (...args: any) => this.generateMethod( 'list', el.plural.toLowerCase(), el.module )(...args), } } // Attach helpers for (const resource in this[moduleName]) { for (const method in this[moduleName][resource]) { this[moduleName][resource][method] = this.enhanceWithHelpers(this[moduleName][resource][method]); } } } } private enhanceWithHelpers<T>(method: (...args: any[]) => Promise<T>): (...args: any[]) => IaYOUneQueryHelpers<T> { return (...args: any[]): IaYOUneQueryHelpers<T> => { // Reset Query Params for a new request (optional, adjust if needed) this._queryParams = {}; const self = this; // Capture 'this' context to use in closures below return { lean: function () { self._queryParams.hideMeta = 'true'; return this; }, yaml: function () { self._queryParams.responseFormat = 'yaml'; return this; }, csv: function (delimiter: string = ',', emptyFieldValue: string = '') { self._queryParams.responseFormat = 'csv'; self._queryParams.delimiter = delimiter; self._queryParams.emptyFieldValue = emptyFieldValue; return this; }, csvPayload: function (delimiter: string = ',', emptyFieldValue: string = '') { self._queryParams.responseFormat = 'csv'; self._queryParams.delimiter = delimiter; self._queryParams.emptyFieldValue = emptyFieldValue; return this; }, xml: function () { self._queryParams.responseFormat = 'xml'; return this; }, table: function () { self._queryParams.responseFormat = 'table'; return this; }, slice: function (n: number = 10) { self._queryParams.sliceResult = n.toString(); return this; }, verbosity: function (verbosity: 'default' | 'extended' | 'minimal' = "extended") { self._queryParams.verbosity = verbosity; return this; }, debug: function () { self._queryParams.debug = 'true'; return this; }, describe: function () { self._queryParams.describe = 'true'; return this; }, exec: async () => { const result = await method.apply(self, args); return result; } }; }; } private buildUrl(moduleName: string, resource: string, data: any, method: string) { const url = `https://${moduleName}-api.ayoune.app/${resource}${typeof data === 'string' ? '/' + data : ''}${method === 'list' ? '/list' : ''}` dbg(`Requesting [${url}]`); return url; } private generateMethod<T>( method: 'get' | 'list' | 'post' | 'put' | 'delete', resource: string, moduleName: string ) { return async (data?: any, options?: any) => { const url = this.buildUrl(moduleName, resource, data, method); let _method = method === 'list' ? 'get' : method; try { const response = await axios({ method: _method, url, data, headers: {Authorization: `Bearer ${this.token}`}, params: this._queryParams }); return new aYOUneApiResponseWrapper(response.data, this as any); } catch (error) { throw error; } }; } private findModuleForResource(resource: string): string { const module = this.resourceToModuleMap[resource]; if (!module) { throw new Error(`Resource '${resource}' not found. Available resources: ${Object.keys(this.resourceToModuleMap).join(', ')}`); } return module; } // Method-based access implementation get(resource: string, id?: string): IaYOUneQueryHelpers<aYOUneApiResponseWrapper> { const module = this.findModuleForResource(resource); const method = this.generateMethod('get', resource, module); return this.enhanceWithHelpers(method)(id); } list(resource: string): IaYOUneQueryHelpers<aYOUneApiResponseWrapper> { const module = this.findModuleForResource(resource); const method = this.generateMethod('list', resource, module); return this.enhanceWithHelpers(method)(); } create(resource: string, data: any): IaYOUneQueryHelpers<aYOUneApiResponseWrapper> { const module = this.findModuleForResource(resource); const method = this.generateMethod('post', resource, module); return this.enhanceWithHelpers(method)(data); } update(resource: string, id: string, data: any): IaYOUneQueryHelpers<aYOUneApiResponseWrapper> { const module = this.findModuleForResource(resource); const method = this.generateMethod('put', resource, module); return this.enhanceWithHelpers(method)({ ...data, id }); } delete(resource: string, id: string): IaYOUneQueryHelpers<aYOUneApiResponseWrapper> { const module = this.findModuleForResource(resource); const method = this.generateMethod('delete', resource, module); return this.enhanceWithHelpers(method)(id); } }