75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import type { ApiOperation } from "../../contracts/api-operations.js";
|
|
|
|
export type OperationRequestInput = Readonly<{
|
|
operationId: string;
|
|
routeId: string;
|
|
pathParams?: Readonly<Record<string, string | number>>;
|
|
searchParams?: unknown;
|
|
body?: unknown;
|
|
signal?: AbortSignal;
|
|
idempotencyKey?: string;
|
|
correlationId?: string;
|
|
}>;
|
|
|
|
export type RequestTargetResult =
|
|
| Readonly<{ success: true; url: URL }>
|
|
| Readonly<{
|
|
success: false;
|
|
code: "PATH_PARAMETER_MISSING" | "SEARCH_PARAMETER_INVALID";
|
|
}>;
|
|
|
|
const pathParameterPattern = /:([A-Za-z][A-Za-z0-9_]*)|\{([A-Za-z][A-Za-z0-9_]*)\}/g;
|
|
|
|
export function buildRequestTarget(
|
|
baseUrl: string,
|
|
operation: ApiOperation,
|
|
pathParams: Readonly<Record<string, string | number>> = {},
|
|
parsedSearch: unknown = {},
|
|
): RequestTargetResult {
|
|
let missingPathParameter = false;
|
|
const pathname = operation.path.replace(
|
|
pathParameterPattern,
|
|
(_token, colonName: string | undefined, braceName: string | undefined) => {
|
|
const name = colonName ?? braceName ?? "";
|
|
const value = pathParams[name];
|
|
if (value === undefined) {
|
|
missingPathParameter = true;
|
|
return "";
|
|
}
|
|
return encodeURIComponent(String(value));
|
|
},
|
|
);
|
|
if (missingPathParameter) {
|
|
return { success: false, code: "PATH_PARAMETER_MISSING" };
|
|
}
|
|
|
|
if (
|
|
parsedSearch === null ||
|
|
typeof parsedSearch !== "object" ||
|
|
Array.isArray(parsedSearch)
|
|
) {
|
|
return { success: false, code: "SEARCH_PARAMETER_INVALID" };
|
|
}
|
|
|
|
const url = new URL(pathname, baseUrl);
|
|
const search = parsedSearch as Readonly<Record<string, unknown>>;
|
|
for (const key of Object.keys(search).sort((left, right) =>
|
|
left.localeCompare(right),
|
|
)) {
|
|
const value = search[key];
|
|
if (value === undefined || value === null) continue;
|
|
const values = Array.isArray(value) ? value : [value];
|
|
for (const item of values) {
|
|
if (
|
|
typeof item !== "string" &&
|
|
typeof item !== "number" &&
|
|
typeof item !== "boolean"
|
|
) {
|
|
return { success: false, code: "SEARCH_PARAMETER_INVALID" };
|
|
}
|
|
url.searchParams.append(key, String(item));
|
|
}
|
|
}
|
|
return { success: true, url };
|
|
}
|