Merge mintaka lib

This commit is contained in:
2024-03-06 19:39:31 -03:00
parent b4d074420a
commit c6136b6e21
15 changed files with 747 additions and 23 deletions

199
src/http/client.ts Normal file
View File

@ -0,0 +1,199 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
import axios, { AxiosInstance } from "axios";
import { JsonCasing, JsonSerializer } from "../json";
import { HttpHeaders } from "./headers";
export interface ErrorResponse {
error: string;
};
export interface MessageResponse {
message: string;
};
export interface HttpClientOptions {
baseUrl?: string | null;
headers?: HttpHeaders;
logEnabled?: boolean;
logTarget?: string;
jsonRequestCasing?: JsonCasing | null;
jsonResponseCasing?: JsonCasing | null;
};
/**
* Http Client
*
* Helper class to make HTTP requests.
*/
export class HttpClient {
private readonly inner: AxiosInstance;
protected readonly options: HttpClientOptions;
constructor(options?: HttpClientOptions | null) {
const defaultOptions = HttpClient.defaultOptions();
this.options = {
baseUrl: options?.baseUrl ?? defaultOptions.baseUrl,
headers: options?.headers ?? defaultOptions.headers,
logEnabled: options?.logEnabled ?? defaultOptions.logEnabled,
logTarget: options?.logTarget ?? defaultOptions.logTarget,
jsonRequestCasing: options?.jsonRequestCasing ?? defaultOptions.jsonRequestCasing,
jsonResponseCasing: options?.jsonResponseCasing ?? defaultOptions.jsonResponseCasing,
};
const inner = axios.create({
baseURL: this.options?.baseUrl ?? undefined,
headers: this.options?.headers?.getRaw(),
});
inner.interceptors.request.use((req) => {
if (this.options.logEnabled ?? false) {
console.log(`(${this.options.logTarget}) ${req.method?.toUpperCase()} ${req.baseURL}${req.url}`);
}
if (req.url && req.url.includes("oauth2") && !req.url.includes("userinfo")) {
return req;
}
const contentType = req.headers["Content-Type"];
if (req.data && (contentType === "application/json" || contentType === "application/x-www-form-urlencoded")) {
req.data = JsonSerializer.toCase(req.data, this.options.jsonRequestCasing);
}
return req;
});
inner.interceptors.response.use((res) => {
if (res.config.url && res.config.url.includes("oauth2") && !res.config.url.includes("userinfo")) {
return res;
}
if (res.data) {
res.data = JsonSerializer.toCase(res.data, this.options.jsonResponseCasing);
}
return res;
});
this.inner = inner;
}
private static defaultOptions(): HttpClientOptions {
return {
baseUrl: null,
headers: new HttpHeaders(),
logEnabled: false,
logTarget: "HttpClient",
jsonRequestCasing: null,
jsonResponseCasing: null,
};
};
/**
* Get a resource from the http server.
* @returns {T | null}
*/
public async get<T>(path: string, headers?: HttpHeaders | null): Promise<T | null> {
try {
const requestHeaders = headers ?? this.options.headers;
const response = await this.inner.get(path, { headers: requestHeaders?.getRaw() });
if (response.status !== 200) {
return null;
}
return response.data as T;
} catch (error) {
return null;
}
}
/**
* Create a resource on the http server.
* @param path
* @param body
* @param headers
* @returns {T | null}
*/
public async post<S, T>(path: string, body: S, headers?: HttpHeaders | null): Promise<T | null> {
try {
const requestHeaders = headers ?? this.options.headers;
const response = await this.inner.post(path, body, { headers: requestHeaders?.getRaw() });
if (response.status !== 200) {
return null;
}
return response.data as T;
} catch (error) {
return null;
}
}
/**
* Update a resource on the http server.
* @param path
* @param body
* @param headers
* @returns {T | null}
*/
public async put<S, T>(path: string, body: S, headers?: HttpHeaders | null): Promise<T | null> {
try {
const requestHeaders = headers ?? this.options.headers;
const response = await this.inner.put(path, body, { headers: requestHeaders?.getRaw() });
if (response.status !== 200) {
return null;
}
return response.data as T;
} catch (error) {
return null;
}
}
/**
* Patch a resource on the http server.
* @param path
* @param body
* @param headers
* @returns {T | null}
*/
public async patch<S, T>(path: string, body: S, headers?: HttpHeaders | null): Promise<T | null> {
try {
const requestHeaders = headers ?? this.options.headers;
const response = await this.inner.patch(path, body, { headers: requestHeaders?.getRaw() });
if (response.status !== 200) {
return null;
}
return response.data as T;
} catch (error) {
return null;
}
}
/**
* Delete a resource from the http server.
* @param path
* @param headers
* @returns {T | null}
*/
public async delete<T>(path: string, headers?: HttpHeaders | null): Promise<T | null> {
try {
const requestHeaders = headers ?? this.options.headers;
const response = await this.inner.delete(path, { headers: requestHeaders?.getRaw() });
if (response.status !== 200) {
return null;
}
return response.data as T;
} catch (error) {
return null;
}
}
}

19
src/http/cookies.ts Normal file
View File

@ -0,0 +1,19 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
import { RawStringMap, StringMap } from "../collections/string_map";
/**
* Http Cookie Map
*
* Helper type to represent HTTP cookies.
*/
export type HttpCookieMap = RawStringMap<string>;
/**
* Http Cookies
*
* Helper class to manage HTTP cookies.
*/
export class HttpCookies extends StringMap<string> {
}

28
src/http/headers.ts Normal file
View File

@ -0,0 +1,28 @@
// Copyright (c) Tribufu. All Rights Reserved.
// SPDX-License-Identifier: MIT
import { RawStringMap, StringMap } from "../collections/string_map";
/**
* Http Header Map
*
* Helper type to represent HTTP headers.
*/
export type HttpHeaderMap = RawStringMap<string>;
/**
* Http Headers
*
* Helper class to manage HTTP headers.
*/
export class HttpHeaders extends StringMap<string> {
public static parseAuthorizationHeader(value: string): { scheme: string, token: string } {
const parts = value.split(" ");
if (parts.length !== 2) {
throw new Error("Invalid authorization header");
}
return { scheme: parts[0], token: parts[1] };
}
}