cdek-ts-api
Version:
CDEK API client
116 lines (97 loc) • 3.46 kB
text/typescript
import { ApiError } from "../errors/api";
import { AuthError } from "../errors/auth";
import { HttpError } from "../errors/http";
import axios from 'axios';
import type { ApiResponse } from "../types/api";
import type { InitOptions, RequestInit, RequestMethod } from "../types/lib";
export class RestClient {
private _token?: ApiResponse.OAuth;
private _token_expire?: number;
private account: string;
private password: string;
private grant_type: string;
private url_base: string;
private on_error?: (error: Error | ApiError | AuthError | HttpError) => void | Promise<void>;
constructor(options: InitOptions) {
this.account = options.account;
this.password = options.password;
this.grant_type = options.grant_type ?? "client_credentials";
this.url_base = options.url_base ?? "https://api.cdek.ru/v2";
this.on_error = options.on_error;
}
get token() {
return this._token;
}
get token_expire() {
return this._token_expire;
}
async auth(): Promise<void> {
try {
const res = await axios.post<any>(`${this.url_base}/oauth/token`, null, {
params: {
grant_type: 'client_credentials',
client_id: this.account,
client_secret: this.password,
},
headers: {
'Accept': 'application/json',
'X-App-Name': 'shildikOnline',
},
});
if (res.status !== 200) {
throw new AuthError('Произошла ошибка');
}
this._token = res.data;
this._token_expire = Date.now() + (this.token?.expires_in ?? 3600) * 1000;
} catch (err: any) {
throw new AuthError('Authentication failed');
}
}
private async request<T>(init: RequestInit & { method: RequestMethod }): Promise<{ data: T; headers: any }> {
try {
if (!this.token || this.token_expire === undefined || Date.now() > this.token_expire) {
await this.auth();
}
const url = `${this.url_base}${init.url}${init.query ? "?" + this.params(init.query) : ""}`;
const headers = {
"Authorization": `Bearer ${this.token?.access_token}`,
"Content-Type": "application/json",
};
const res = await axios({
url,
method: init.method,
headers,
params: init.query,
data: init.payload ? JSON.stringify(init.payload) : undefined,
});
return { data: res.data as T, headers: res.headers };
} catch (err: any) {
if (this.on_error) {
await this.on_error(err);
return { data: null as T, headers: {} };
} else {
throw err;
}
}
}
get<T>(init: RequestInit): Promise<{ data: T; headers: any }> {
return this.request<T>({ ...init, method: "GET" });
}
post<T>(init: RequestInit): Promise<{ data: T; headers: any }> {
return this.request<T>({ ...init, method: "POST" });
}
put<T>(init: RequestInit): Promise<{ data: T; headers: any }> {
return this.request<T>({ ...init, method: "PUT" });
}
patch<T>(init: RequestInit): Promise<{ data: T; headers: any }> {
return this.request<T>({ ...init, method: "PATCH" });
}
delete<T>(init: RequestInit): Promise<{ data: T; headers: any }> {
return this.request<T>({ ...init, method: "DELETE" });
}
private params(query: Record<string, any>): URLSearchParams {
return new URLSearchParams(
Object.entries(query).map<string[]>((item) => [item[0], item[1].toString()])
);
}
}