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: Add useInfiniteQuery support #122

Merged
merged 7 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/nextjs-app/app/components/PaginatedPets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { useDefaultServiceFindPaginatedPetsInfinite } from "@/openapi/queries/infiniteQueries";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import React from "react";

export default function PaginatedPets() {
const { data, fetchNextPage } = useDefaultServiceFindPaginatedPetsInfinite({
limit: 10,
tags: [],
});

return (
<>
<h1>Pet List with Pagination</h1>
<ul>
{data?.pages.map((group, i) => (
<React.Fragment key={group.pets?.at(0)?.id}>
{group.pets?.map((pet) => (
<li key={pet.id}>{pet.name}</li>
))}
</React.Fragment>
))}
</ul>
{data?.pages.at(-1)?.nextPage && (
<button
type="button"
onClick={() => fetchNextPage()}
className="bg-blue-500 px-4 py-2 text-white mt-4"
>
Load More
</button>
)}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
9 changes: 9 additions & 0 deletions examples/nextjs-app/app/infinite-loader/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import PaginatedPets from "../components/PaginatedPets";

export default async function InfiniteLoaderPage() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<PaginatedPets />
</main>
);
}
6 changes: 5 additions & 1 deletion examples/nextjs-app/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
QueryClient,
dehydrate,
} from "@tanstack/react-query";
import Pets from "./pets";
import Link from "next/link";
import Pets from "./components/Pets";

export default async function Home() {
const queryClient = new QueryClient();
Expand All @@ -19,6 +20,9 @@ export default async function Home() {
<HydrationBoundary state={dehydrate(queryClient)}>
<Pets />
</HydrationBoundary>
<Link href="/infinite-loader" className="underline">
Go to Infinite Loader &rarr;
</Link>
</main>
);
}
5 changes: 4 additions & 1 deletion examples/nextjs-app/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ export const request = <T>(
url: options.url,
data: options.body,
method: options.method,
params: options.path,
params: {
...options.query,
...options.path,
},
headers: formattedHeaders,
cancelToken: source.token,
})
Expand Down
46 changes: 46 additions & 0 deletions examples/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,52 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/paginated-pets:
get:
description: |
Returns paginated pets from the system that the user has access to
operationId: findPaginatedPets
parameters:
- name: page
in: query
description: page number
required: false
schema:
type: integer
format: int32
- name: tags
in: query
description: tags to filter by
required: false
style: form
schema:
type: array
items:
type: string
- name: limit
in: query
description: maximum number of results to return
required: false
schema:
type: integer
format: int32
responses:
'200':
description: pet response
content:
application/json:
schema:
type: object
properties:
pets:
type: array
items:
$ref: '#/components/schemas/Pet'
nextPage:
type: integer
format: int32
minimum: 1

components:
schemas:
Pet:
Expand Down
5 changes: 4 additions & 1 deletion examples/react-app/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ export const request = <T>(
url: options.url,
data: options.body,
method: options.method,
params: options.path,
params: {
...options.query,
...options.path,
},
headers: formattedHeaders,
cancelToken: source.token,
})
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/cli.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export type LimitedUserConfig = {
debug?: boolean;
noSchemas?: boolean;
schemaType?: "form" | "json";
pageParam: string;
nextPageParam: string;
};

async function setupProgram() {
Expand Down Expand Up @@ -90,6 +92,16 @@ async function setupProgram() {
"Type of JSON schema [Default: 'json']",
).choices(["form", "json"]),
)
.option(
"--pageParam <value>",
"Name of the query parameter used for pagination",
"page",
)
.option(
"--nextPageParam <value>",
"Name of the response parameter used for next page",
"nextPage",
)
.parse();

const options = program.opts<LimitedUserConfig>();
Expand Down
1 change: 1 addition & 0 deletions src/constants.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const modalsFileName = "types.gen";

export const OpenApiRqFiles = {
queries: "queries",
infiniteQueries: "infiniteQueries",
common: "common",
suspense: "suspense",
index: "index",
Expand Down
19 changes: 17 additions & 2 deletions src/createExports.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type ts from "typescript";
import { createPrefetch } from "./createPrefetch.mjs";
import { createUseMutation } from "./createUseMutation.mjs";
import { createUseQuery } from "./createUseQuery.mjs";
import type { Service } from "./service.mjs";

export const createExports = (service: Service) => {
export const createExports = (
service: Service,
pageParam: string,
nextPageParam: string,
) => {
const { klasses } = service;
const methods = klasses.flatMap((k) => k.methods);

Expand All @@ -23,7 +28,9 @@ export const createExports = (service: Service) => {
m.httpMethodName.toUpperCase().includes("DELETE"),
);

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

const allPostMutations = allPost.map((m) => createUseMutation(m));
Expand Down Expand Up @@ -60,6 +67,10 @@ export const createExports = (service: Service) => {

const mainExports = [...mainQueries, ...mainMutations];

const infiniteQueriesExports = allQueries
.flatMap(({ infiniteQueryHook }) => [infiniteQueryHook])
.filter(Boolean) as ts.VariableStatement[];

const suspenseQueries = allQueries.flatMap(({ suspenseQueryHook }) => [
suspenseQueryHook,
]);
Expand All @@ -81,6 +92,10 @@ export const createExports = (service: Service) => {
* Main exports are the hooks that are used in the components
*/
mainExports,
/**
* Infinite queries exports are the hooks that are used in the infinite scroll components
*/
infiniteQueriesExports,
/**
* Suspense exports are the hooks that are used in the suspense components
*/
Expand Down
47 changes: 44 additions & 3 deletions src/createSource.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { createExports } from "./createExports.mjs";
import { createImports } from "./createImports.mjs";
import { getServices } from "./service.mjs";

const createSourceFile = async (outputPath: string, serviceEndName: string) => {
const createSourceFile = async (
outputPath: string,
serviceEndName: string,
pageParam: string,
nextPageParam: string,
) => {
const project = new Project({
// Optionally specify compiler options, tsconfig.json, in-memory file system, and more here.
// If you initialize with a tsconfig.json, then it will automatically populate the project
Expand All @@ -25,7 +30,7 @@ const createSourceFile = async (outputPath: string, serviceEndName: string) => {
project,
});

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

const commonSource = ts.factory.createSourceFile(
[...imports, ...exports.allCommon],
Expand Down Expand Up @@ -66,6 +71,12 @@ const createSourceFile = async (outputPath: string, serviceEndName: string) => {
ts.NodeFlags.None,
);

const infiniteQueriesSource = ts.factory.createSourceFile(
[commonImport, ...imports, ...exports.infiniteQueriesExports],
ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
ts.NodeFlags.None,
);

const suspenseSource = ts.factory.createSourceFile(
[commonImport, ...imports, ...exports.suspenseExports],
ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
Expand All @@ -86,6 +97,7 @@ const createSourceFile = async (outputPath: string, serviceEndName: string) => {

return {
commonSource,
infiniteQueriesSource,
mainSource,
suspenseSource,
indexSource,
Expand All @@ -97,10 +109,14 @@ export const createSource = async ({
outputPath,
version,
serviceEndName,
pageParam,
nextPageParam,
}: {
outputPath: string;
version: string;
serviceEndName: string;
pageParam: string;
nextPageParam: string;
}) => {
const queriesFile = ts.createSourceFile(
`${OpenApiRqFiles.queries}.ts`,
Expand All @@ -109,6 +125,13 @@ export const createSource = async ({
false,
ts.ScriptKind.TS,
);
const infiniteQueriesFile = ts.createSourceFile(
`${OpenApiRqFiles.infiniteQueries}.ts`,
"",
ts.ScriptTarget.Latest,
false,
ts.ScriptKind.TS,
);
const commonFile = ts.createSourceFile(
`${OpenApiRqFiles.common}.ts`,
"",
Expand Down Expand Up @@ -148,10 +171,16 @@ export const createSource = async ({
const {
commonSource,
mainSource,
infiniteQueriesSource,
suspenseSource,
indexSource,
prefetchSource,
} = await createSourceFile(outputPath, serviceEndName);
} = await createSourceFile(
outputPath,
serviceEndName,
pageParam,
nextPageParam,
);

const comment = `// generated with @7nohe/openapi-react-query-codegen@${version} \n\n`;

Expand All @@ -163,6 +192,14 @@ export const createSource = async ({
comment +
printer.printNode(ts.EmitHint.Unspecified, mainSource, queriesFile);

const infiniteQueriesResult =
comment +
printer.printNode(
ts.EmitHint.Unspecified,
infiniteQueriesSource,
infiniteQueriesFile,
);

const suspenseResult =
comment +
printer.printNode(ts.EmitHint.Unspecified, suspenseSource, suspenseFile);
Expand All @@ -184,6 +221,10 @@ export const createSource = async ({
name: `${OpenApiRqFiles.common}.ts`,
content: commonResult,
},
{
name: `${OpenApiRqFiles.infiniteQueries}.ts`,
content: infiniteQueriesResult,
},
{
name: `${OpenApiRqFiles.queries}.ts`,
content: mainResult,
Expand Down
Loading