Skip to content

Commit

Permalink
feat: support params serializer (#999)
Browse files Browse the repository at this point in the history
  • Loading branch information
GMierzwa authored Oct 30, 2023
1 parent f38331c commit 219d97b
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 4 deletions.
65 changes: 65 additions & 0 deletions docs/src/pages/reference/configuration/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,71 @@ export const customFormUrlEncodedFn = <Body>(body: Body): URLSearchParams => {
};
```

#### paramsSerializer

Type: `String` or `Object`.

Valid values: path of the paramsSerializer function or object with a path and name.

Use this property to add a custom params serializer to all requests that use query params.

If you provide an object you can also add a default property to use an export default function.

Example:

```js
module.exports = {
petstore: {
output: {
override: {
paramsSerializer: {
path: './api/mutator/custom-params-serializer-fn.ts',
name: 'customParamsSerializerFn',
// default: true
},
},
},
},
};
```

```ts
// type signature
export const customParamsSerializerFn = (
params: Record<string, any>,
): string => {
// do your implementation to transform the params

return params;
};
```

#### paramsSerializerOptions

Type: `Object`

Use this property to add a default params serializer. Current options are: `qs`.

All options are then passed to the chosen serializer.

Example:

```js
module.exports = {
petstore: {
output: {
override: {
paramsSerializerOptions: {
qs: {
arrayFormat: 'repeat',
},
},
},
},
},
};
```

#### useDates

Type: `Boolean`
Expand Down
3 changes: 3 additions & 0 deletions packages/angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const generateImplementation = (
override,
formData,
formUrlEncoded,
paramsSerializer,
}: GeneratorVerbOptions,
{ route, context }: GeneratorOptions,
) => {
Expand Down Expand Up @@ -204,6 +205,8 @@ const generateImplementation = (
requestOptions: override?.requestOptions,
isFormData,
isFormUrlEncoded,
paramsSerializer,
paramsSerializerOptions: override?.paramsSerializerOptions,
isAngular: true,
isExactOptionalPropertyTypes,
hasSignal: false,
Expand Down
23 changes: 22 additions & 1 deletion packages/axios/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,29 @@ const AXIOS_DEPENDENCIES: GeneratorDependency[] = [
},
];

const PARAMS_SERIALIZER_DEPENDENCIES: GeneratorDependency[] = [
{
exports: [
{
name: 'qs',
default: true,
values: true,
syntheticDefaultImport: true,
},
],
dependency: 'qs',
},
];

const returnTypesToWrite: Map<string, (title?: string) => string> = new Map();

export const getAxiosDependencies: ClientDependenciesBuilder = (
hasGlobalMutator,
) => [...(!hasGlobalMutator ? AXIOS_DEPENDENCIES : [])];
hasParamsSerializerOptions: boolean,
) => [
...(!hasGlobalMutator ? AXIOS_DEPENDENCIES : []),
...(hasParamsSerializerOptions ? PARAMS_SERIALIZER_DEPENDENCIES : []),
];

const generateAxiosImplementation = (
{
Expand All @@ -55,6 +73,7 @@ const generateAxiosImplementation = (
override,
formData,
formUrlEncoded,
paramsSerializer,
}: GeneratorVerbOptions,
{ route, context }: GeneratorOptions,
) => {
Expand Down Expand Up @@ -141,6 +160,8 @@ const generateAxiosImplementation = (
requestOptions: override?.requestOptions,
isFormData,
isFormUrlEncoded,
paramsSerializer,
paramsSerializerOptions: override?.paramsSerializerOptions,
isExactOptionalPropertyTypes,
hasSignal: false,
});
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/generators/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
GetterBody,
GetterQueryParam,
GetterResponse,
ParamsSerializerOptions,
Verbs,
} from '../types';
import { isObject, stringify } from '../utils';
Expand Down Expand Up @@ -36,13 +37,17 @@ export const generateAxiosOptions = ({
headers,
requestOptions,
hasSignal,
paramsSerializer,
paramsSerializerOptions,
}: {
response: GetterResponse;
isExactOptionalPropertyTypes: boolean;
queryParams?: GeneratorSchema;
headers?: GeneratorSchema;
requestOptions?: object | boolean;
hasSignal: boolean;
paramsSerializer?: GeneratorMutator;
paramsSerializerOptions?: ParamsSerializerOptions;
}) => {
const isRequestOptions = requestOptions !== false;
if (!queryParams && !headers && !response.isBlob) {
Expand Down Expand Up @@ -99,6 +104,16 @@ export const generateAxiosOptions = ({
}
}

if (queryParams && (paramsSerializer || paramsSerializerOptions?.qs)) {
if (paramsSerializer) {
value += `\n paramsSerializer: ${paramsSerializer.name},`;
} else {
value += `\n paramsSerializer: (params) => qs.stringify(params, ${JSON.stringify(
paramsSerializerOptions!.qs,
)}),`;
}
}

return value;
};

Expand All @@ -115,6 +130,8 @@ export const generateOptions = ({
isAngular,
isExactOptionalPropertyTypes,
hasSignal,
paramsSerializer,
paramsSerializerOptions,
}: {
route: string;
body: GetterBody;
Expand All @@ -128,6 +145,8 @@ export const generateOptions = ({
isAngular?: boolean;
isExactOptionalPropertyTypes: boolean;
hasSignal: boolean;
paramsSerializer?: GeneratorMutator;
paramsSerializerOptions?: ParamsSerializerOptions;
}) => {
const isBodyVerb = VERBS_WITH_BODY.includes(verb);
const bodyOptions = isBodyVerb
Expand All @@ -141,6 +160,8 @@ export const generateOptions = ({
requestOptions,
isExactOptionalPropertyTypes,
hasSignal,
paramsSerializer,
paramsSerializerOptions,
});

const options = axiosOptions ? `{${axiosOptions}}` : '';
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/generators/verbs-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
GeneratorVerbOptions,
GeneratorVerbsOptions,
NormalizedInputOptions,
NormalizedMutator,
NormalizedOperationOptions,
NormalizedOutputOptions,
NormalizedOverrideOutput,
Expand Down Expand Up @@ -164,6 +165,17 @@ const generateVerbOptions = async ({
})
: undefined;

const paramsSerializer =
isString(override?.paramsSerializer) || isObject(override?.paramsSerializer)
? await generateMutator({
output: output.target,
name: 'paramsSerializer',
mutator: override.paramsSerializer as NormalizedMutator,
workspace: context.workspace,
tsconfig: context.tsconfig,
})
: undefined;

const doc = jsDoc({ description, deprecated, summary });

const verbOption: GeneratorVerbOptions = {
Expand All @@ -181,6 +193,7 @@ const generateVerbOptions = async ({
mutator,
formData,
formUrlEncoded,
paramsSerializer,
override,
doc,
deprecated,
Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export type NormalizedOutputOptions = {
baseUrl?: string;
};

export type NormalizedParamsSerializerOptions = {
qs?: Record<string, any>;
};

export type NormalizedOverrideOutput = {
title?: (title: string) => string;
transformer?: OutputTransformer;
Expand All @@ -74,6 +78,8 @@ export type NormalizedOverrideOutput = {
header: false | ((info: InfoObject) => string[] | string);
formData: boolean | NormalizedMutator;
formUrlEncoded: boolean | NormalizedMutator;
paramsSerializer?: NormalizedMutator;
paramsSerializerOptions?: NormalizedParamsSerializerOptions;
components: {
schemas: {
suffix: string;
Expand Down Expand Up @@ -133,6 +139,7 @@ export type NormalizedOperationOptions = {
) => string;
formData?: boolean | NormalizedMutator;
formUrlEncoded?: boolean | NormalizedMutator;
paramsSerializer?: NormalizedMutator;
requestOptions?: object | boolean;
};

Expand Down Expand Up @@ -238,6 +245,10 @@ export type MutatorObject = {

export type Mutator = string | MutatorObject;

export type ParamsSerializerOptions = {
qs?: Record<string, any>;
};

export type OverrideOutput = {
title?: (title: string) => string;
transformer?: OutputTransformer;
Expand All @@ -257,6 +268,8 @@ export type OverrideOutput = {
header?: boolean | ((info: InfoObject) => string[] | string);
formData?: boolean | Mutator;
formUrlEncoded?: boolean | Mutator;
paramsSerializer?: Mutator;
paramsSerializerOptions?: ParamsSerializerOptions;
components?: {
schemas?: {
suffix?: string;
Expand Down Expand Up @@ -355,6 +368,7 @@ export type OperationOptions = {
) => string;
formData?: boolean | Mutator;
formUrlEncoded?: boolean | Mutator;
paramsSerializer?: Mutator;
requestOptions?: object | boolean;
};

Expand Down Expand Up @@ -487,6 +501,7 @@ export type GeneratorTarget = {
clientMutators?: GeneratorMutator[];
formData?: GeneratorMutator[];
formUrlEncoded?: GeneratorMutator[];
paramsSerializer?: GeneratorMutator[];
};

export type GeneratorTargetFull = {
Expand All @@ -501,6 +516,7 @@ export type GeneratorTargetFull = {
clientMutators?: GeneratorMutator[];
formData?: GeneratorMutator[];
formUrlEncoded?: GeneratorMutator[];
paramsSerializer?: GeneratorMutator[];
};

export type GeneratorOperation = {
Expand All @@ -513,6 +529,7 @@ export type GeneratorOperation = {
clientMutators?: GeneratorMutator[];
formData?: GeneratorMutator;
formUrlEncoded?: GeneratorMutator;
paramsSerializer?: GeneratorMutator;
operationName: string;
types?: {
result: (title?: string) => string;
Expand All @@ -535,6 +552,7 @@ export type GeneratorVerbOptions = {
mutator?: GeneratorMutator;
formData?: GeneratorMutator;
formUrlEncoded?: GeneratorMutator;
paramsSerializer?: GeneratorMutator;
override: NormalizedOverrideOutput;
deprecated?: boolean;
originalOperation: OperationObject;
Expand Down Expand Up @@ -601,6 +619,7 @@ export type ClientTitleBuilder = (title: string) => string;

export type ClientDependenciesBuilder = (
hasGlobalMutator: boolean,
hasParamsSerializerOptions: boolean,
packageJson?: PackageJson,
) => GeneratorDependency[];

Expand Down Expand Up @@ -817,6 +836,7 @@ export type GeneratorClientImports = (data: {
hasSchemaDir: boolean;
isAllowSyntheticDefaultImports: boolean;
hasGlobalMutator: boolean;
hasParamsSerializerOptions: boolean;
packageJson?: PackageJson;
}) => string;

Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/writers/single-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const writeSingleMode = async ({
clientMutators,
formData,
formUrlEncoded,
paramsSerializer,
} = generateTarget(builder, output);

let data = header;
Expand Down Expand Up @@ -60,6 +61,7 @@ export const writeSingleMode = async ({
hasSchemaDir: !!output.schemas,
isAllowSyntheticDefaultImports,
hasGlobalMutator: !!output.override.mutator,
hasParamsSerializerOptions: !!output.override.paramsSerializerOptions,
packageJson: output.packageJson,
});

Expand Down Expand Up @@ -91,6 +93,10 @@ export const writeSingleMode = async ({
data += generateMutatorImports({ mutators: formUrlEncoded });
}

if (paramsSerializer) {
data += generateMutatorImports({ mutators: paramsSerializer });
}

if (implementation.includes('NonReadonly<')) {
data += getOrvalGeneratedTypes();
data += '\n';
Expand Down
Loading

0 comments on commit 219d97b

Please sign in to comment.