Skip to content

Commit

Permalink
Adding initialPageParam and nested nextPageParam support (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamgoose committed Sep 16, 2024
1 parent 0582f1d commit d67c594
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 32 deletions.
39 changes: 21 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,25 @@ Usage: openapi-rq [options]
Generate React Query code based on OpenAPI
Options:
-V, --version output the version number
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
-o, --output <value> Output directory (default: "openapi")
-c, --client <value> HTTP client to generate (choices: "angular", "axios", "fetch", "node", "xhr", default: "fetch")
--request <value> Path to custom request file
--format <value> Process output folder with formatter? (choices: "biome", "prettier")
--lint <value> Process output folder with linter? (choices: "biome", "eslint")
--operationId Use operation ID to generate operation names?
--serviceResponse <value> Define shape of returned value from service calls (choices: "body", "response", default: "body")
--base <value> Manually set base in OpenAPI config instead of inferring from server value
--enums <value> Generate JavaScript objects from enum definitions? ['javascript', 'typescript', 'typescript+namespace']
--enums <value> Generate JavaScript objects from enum definitions? (choices: "javascript", "typescript")
--useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
--debug Run in debug mode?
--noSchemas Disable generating JSON schemas
--schemaType <value> Type of JSON schema [Default: 'json'] (choices: "form", "json")
--pageParam <value> Name of the query parameter used for pagination (default: "page")
--nextPageParam <value> Name of the response parameter used for next page (default: "nextPage")
-V, --version output the version number
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
-o, --output <value> Output directory (default: "openapi")
-c, --client <value> HTTP client to generate (choices: "angular", "axios", "fetch", "node", "xhr", default: "fetch")
--request <value> Path to custom request file
--format <value> Process output folder with formatter? (choices: "biome", "prettier")
--lint <value> Process output folder with linter? (choices: "biome", "eslint")
--operationId Use operation ID to generate operation names?
--serviceResponse <value> Define shape of returned value from service calls (choices: "body", "response", default: "body")
--base <value> Manually set base in OpenAPI config instead of inferring from server value
--enums <value> Generate JavaScript objects from enum definitions? ['javascript', 'typescript', 'typescript+namespace']
--enums <value> Generate JavaScript objects from enum definitions? (choices: "javascript", "typescript")
--useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object
--debug Run in debug mode?
--noSchemas Disable generating JSON schemas
--schemaType <value> Type of JSON schema [Default: 'json'] (choices: "form", "json")
--pageParam <value> Name of the query parameter used for pagination (default: "page")
--nextPageParam <value> Name of the response parameter used for next page (default: "nextPage")
--initialPageParam <value> Initial value for the pagination parameter (default: "1")
-h, --help display help for command
```

Expand Down Expand Up @@ -241,6 +242,8 @@ export default App;

This feature will generate a function in infiniteQueries.ts when the name specified by the `pageParam` option exists in the query parameters and the name specified by the `nextPageParam` option exists in the response.

The `initialPageParam` option can be specified to set the intial page to load, defaults to 1. The `nextPageParam` supports dot notation for nested values (i.e. `meta.next`).

Example Schema:

```yml
Expand Down
6 changes: 6 additions & 0 deletions src/cli.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type LimitedUserConfig = {
schemaType?: "form" | "json";
pageParam: string;
nextPageParam: string;
initialPageParam: string | number;
};

async function setupProgram() {
Expand Down Expand Up @@ -102,6 +103,11 @@ async function setupProgram() {
"Name of the response parameter used for next page",
"nextPage",
)
.option(
"--initialPageParam <value>",
"Initial page value to query",
"initialPageParam",
)
.parse();

const options = program.opts<LimitedUserConfig>();
Expand Down
9 changes: 6 additions & 3 deletions src/common.mts
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ export function formatOptions(options: LimitedUserConfig) {
} else if (!Number.isNaN(parsedNumber)) {
(acc as unknown as Record<string, number>)[typedKey] = parsedNumber;
} else {
(acc as unknown as Record<string, string | undefined | boolean>)[
typedKey
] = typedValue;
(
acc as unknown as Record<
string,
string | number | undefined | boolean
>
)[typedKey] = typedValue;
}
return acc;
},
Expand Down
3 changes: 2 additions & 1 deletion src/createExports.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const createExports = (
service: Service,
pageParam: string,
nextPageParam: string,
initialPageParam: string,
) => {
const { klasses } = service;
const methods = klasses.flatMap((k) => k.methods);
Expand All @@ -29,7 +30,7 @@ export const createExports = (
);

const allGetQueries = allGet.map((m) =>
createUseQuery(m, pageParam, nextPageParam),
createUseQuery(m, pageParam, nextPageParam, initialPageParam),
);
const allPrefetchQueries = allGet.map((m) => createPrefetch(m));

Expand Down
11 changes: 10 additions & 1 deletion src/createSource.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const createSourceFile = async (
serviceEndName: string,
pageParam: string,
nextPageParam: string,
initialPageParam: string,
) => {
const project = new Project({
// Optionally specify compiler options, tsconfig.json, in-memory file system, and more here.
Expand All @@ -30,7 +31,12 @@ const createSourceFile = async (
project,
});

const exports = createExports(service, pageParam, nextPageParam);
const exports = createExports(
service,
pageParam,
nextPageParam,
initialPageParam,
);

const commonSource = ts.factory.createSourceFile(
[...imports, ...exports.allCommon],
Expand Down Expand Up @@ -111,12 +117,14 @@ export const createSource = async ({
serviceEndName,
pageParam,
nextPageParam,
initialPageParam,
}: {
outputPath: string;
version: string;
serviceEndName: string;
pageParam: string;
nextPageParam: string;
initialPageParam: string;
}) => {
const queriesFile = ts.createSourceFile(
`${OpenApiRqFiles.queries}.ts`,
Expand Down Expand Up @@ -180,6 +188,7 @@ export const createSource = async ({
serviceEndName,
pageParam,
nextPageParam,
initialPageParam,
);

const comment = `// generated with @7nohe/openapi-react-query-codegen@${version} \n\n`;
Expand Down
33 changes: 27 additions & 6 deletions src/createUseQuery.mts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ export function createQueryHook({
className,
pageParam,
nextPageParam,
initialPageParam,
}: {
queryString: "useSuspenseQuery" | "useQuery" | "useInfiniteQuery";
suffix: string;
Expand All @@ -238,6 +239,7 @@ export function createQueryHook({
className: string;
pageParam?: string;
nextPageParam?: string;
initialPageParam?: string;
}) {
const methodName = getNameFromMethod(method);
const customHookName = hookNameFromMethod({ method, className });
Expand Down Expand Up @@ -447,7 +449,11 @@ export function createQueryHook({
),
),
),
...createInfiniteQueryParams(pageParam, nextPageParam),
...createInfiniteQueryParams(
pageParam,
nextPageParam,
initialPageParam,
),
ts.factory.createSpreadAssignment(
ts.factory.createIdentifier("options"),
),
Expand All @@ -467,6 +473,7 @@ export const createUseQuery = (
{ className, method, jsDoc }: MethodDescription,
pageParam: string,
nextPageParam: string,
initialPageParam: string,
) => {
const methodName = getNameFromMethod(method);
const queryKey = createQueryKeyFromMethod({ method, className });
Expand Down Expand Up @@ -517,6 +524,7 @@ export const createUseQuery = (
className,
pageParam,
nextPageParam,
initialPageParam,
})
: undefined;

Expand Down Expand Up @@ -625,14 +633,18 @@ function queryKeyFn(
);
}

function createInfiniteQueryParams(pageParam?: string, nextPageParam?: string) {
function createInfiniteQueryParams(
pageParam?: string,
nextPageParam?: string,
initialPageParam = "1",
) {
if (pageParam === undefined || nextPageParam === undefined) {
return [];
}
return [
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier("initialPageParam"),
ts.factory.createNumericLiteral(1),
ts.factory.createStringLiteral(initialPageParam),
),
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier("getNextPageParam"),
Expand All @@ -655,9 +667,18 @@ function createInfiniteQueryParams(pageParam?: string, nextPageParam?: string) {
ts.factory.createParenthesizedExpression(
ts.factory.createAsExpression(
ts.factory.createIdentifier("response"),
ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(`{ ${nextPageParam}: number }`),
),
nextPageParam.split(".").reduceRight((acc, segment) => {
return ts.factory.createTypeLiteralNode([
ts.factory.createPropertySignature(
undefined,
ts.factory.createIdentifier(segment),
undefined,
acc,
),
]);
}, ts.factory.createKeywordTypeNode(
ts.SyntaxKind.NumberKeyword,
) as ts.TypeNode),
),
),
ts.factory.createIdentifier(nextPageParam),
Expand Down
1 change: 1 addition & 0 deletions src/generate.mts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function generate(options: LimitedUserConfig, version: string) {
serviceEndName: "Service", // we are hard coding this because changing the service end name was depreciated in @hey-api/openapi-ts
pageParam: formattedOptions.pageParam,
nextPageParam: formattedOptions.nextPageParam,
initialPageParam: formattedOptions.initialPageParam.toString(),
});
await print(source, formattedOptions);
const queriesOutputPath = buildQueriesOutputPath(options.output);
Expand Down
8 changes: 7 additions & 1 deletion tests/__snapshots__/generate.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ import * as Common from "./common";
export const useDefaultServiceFindPaginatedPetsInfinite = <TData = InfiniteData<Common.DefaultServiceFindPaginatedPetsDefaultResponse>, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ limit, tags }: {
limit?: number;
tags?: string[];
} = {}, queryKey?: TQueryKey, options?: Omit<UseInfiniteQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useInfiniteQuery({ queryKey: Common.UseDefaultServiceFindPaginatedPetsKeyFn({ limit, tags }, queryKey), queryFn: ({ pageParam }) => DefaultService.findPaginatedPets({ limit, page: pageParam as number, tags }) as TData, initialPageParam: 1, getNextPageParam: response => (response as { nextPage: number }).nextPage, ...options });
} = {}, queryKey?: TQueryKey, options?: Omit<UseInfiniteQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useInfiniteQuery({
queryKey: Common.UseDefaultServiceFindPaginatedPetsKeyFn({ limit, tags }, queryKey), queryFn: ({ pageParam }) => DefaultService.findPaginatedPets({ limit, page: pageParam as number, tags }) as TData, initialPageParam: "initial", getNextPageParam: response => (response as {
meta: {
next: number;
};
}).meta.next, ...options
});
"
`;
Expand Down
2 changes: 1 addition & 1 deletion tests/createExports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe(fileName, () => {
});
project.addSourceFilesAtPaths(path.join(outputPath(fileName), "**", "*"));
const service = await getServices(project);
const exports = createExports(service, "page", "nextPage");
const exports = createExports(service, "page", "nextPage", "initial");

const commonTypes = exports.allCommon
.filter((c) => c.kind === SyntaxKind.TypeAliasDeclaration)
Expand Down
3 changes: 2 additions & 1 deletion tests/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ describe("generate", () => {
output: path.join("tests", "outputs"),
lint: "eslint",
pageParam: "page",
nextPageParam: "nextPage",
nextPageParam: "meta.next",
initialPageParam: "initial",
};
await generate(options, "1.0.0");
});
Expand Down

0 comments on commit d67c594

Please sign in to comment.