@tolinax/ayoune
Version:
aYOUne - Business as a Service
268 lines (233 loc) • 10.1 kB
text/typescript
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);
}
}