@nestia/fetcher
Version:
Fetcher library of Nestia SDK
177 lines (167 loc) • 6.33 kB
text/typescript
import { AesPkcs5 } from "./AesPkcs5";
import { IConnection } from "./IConnection";
import { IEncryptionPassword } from "./IEncryptionPassword";
import { IFetchRoute } from "./IFetchRoute";
import { IPropagation } from "./IPropagation";
import { FetcherBase } from "./internal/FetcherBase";
/**
* Utility class for `fetch` functions used in `@nestia/sdk` with encryption.
*
* `EncryptedFetcher` is a utility class designed for SDK functions generated by
* [`@nestia/sdk`](https://nestia.io/docs/sdk/sdk), interacting with the remote
* HTTP API encrypted by AES-PKCS algorithm. In other words, this is a
* collection of dedicated `fetch()` functions for `@nestia/sdk` with
* encryption.
*
* For reference, `EncryptedFetcher` class being used only when target
* controller method is encrypting body data by `@EncryptedRoute` or
* `@EncryptedBody` decorators. If those decorators are not used,
* {@link PlainFetcher} class would be used instead.
*
* @author Jeongho Nam - https://github.com/samchon
*/
export namespace EncryptedFetcher {
/**
* Fetch function only for `HEAD` method.
*
* @param connection Connection information for the remote HTTP server
* @param route Route information about the target API
* @returns Nothing because of `HEAD` method
*/
export function fetch(
connection: IConnection,
route: IFetchRoute<"HEAD">,
): Promise<void>;
/**
* Fetch function only for `GET` method.
*
* @param connection Connection information for the remote HTTP server
* @param route Route information about the target API
* @returns Response body data from the remote API
*/
export function fetch<Output>(
connection: IConnection,
route: IFetchRoute<"GET">,
): Promise<Output>;
/**
* Fetch function for the `POST`, `PUT`, `PATCH` and `DELETE` methods.
*
* @param connection Connection information for the remote HTTP server
* @param route Route information about the target API
* @returns Response body data from the remote API
*/
export function fetch<Input, Output>(
connection: IConnection,
route: IFetchRoute<"POST" | "PUT" | "PATCH" | "DELETE">,
input?: Input,
stringify?: (input: Input) => string,
): Promise<Output>;
export async function fetch<Input, Output>(
connection: IConnection,
route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
input?: Input,
stringify?: (input: Input) => string,
): Promise<Output> {
if (
(route.request?.encrypted === true || route.response?.encrypted) &&
connection.encryption === undefined
)
throw new Error(
"Error on EncryptedFetcher.fetch(): the encryption password has not been configured.",
);
const closure =
typeof connection.encryption === "function"
? (direction: "encode" | "decode") =>
(
headers: Record<string, IConnection.HeaderValue | undefined>,
body: string,
) =>
(connection.encryption as IEncryptionPassword.Closure)({
headers,
body,
direction,
})
: () => () => connection.encryption as IEncryptionPassword;
return FetcherBase.request({
className: "EncryptedFetcher",
encode:
route.request?.encrypted === true
? (input, headers) => {
const p: IEncryptionPassword = closure("encode")(headers, input);
return AesPkcs5.encrypt(
(stringify ?? JSON.stringify)(input),
p.key,
p.iv,
);
}
: (input) => input,
decode:
route.response?.encrypted === true
? (input, headers) => {
const p: IEncryptionPassword = closure("decode")(headers, input);
const s: string = AesPkcs5.decrypt(input, p.key, p.iv);
return s.length ? JSON.parse(s) : s;
}
: (input) => input,
})(connection, route, input, stringify);
}
export function propagate<Output extends IPropagation<any, any>>(
connection: IConnection,
route: IFetchRoute<"GET" | "HEAD">,
): Promise<Output>;
export function propagate<Input, Output extends IPropagation<any, any>>(
connection: IConnection,
route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
input?: Input,
stringify?: (input: Input) => string,
): Promise<Output>;
export async function propagate<Input, Output extends IPropagation<any, any>>(
connection: IConnection,
route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
input?: Input,
stringify?: (input: Input) => string,
): Promise<Output> {
if (
(route.request?.encrypted === true || route.response?.encrypted) &&
connection.encryption === undefined
)
throw new Error(
"Error on EncryptedFetcher.propagate(): the encryption password has not been configured.",
);
const closure =
typeof connection.encryption === "function"
? (direction: "encode" | "decode") =>
(
headers: Record<string, IConnection.HeaderValue | undefined>,
body: string,
) =>
(connection.encryption as IEncryptionPassword.Closure)({
headers,
body,
direction,
})
: () => () => connection.encryption as IEncryptionPassword;
return FetcherBase.propagate({
className: "EncryptedFetcher",
encode:
route.request?.encrypted === true
? (input, headers) => {
const p: IEncryptionPassword = closure("encode")(headers, input);
return AesPkcs5.encrypt(
(stringify ?? JSON.stringify)(input),
p.key,
p.iv,
);
}
: (input) => input,
decode:
route.response?.encrypted === true
? (input, headers) => {
const p: IEncryptionPassword = closure("decode")(headers, input);
const s: string = AesPkcs5.decrypt(input, p.key, p.iv);
return s.length ? JSON.parse(s) : s;
}
: (input) => input,
})(connection, route, input, stringify) as Promise<Output>;
}
}