UNPKG

@aep_dev/aep-lib-ts

Version:

Utility libraries for AEP TypeScript-based tools including case conversion, OpenAPI utilities, and API clients

185 lines (160 loc) 5.23 kB
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; import { Resource } from "../api/types.js"; type RequestLoggingFunction = (ctx: any, req: any, ...args: any[]) => void; type ResponseLoggingFunction = (ctx: any, resp: any, ...args: any[]) => void; export class Client { private headers: Record<string, string>; private client: AxiosInstance; private requestLoggingFunction: RequestLoggingFunction; private responseLoggingFunction: ResponseLoggingFunction; constructor(client: AxiosInstance, headers: Record<string, string>, requestLoggingFunction: RequestLoggingFunction, responseLoggingFunction: ResponseLoggingFunction) { this.client = client; this.headers = headers; this.requestLoggingFunction = requestLoggingFunction; this.responseLoggingFunction = responseLoggingFunction; } async create( ctx: any, resource: Resource, serverUrl: string, body: Record<string, any>, parameters: Record<string, string> ): Promise<Record<string, any>> { let suffix = ""; if (resource.createMethod?.supportsUserSettableCreate) { const id = body.id; if (!id) { throw new Error(`id field not found in ${JSON.stringify(body)}`); } if (typeof id === "string") { suffix = `?id=${id}`; } } const url = this.basePath(ctx, resource, serverUrl, parameters, suffix); const response = await this.makeRequest(ctx, "POST", url, body); return response; } async list( ctx: any, resource: Resource, serverUrl: string, parameters: Record<string, string> ): Promise<Record<string, any>[]> { const url = this.basePath(ctx, resource, serverUrl, parameters, ""); const response = await this.makeRequest(ctx, "GET", url); const kebab = this.kebabToCamelCase(resource.plural); const lowerKebab = kebab.length > 1 ? kebab.charAt(0).toLowerCase() + kebab.slice(1) : ""; const possibleKeys = ["results", resource.plural, kebab, lowerKebab]; for (const key of possibleKeys) { if (response[key] && Array.isArray(response[key])) { return response[key].filter((item: any) => typeof item === "object"); } } throw new Error("No valid list key was found"); } async get( ctx: any, serverUrl: string, path: string ): Promise<Record<string, any>> { const url = `${serverUrl}/${path.replace(/^\//, "")}`; return this.makeRequest(ctx, "GET", url); } async getWithFullUrl( ctx: any, url: string ): Promise<Record<string, any>> { return this.makeRequest(ctx, "GET", url); } async delete(ctx: any, serverUrl: string, path: string): Promise<void> { const url = `${serverUrl}/${path.replace(/^\//, "")}`; await this.makeRequest(ctx, "DELETE", url); } async update( ctx: any, serverUrl: string, path: string, body: Record<string, any> ): Promise<Record<string, any>> { const url = `${serverUrl}/${path.replace(/^\//, "")}`; return this.makeRequest(ctx, "PATCH", url, body); } private async makeRequest( ctx: any, method: string, url: string, body?: Record<string, any> ): Promise<Record<string, any>> { if (body) { Object.keys(body).forEach(key => { if (body[key] === null) { delete body[key]; } }); } const config: AxiosRequestConfig = { method, url, headers: this.headers, data: body, }; this.requestLoggingFunction(ctx, config); try { const response = await this.client.request(config); this.responseLoggingFunction(ctx, response); const data = response.data; this.checkErrors(data); return data; } catch (error: any) { if (error.response) { this.responseLoggingFunction(ctx, error.response); throw new Error( `Request failed: ${JSON.stringify(error.response.data)} for request ${JSON.stringify(config)}` ); } throw error; } } private checkErrors(response: Record<string, any>): void { if (response.error) { throw new Error(`Returned errors: ${JSON.stringify(response.error)}`); } } private basePath( ctx: any, resource: Resource, serverUrl: string, parameters: Record<string, string>, suffix: string ): string { serverUrl = serverUrl.replace(/\/$/, ""); const urlElems = [serverUrl]; for (let i = 0; i < resource.patternElems.length - 1; i++) { const elem = resource.patternElems[i]; if (i % 2 === 0) { urlElems.push(elem); } else { const paramName = elem.slice(1, -1); const value = parameters[paramName]; if (!value) { throw new Error( `Parameter ${paramName} not found in parameters ${JSON.stringify( parameters )}` ); } const lastValue = value.split("/").pop() || value; urlElems.push(lastValue); } } let result = urlElems.join("/"); if (suffix) { result += suffix; } return result; } private kebabToCamelCase(str: string): string { return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); } }