From 313c8b0f52df5cf8285730a2c183435ac14f4dd0 Mon Sep 17 00:00:00 2001 From: Georgiev Anton Date: Tue, 7 Nov 2023 15:21:07 +0300 Subject: [PATCH] feat(query): add prefetch functions (#956) * feat(query): add prefetch functions * feat(query): add prefetch functions doc * feat(query): prefetch functions add types * feat(query): prefetch functions generate samples * feat(query): prefetch functions fix pageParam * feat(query): prefetch functions fix samples * Update object.ts * feat(query): prefetch functions fix after review --------- Co-authored-by: Melloware --- CODE_OF_CONDUCT.md | 24 ++++++------- README.md | 1 + .../pages/reference/configuration/output.md | 34 ++++++++++++++++++- packages/core/src/getters/object.ts | 3 +- packages/core/src/types.ts | 2 ++ packages/core/src/writers/schemas.ts | 22 ++++++------ packages/orval/src/utils/options.ts | 3 ++ packages/query/src/index.ts | 24 ++++++++++++- packages/query/src/utils.ts | 1 + packages/zod/src/index.ts | 2 +- .../petstoreFromFileSpecWithTransformer.ts | 6 ++-- .../petstoreFromFileSpecWithTransformer.ts | 3 +- 12 files changed, 94 insertions(+), 31 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 6e73e5688..048844e9e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -119,8 +119,8 @@ version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. +[Mozilla's code of conduct enforcement ladder][mozilla coc]. For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. diff --git a/README.md b/README.md index 120a20cfd..2130dc2ce 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ You can find below some samples - [angular app](https://github.com/anymaniax/orval/tree/master/samples/angular-app) ### All Thanks To Our Contributors: + diff --git a/docs/src/pages/reference/configuration/output.md b/docs/src/pages/reference/configuration/output.md index 558cd789f..b0007fdd4 100644 --- a/docs/src/pages/reference/configuration/output.md +++ b/docs/src/pages/reference/configuration/output.md @@ -565,6 +565,38 @@ Type: `Boolean`. Use to generate a useInfiniteQuery custom hook. +##### usePrefetch + +Type: `Boolean`. + +Use to generate a prefetching functions. +This may be useful for the NextJS SSR or any prefetching situations. + +Example generated function: + +```js +export const prefetchGetCategories = async < + TData = Awaited>, + TError = ErrorType, +>( + queryClient: QueryClient, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData, + >, + request?: SecondParameter, + }, +): Promise => { + const queryOptions = getGetCategoriesQueryOptions(options); + + await queryClient.prefetchQuery(queryOptions); + + return queryClient; +}; +``` + ##### useInfiniteQueryParam Type: `String`. @@ -691,7 +723,7 @@ module.exports = { mock: { properties: { '/tag|name/': 'jon', // Matches every property named 'tag' or 'name', including nested ones - '/.*\.user\.id/': faker.string.uuid(), // Matches every property named 'id', inside an object named 'user', including nested ones + '/.*.user.id/': faker.string.uuid(), // Matches every property named 'id', inside an object named 'user', including nested ones email: () => faker.internet.email(), // Matches only the property 'email' 'user.id': () => faker.string.uuid(), // Matches only the full path 'user.id' }, diff --git a/packages/core/src/getters/object.ts b/packages/core/src/getters/object.ts index c51ec27a4..b777427f4 100644 --- a/packages/core/src/getters/object.ts +++ b/packages/core/src/getters/object.ts @@ -181,8 +181,7 @@ export const getObject = ({ } return { - value: - (item.type === 'object' ? '{ [key: string]: any }' : 'unknown') + nullable, + value: (item.type === 'object' ? '{ [key: string]: any }' : 'unknown') + nullable, imports: [], schemas: [], isEnum: false, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d9f20b973..97ac6c594 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -316,6 +316,7 @@ export type NormalizedQueryOptions = { useInfinite?: boolean; useSuspenseInfiniteQuery?: boolean; useInfiniteQueryParam?: string; + usePrefetch?: boolean; options?: any; queryKey?: NormalizedMutator; queryOptions?: NormalizedMutator; @@ -331,6 +332,7 @@ export type QueryOptions = { useInfinite?: boolean; useSuspenseInfiniteQuery?: boolean; useInfiniteQueryParam?: string; + usePrefetch?: boolean; options?: any; queryKey?: Mutator; queryOptions?: Mutator; diff --git a/packages/core/src/writers/schemas.ts b/packages/core/src/writers/schemas.ts index 998e62f01..fe6bb183a 100644 --- a/packages/core/src/writers/schemas.ts +++ b/packages/core/src/writers/schemas.ts @@ -116,21 +116,21 @@ export const writeSchemas = async ({ const stringData = data.toString(); const importStatements = schemas - .filter((schema) => { - return ( - !stringData.includes(`export * from './${camel(schema.name)}'`) && - !stringData.includes(`export * from "./${camel(schema.name)}"`) - ); - }) - .map((schema) => `export * from './${camel(schema.name)}';`); + .filter((schema) => { + return ( + !stringData.includes(`export * from './${camel(schema.name)}'`) && + !stringData.includes(`export * from "./${camel(schema.name)}"`) + ); + }) + .map((schema) => `export * from './${camel(schema.name)}';`); const currentFileExports = (stringData - .match(/export \* from(.*)('|")/g) - ?.map((s) => s + ';') ?? []) as string[]; + .match(/export \* from(.*)('|")/g) + ?.map((s) => s + ';') ?? []) as string[]; const exports = [...currentFileExports, ...importStatements] - .sort() - .join('\n'); + .sort() + .join('\n'); const fileContent = `${header}\n${exports}`; diff --git a/packages/orval/src/utils/options.ts b/packages/orval/src/utils/options.ts index a51bba48f..06ab8867e 100644 --- a/packages/orval/src/utils/options.ts +++ b/packages/orval/src/utils/options.ts @@ -381,6 +381,9 @@ const normalizeQueryOptions = ( } return { + ...(!isUndefined(queryOptions.usePrefetch) + ? { usePrefetch: queryOptions.usePrefetch } + : {}), ...(!isUndefined(queryOptions.useQuery) ? { useQuery: queryOptions.useQuery } : {}), diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index df6f8536b..f385b6920 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -154,6 +154,7 @@ const REACT_QUERY_DEPENDENCIES_V3: GeneratorDependency[] = [ { name: 'UseQueryResult' }, { name: 'UseInfiniteQueryResult' }, { name: 'QueryKey' }, + { name: 'QueryClient' }, ], dependency: 'react-query', }, @@ -178,6 +179,7 @@ const REACT_QUERY_DEPENDENCIES: GeneratorDependency[] = [ { name: 'UseInfiniteQueryResult' }, { name: 'UseSuspenseInfiniteQueryResult' }, { name: 'QueryKey' }, + { name: 'QueryClient' }, { name: 'InfiniteData' }, ], dependency: '@tanstack/react-query', @@ -803,6 +805,7 @@ const generateQueryImplementation = ({ hasSvelteQueryV4, hasQueryV5, doc, + usePrefetch, }: { queryOption: { name: string; @@ -830,6 +833,7 @@ const generateQueryImplementation = ({ hasSvelteQueryV4: boolean; hasQueryV5: boolean; doc?: string; + usePrefetch?: boolean; }) => { const queryProps = toObjectString(props, 'implementation'); @@ -1031,7 +1035,24 @@ ${doc}export const ${camel( }; return query; -}\n`; +}\n +${ + usePrefetch + ? `${doc}export const ${camel( + `prefetch-${name}`, + )} = async >, TError = ${errorType}>(\n queryClient: QueryClient, ${queryProps} ${queryArguments}\n ): Promise => { + + const ${queryOptionsVarName} = ${queryOptionsFnName}(${queryProperties}${ + queryProperties ? ',' : '' + }${isRequestOptions ? 'options' : 'queryOptions'}) + + await queryClient.${camel(`prefetch-${type}`)}(${queryOptionsVarName}); + + return queryClient; +}\n` + : '' +} +`; }; const generateQueryHook = async ( @@ -1218,6 +1239,7 @@ const generateQueryHook = async ( hasSvelteQueryV4, hasQueryV5, doc, + usePrefetch: query.usePrefetch, }), '', )} diff --git a/packages/query/src/utils.ts b/packages/query/src/utils.ts index daa757ef0..a03a5a538 100644 --- a/packages/query/src/utils.ts +++ b/packages/query/src/utils.ts @@ -19,6 +19,7 @@ export const normalizeQueryOptions = ( outputWorkspace: string, ): NormalizedQueryOptions => { return { + ...(queryOptions.usePrefetch ? { usePrefetch: true } : {}), ...(queryOptions.useQuery ? { useQuery: true } : {}), ...(queryOptions.useInfinite ? { useInfinite: true } : {}), ...(queryOptions.useInfiniteQueryParam diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 030290ab4..c174b9e29 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -279,7 +279,7 @@ const parseZodValidationSchemaDefinition = ( if (fn === 'additionalProperties') { const value = args.functions.map(parseProperty).join(''); const valueWithZod = `${value.startsWith('.') ? 'zod' : ''}${value}`; - consts += args.consts + consts += args.consts; return `zod.record(zod.string(), ${valueWithZod})`; } diff --git a/samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts b/samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts index 6feff2f4f..a754ae7b7 100644 --- a/samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts +++ b/samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts @@ -117,7 +117,8 @@ export const getListPetsInfiniteQueryOptions = < Awaited>, QueryKey, ListPetsParams['limit'] - > = ({ pageParam }) => listPets({ limit: pageParam, ...params }, version); + > = ({ pageParam }) => + listPets({ ...params, limit: pageParam || params.page }, version); return { queryKey, @@ -348,7 +349,8 @@ export const getListPetsSuspenseInfiniteQueryOptions = < Awaited>, QueryKey, ListPetsParams['limit'] - > = ({ pageParam }) => listPets({ limit: pageParam, ...params }, version); + > = ({ pageParam }) => + listPets({ ...params, limit: pageParam || params.page }, version); return { queryKey, diff --git a/samples/vue-query/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts b/samples/vue-query/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts index 55193f05f..1a1cc22c7 100644 --- a/samples/vue-query/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts +++ b/samples/vue-query/src/api/endpoints/petstoreFromFileSpecWithTransformer.ts @@ -79,7 +79,8 @@ export const getListPetsInfiniteQueryOptions = < const queryFn: QueryFunction>> = ({ signal, pageParam, - }) => listPets({ limit: pageParam, ...params }, version, signal); + }) => + listPets({ ...params, limit: pageParam || params.page }, version, signal); return { queryKey,