Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend endpoint overrides for openapi codegen #4305

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 29 additions & 1 deletion docs/rtk-query/usage/code-generation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ npx @rtk-query/codegen-openapi openapi-config.ts

#### Generating tags

If your OpenAPI specification uses [tags](https://swagger.io/docs/specification/grouping-operations-with-tags/), you can specify the `tag` option to the codegen.
If your OpenAPI specification uses [tags](https://swagger.io/docs/specification/grouping-operations-with-tags/), you can specify the `tag` option to the codegen.
That will result in all generated endpoints having `providesTags`/`invalidatesTags` declarations for the `tags` of their respective operation definition.

Note that this will only result in string tags with no ids, so it might lead to scenarios where too much is invalidated and unneccessary requests are made on mutation.
Expand Down Expand Up @@ -144,6 +144,34 @@ const withOverride: ConfigFile = {
}
```

You can also filter the parameters that are included for an endpoint, as long as they aren't a path parameter. This filter is of type `ParameterMatcher`. For example, to only include parameters that begin with "x-" for the 'loginUser' endpoint, see the below example.

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: 'loginUser',
parameterFilter: /^x-/,
},
],
}
```

For more complex requirements, consider the other possible matchers, such as a `ParameterMatcherFunction`. The below example filters out any parameters that are in the header of the request.

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
// ...
endpointOverrides: [
{
pattern: /.*/,
parameterFilter: (_name, parameter) => parameter.in !== "header",
},
],
}
```

#### Generating hooks

Setting `hooks: true` will generate `useQuery` and `useMutation` hook exports. If you also want `useLazyQuery` hooks generated or more granular control, you can also pass an object in the shape of: `{ queries: boolean; lazyQueries: boolean; mutations: boolean }`.
Expand Down
21 changes: 19 additions & 2 deletions packages/rtk-query-codegen-openapi/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ import ts from 'typescript';
import type { ObjectPropertyDefinitions } from './codegen';
import { generateCreateApiCall, generateEndpointDefinition, generateImportNode, generateTagTypes } from './codegen';
import { generateReactHooks } from './generators/react-hooks';
import type { EndpointMatcher, EndpointOverrides, GenerationOptions, OperationDefinition, TextMatcher } from './types';
import type {
EndpointMatcher,
EndpointOverrides,
GenerationOptions,
OperationDefinition,
ParameterDefinition,
ParameterMatcher,
TextMatcher,
} from './types';
import { capitalize, getOperationDefinitions, getV3Doc, removeUndefined, isQuery as testIsQuery } from './utils';
import { factory } from './utils/factory';

Expand Down Expand Up @@ -55,6 +63,15 @@ function operationMatches(pattern?: EndpointMatcher) {
};
}

function argumentMatches(pattern?: ParameterMatcher) {
const checkMatch = typeof pattern === 'function' ? pattern : patternMatches(pattern);
return function matcher(argumentDefinition: ParameterDefinition) {
if (!pattern || argumentDefinition.in === 'path') return true;
const argumentName = argumentDefinition.name;
return checkMatch(argumentName, argumentDefinition);
};
}

function withQueryComment<T extends ts.Node>(node: T, def: QueryArgDefinition, hasTrailingNewLine: boolean): T {
const comment = def.origin === 'param' ? def.param.description : def.body.description;
if (comment) {
Expand Down Expand Up @@ -260,7 +277,7 @@ export async function generateApi(
const parameters = supportDeepObjects([
...apiGen.resolveArray(pathItem.parameters),
...apiGen.resolveArray(operation.parameters),
]);
]).filter(argumentMatches(overrides?.parameterFilter));

const allNames = parameters.map((p) => p.name);
const queryArg: QueryArgDefinitions = {};
Expand Down
15 changes: 13 additions & 2 deletions packages/rtk-query-codegen-openapi/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ export type OperationDefinition = {
operation: OpenAPIV3.OperationObject;
};

export type ParameterDefinition = OpenAPIV3.ParameterObject;

type Require<T, K extends keyof T> = { [k in K]-?: NonNullable<T[k]> } & Omit<T, K>;
type Optional<T, K extends keyof T> = { [k in K]?: NonNullable<T[k]> } & Omit<T, K>;
type Id<T> = { [K in keyof T]: T[K] } & {};
type AtLeastOneKey<T> = {
[K in keyof T]-?: Pick<T, K> & Partial<T>;
}[keyof T];

export const operationKeys = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] as const;

Expand Down Expand Up @@ -80,6 +85,10 @@ export type EndpointMatcherFunction = (operationName: string, operationDefinitio

export type EndpointMatcher = TextMatcher | EndpointMatcherFunction;

export type ParameterMatcherFunction = (parameterName: string, parameterDefinition: ParameterDefinition) => boolean;

export type ParameterMatcher = TextMatcher | ParameterMatcherFunction;

export interface OutputFileOptions extends Partial<CommonOptions> {
outputFile: string;
filterEndpoints?: EndpointMatcher;
Expand All @@ -91,10 +100,12 @@ export interface OutputFileOptions extends Partial<CommonOptions> {
useEnumType?: boolean;
}

export interface EndpointOverrides {
export type EndpointOverrides = {
pattern: EndpointMatcher;
} & AtLeastOneKey<{
type: 'mutation' | 'query';
}
parameterFilter: ParameterMatcher;
}>;

export type ConfigFile =
| Id<Require<CommonOptions & OutputFileOptions, 'outputFile'>>
Expand Down