@openapi-qraft/react
Version:
OpenAPI client for React, providing type-safe requests and dynamic TanStack Query React Hooks via a modular, Proxy-based architecture.
150 lines (136 loc) • 4.3 kB
text/typescript
import type {
OperationSchema,
RequestFn,
RequestFnInfo,
RequestFnOptions,
RequestFnResponse,
} from './lib/requestFn.js';
import { shelfMerge } from './lib/shelfMerge.js';
export type QraftPredefinedParameterValue<TValue> =
| TValue
| (() => Promise<TValue | undefined>)
| undefined;
export type InputPredefinedParametersItem<TValue> = {
requestPattern: string;
parameters: Array<{
in: 'header' | 'query' | 'cookie';
name: string;
value: QraftPredefinedParameterValue<TValue>;
}>;
};
type TargetPredefinedParametersMethods =
| 'get'
| 'post'
| 'put'
| 'patch'
| 'delete'
| 'options'
| 'head'
| 'trace';
export type TargetPredefinedParametersItem = {
requestPattern: string;
methods:
| Array<TargetPredefinedParametersMethods>
| ReadonlyArray<Readonly<TargetPredefinedParametersMethods>>;
paths: Array<string> | ReadonlyArray<Readonly<string>>;
};
export function qraftPredefinedParametersRequestFn<
TInputPredefinedParametersItem extends InputPredefinedParametersItem<any>,
TData,
TError,
>(
inputPredefinedParameters:
| Array<TInputPredefinedParametersItem>
| ReadonlyArray<Readonly<TInputPredefinedParametersItem>>,
targetPredefinedParameters:
| Array<TargetPredefinedParametersItem>
| ReadonlyArray<Readonly<TargetPredefinedParametersItem>>,
requestFn: RequestFn<TData, TError>
): RequestFn<TData, TError> {
return function predefinedParametersRequestFn(
schema: OperationSchema,
requestInfo: RequestFnInfo,
options?: RequestFnOptions
): Promise<RequestFnResponse<TData, TError>> {
const matchingInputPredefinedParameters =
targetPredefinedParameters.flatMap((targetParameter) =>
targetParameter.paths.includes(schema.url) &&
(!targetParameter.methods.length ||
targetParameter.methods.includes(schema.method))
? inputPredefinedParameters.filter(
(predefinedParameter) =>
predefinedParameter.requestPattern ===
targetParameter.requestPattern
)
: []
);
if (!matchingInputPredefinedParameters?.length)
return requestFn(schema, requestInfo, options);
return Promise.all(
matchingInputPredefinedParameters.map((item) =>
resolveQraftPredefinedParameters(item.parameters)
)
).then((resolvedPredefinedParameters) => {
const mergedParameters = resolvedPredefinedParameters.reduce(
(acc, item) => {
acc = shelfMerge(2, acc, item);
return acc;
},
{}
);
return requestFn(
schema,
{
...requestInfo,
parameters: shelfMerge(2, mergedParameters, requestInfo.parameters),
},
options
);
});
};
}
async function resolveQraftPredefinedParameters<T>(
predefinedParameters: InputPredefinedParametersItem<T>['parameters']
) {
return Promise.all(
predefinedParameters.map(async (predefinedParameter) => {
return {
...predefinedParameter,
value: await resolveQraftPredefinedParameterValue(
predefinedParameter.value
),
};
})
).then((resolvedPredefinedParameters) =>
resolvedPredefinedParameters
.filter(
(resolvedPredefinedParameter) =>
resolvedPredefinedParameter.value !== undefined
)
.reduce(
(acc, resolvedPredefinedParameter) => {
acc[resolvedPredefinedParameter.in] =
acc[resolvedPredefinedParameter.in] ?? {};
acc[resolvedPredefinedParameter.in][
resolvedPredefinedParameter.name
] = resolvedPredefinedParameter.value;
return acc;
},
{} as Record<string, Record<string, unknown>>
)
);
}
type QraftPredefinedParameterResolvedValue<TData> = TData extends () => Promise<
infer TAsyncData
>
? TAsyncData
: TData;
async function resolveQraftPredefinedParameterValue<TData>(
value: QraftPredefinedParameterValue<TData>
): Promise<QraftPredefinedParameterResolvedValue<TData>> {
if (value && typeof value === 'function')
return (await (
value as () => Promise<TData | undefined>
)()) as QraftPredefinedParameterResolvedValue<TData>;
return value as QraftPredefinedParameterResolvedValue<TData>;
}