import { RequestPromiseOptions } from "request-promise";
import request from "request-promise";
import { UriOptions } from "request";
import { Log } from "@uk/log";
import { PosterException } from "./errors";
export interface QContext
{
method?: "GET" | "POST";
apiMethod?: string;
body?: TBody;
query?: TQuery;
}
export type Query = RequestPromiseOptions & UriOptions;
enum LogLevel {
INFO = 1,
DEBUG,
}
export interface PosterOptions {
logLevel?: LogLevel;
}
export abstract class BaseApiRoute {
protected static readonly urlPrefix = "https://joinposter.com/api";
static route: string;
log: Log;
constructor(protected readonly token: string, protected options?: PosterOptions) {
this.log = new Log(`POSTER/${this.constructor.name.toUpperCase()}`);
}
protected formateDate(date?: Date) {
if (!date) return undefined;
const rv = date.toLocaleDateString().split(".").reverse().join("");
return rv as any;
}
protected async queryRunner(ctx: QContext): Promise {
const query: Query = {
uri: `${BaseApiRoute.urlPrefix}/${ctx.apiMethod}`,
method: ctx.method,
qs: {
token: this.token,
...(ctx.query || {}),
},
simple: true,
json: true,
};
(ctx)["posterQuery"] = query;
if (ctx.method === "POST" && ctx.body) {
const data = {} as any;
for (const n in ctx.body) {
const val = ctx.body[n];
if (val !== null && val !== undefined) data[n] = val;
}
query["body"] = data;
}
const body = await request(query);
if (body.error) {
const { code, message } = body.error;
throw new PosterException(code, message);
}
const response = body.response;
if (response === false) {
throw new PosterException(404, `Not found entity for this query.`);
}
if (response.err_code) {
const msg = `Got error at ${ctx.apiMethod}. Response body: ${JSON.stringify(body.response)}`;
throw new PosterException(response.err_code, msg);
}
if (response.err_code === 0) {
return null as any;
}
return body.response as R;
}
}
export function ApiMethod() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const defaultMethod = target[propertyKey];
let route = target.constructor.name.toLowerCase();
if (route === "incomingorders") route = "incomingOrders";
const meta = Reflect.getMetadata(propertyKey, target);
descriptor.value = function (...args: any[]) {
const thiz = this as BaseApiRoute;
const options = thiz["options"] as PosterOptions;
const debug = options?.logLevel === LogLevel.DEBUG;
const info = options?.logLevel === LogLevel.INFO;
const msg = `Call '${propertyKey}' method`;
const logInfo: any = {};
const ctx: QContext = {
apiMethod: `${route}.${propertyKey}`,
method: propertyKey.includes("get") ? "GET" : "POST",
};
if (meta) {
const { bodyIndex, ctxIndex } = meta;
if (bodyIndex !== undefined) {
ctx.body = args[bodyIndex];
}
args[ctxIndex || 0] = ctx;
}
return defaultMethod
.call(this, ...args)
.then((rv: any) => {
logInfo.rv = rv;
return rv;
})
.catch((err: PosterException) => {
logInfo.err = err;
throw err;
})
.finally(() => {
if (info) {
thiz.log.info(msg);
}
if (debug) {
const { posterQuery, ...context } = ctx as any;
const cleanArgs = args.slice(0, args.length - 1);
logInfo.args = cleanArgs;
logInfo.ctx = context;
posterQuery.qs.token = "POSTER_API_TOKEN (HIDDEN)";
logInfo.posterQuery = posterQuery;
if (logInfo.err) thiz.log.error(msg, logInfo);
else thiz.log.debug(msg, logInfo);
}
});
};
};
}
export function Context() {
return function (target: any, propertyKey: string | symbol, ctxIndex: number) {
let metadata = Reflect.getMetadata(propertyKey, target);
if (!metadata) {
Reflect.defineMetadata(propertyKey, { ctxIndex }, target);
} else {
metadata.ctxIndex = ctxIndex;
}
};
}
export function CBody() {
return function (target: any, propertyKey: string | symbol, bodyIndex: number) {
let metadata = Reflect.getMetadata(propertyKey, target);
if (!metadata) {
Reflect.defineMetadata(propertyKey, { bodyIndex }, target);
} else {
metadata.bodyIndex = bodyIndex;
}
};
}