Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 48 additions & 6 deletions src/generators/generate/generateQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,40 @@ function renderMutation({
? `${endpointParams}${endpointParams ? "; " : ""}abortController?: AbortController; onUploadProgress?: (progress: { loaded: number; total: number }) => void`
: endpointParams;

const isPost = endpoint.method === "post";
const scopeEnabled = !!resolver.options.mutationScope && !isPost;
const scopePathParams = scopeEnabled
? mapEndpointParamsToFunctionParams(resolver, endpoint, {}).filter((p) => p.paramType === "Path")
: [];
const isScoped = scopePathParams.length > 0;

const nonPathEndpointParams = isScoped
? renderEndpointParams(resolver, endpoint, {
includeFileParam: true,
optionalPathParams: resolver.options.workspaceContext,
modelNamespaceTag: tag,
excludePathParams: true,
})
: endpointParams;
const nonPathMutationVariablesType = isScoped
? endpoint.mediaUpload
? `${nonPathEndpointParams}${nonPathEndpointParams ? "; " : ""}abortController?: AbortController; onUploadProgress?: (progress: { loaded: number; total: number }) => void`
: nonPathEndpointParams
: mutationVariablesType;

const pathParamFirstArg = isScoped
? `{ ${scopePathParams.map((p) => p.name).join(", ")} }: { ${scopePathParams.map((p) => `${p.name}: ${p.type}`).join("; ")} }, `
: "";
const mutationOptionsTypeArg = isScoped
? nonPathMutationVariablesType
? `, { ${nonPathMutationVariablesType} }`
: ""
: `, { ${mutationVariablesType} }`;

const lines: string[] = [];
lines.push(renderQueryJsDocs({ resolver, endpoint, mode: "mutation", tag }));
lines.push(
`export const ${getQueryName(endpoint, true)} = (options?: AppMutationOptions<typeof ${endpointFunction}, { ${mutationVariablesType} }>${hasMutationEffects ? ` & ${MUTATION_EFFECTS.optionsType}` : ""}${hasAxiosRequestConfig ? `, ${AXIOS_REQUEST_CONFIG_NAME}?: ${AXIOS_REQUEST_CONFIG_TYPE}` : ""}) => {`,
`export const ${getQueryName(endpoint, true)} = (${pathParamFirstArg}options?: AppMutationOptions<typeof ${endpointFunction}${mutationOptionsTypeArg}>${hasMutationEffects ? ` & ${MUTATION_EFFECTS.optionsType}` : ""}${hasAxiosRequestConfig ? `, ${AXIOS_REQUEST_CONFIG_NAME}?: ${AXIOS_REQUEST_CONFIG_TYPE}` : ""}) => {`,
);
if (hasMutationDefaultOnError) {
lines.push(" const queryConfig = OpenApiQueryConfig.useConfig();");
Expand All @@ -963,9 +993,15 @@ function renderMutation({
lines.push("");
lines.push(` return ${QUERY_HOOKS.mutation}({`);

const mutationFnArg = endpointParams
? `{ ${destructuredMutationArgs}${endpoint.mediaUpload ? `${destructuredMutationArgs ? ", " : ""}abortController, onUploadProgress` : ""} }`
: "";
const nonPathDestructuredArgs = isScoped
? renderEndpointArgs(resolver, endpoint, { includeFileParam: true, excludePathParams: true })
: destructuredMutationArgs;
const effectiveParams = isScoped ? nonPathEndpointParams : endpointParams;
const effectiveArgs = isScoped ? nonPathDestructuredArgs : destructuredMutationArgs;
const mutationFnArg =
effectiveParams || endpoint.mediaUpload
? `{ ${effectiveArgs}${endpoint.mediaUpload ? `${effectiveArgs ? ", " : ""}abortController, onUploadProgress` : ""} }`
: "";
lines.push(
` mutationFn: ${endpoint.mediaUpload ? "async " : ""}(${mutationFnArg}) => ${hasMutationFnBody ? "{ " : ""}`,
);
Expand Down Expand Up @@ -1015,15 +1051,21 @@ function renderMutation({
lines.push(",");
}

if (isScoped) {
const scopePathParamInterpolations = scopePathParams.map((p) => `:\${${p.name}}`).join("");
lines.push(` scope: { id: \`${getEndpointName(endpoint)}${scopePathParamInterpolations}\` },`);
}
lines.push(" ...options,");
if (hasMutationDefaultOnError) {
lines.push(" onError: options?.onError ?? queryConfig.onError,");
}
if (hasMutationEffects) {
lines.push(" onSuccess: async (resData, variables, onMutateResult, context) => {");
if (updateQueryEndpoints.length > 0) {
if (destructuredVariables.length > 0) {
lines.push(` const { ${destructuredVariables.join(", ")} } = variables;`);
const scopedPathParamNames = new Set(scopePathParams.map((p) => p.name));
const variablesDestructure = destructuredVariables.filter((v) => !scopedPathParamNames.has(v));
if (variablesDestructure.length > 0) {
lines.push(` const { ${variablesDestructure.join(", ")} } = variables;`);
}
lines.push(...renderWorkspaceParamCoalescing({ replacements: workspaceParamReplacements, indent: " " }));
lines.push(
Expand Down
17 changes: 11 additions & 6 deletions src/generators/generateCodeFromOpenAPIDoc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,29 +167,34 @@ describe("generateCodeFromOpenAPIDoc", () => {
const queries = files.find(({ fileName }) => fileName === "output/documents/documents.queries.ts")?.content;

expect(queries).toBeDefined();

// PUT with path params + body: path params become first arg, body stays in mutationFn, scope added
expect(queries).toContain(
"export const useUpdate = ({ userId, documentId }: { userId: string; documentId: string; }, options?: AppMutationOptions<typeof DocumentsApi.update, { data: DocumentsModels.UpdateDocumentBody, }>) => {",
"export const useUpdate = ({ userId, documentId }: { userId: string; documentId: string }, options?: AppMutationOptions<typeof DocumentsApi.update, { data: DocumentsModels.UpdateDocumentBody }>) => {",
);
expect(queries).toContain("mutationFn: ( { data } ) =>");
expect(queries).toContain("mutationFn: ({ data }) =>");
expect(queries).toContain("DocumentsApi.update(userId, documentId, data)");
expect(queries).toContain("scope: { id: `update:${userId}:${documentId}` }");

// DELETE with only path params: path params become first arg, mutationFn has no args, no TVariables type arg
expect(queries).toContain(
"export const useDeleteDocument = ({ userId, documentId }: { userId: string; documentId: string; }, options?: AppMutationOptions<typeof DocumentsApi.deleteDocument>) => {",
"export const useDeleteDocument = ({ userId, documentId }: { userId: string; documentId: string }, options?: AppMutationOptions<typeof DocumentsApi.deleteDocument>) => {",
);
expect(queries).toContain("mutationFn: () =>");
expect(queries).toContain("DocumentsApi.deleteDocument(userId, documentId)");
expect(queries).toContain("scope: { id: `deleteDocument:${userId}:${documentId}` }");

// POST without path params: no scope, no API change
expect(queries).toContain(
"export const useCreate = (options?: AppMutationOptions<typeof DocumentsApi.create, { data: DocumentsModels.UpdateDocumentBody, }>) => {",
"export const useCreate = (options?: AppMutationOptions<typeof DocumentsApi.create, { data: DocumentsModels.UpdateDocumentBody }>) => {",
);
expect(queries).not.toContain("scope: { id: `create");

// POST with path params + media upload: POST is excluded from scoping, no API change
expect(queries).toContain(
"export const useUploadAvatar = (options?: AppMutationOptions<typeof DocumentsApi.uploadAvatar, { userId: string, data: string, file?: File, abortController?: AbortController, onUploadProgress?: (progress: { loaded: number; total: number }) => void }>) => {",
"export const useUploadAvatar = (options?: AppMutationOptions<typeof DocumentsApi.uploadAvatar, { userId: string, data: string, file?: File;",
);
expect(queries).toContain("mutationFn: async ( { userId, data, file, abortController, onUploadProgress } ) =>");
expect(queries).toContain("mutationFn: async ({ userId, data, file, abortController, onUploadProgress }) => {");
expect(queries).toContain("DocumentsApi.uploadAvatar(userId, data)");
expect(queries).not.toContain("scope: { id: `uploadAvatar:${userId}` }");
});
Expand Down
23 changes: 0 additions & 23 deletions src/generators/templates/partials/query-use-infinite-query.hbs

This file was deleted.

55 changes: 0 additions & 55 deletions src/generators/templates/partials/query-use-mutation.hbs

This file was deleted.

17 changes: 0 additions & 17 deletions src/generators/templates/partials/query-use-query.hbs

This file was deleted.

124 changes: 0 additions & 124 deletions src/generators/utils/hbs/hbs.endpoints.utils.ts

This file was deleted.

Loading