Skip to content
Merged
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
117 changes: 117 additions & 0 deletions tests/__snapshots__/vue-query/issue-1026/endpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Generated by orval v8.11.0 🍺
* Do not edit manually.
* Issue 1026 - Vue Query header parameters
* OpenAPI spec version: 1.0.0
*/
import { useQuery } from '@tanstack/vue-query';
import type {
DataTag,
QueryClient,
QueryFunction,
QueryKey,
UseQueryOptions,
UseQueryReturnType,
} from '@tanstack/vue-query';

import axios from 'axios';
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';

import { unref } from 'vue';
import type { MaybeRef } from 'vue';

import type { GetSomeEndpointHeaders, SomeEndpointResult } from './model';

/**
* @summary Get something with header parameters
*/
export const getSomeEndpoint = (
headers?: MaybeRef<GetSomeEndpointHeaders>,
options?: AxiosRequestConfig,
): Promise<AxiosResponse<SomeEndpointResult>> => {
headers = unref(headers);

return axios.get(`/api/v1/someEndPoint`, {
...options,
headers: { ...headers, ...options?.headers },
});
};

export const getGetSomeEndpointQueryKey = () => {
return ['api', 'v1', 'someEndPoint'] as const;
};

export const getGetSomeEndpointQueryOptions = <
TData = Awaited<ReturnType<typeof getSomeEndpoint>>,
TError = AxiosError<unknown>,
>(
headers?: MaybeRef<GetSomeEndpointHeaders>,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getSomeEndpoint>>,
TError,
TData
>
>;
axios?: AxiosRequestConfig;
},
) => {
const { query: queryOptions, axios: axiosOptions } = options ?? {};

const queryKey = getGetSomeEndpointQueryKey();

const queryFn: QueryFunction<Awaited<ReturnType<typeof getSomeEndpoint>>> = ({
signal,
}) => getSomeEndpoint(headers, { signal, ...axiosOptions });

return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getSomeEndpoint>>,
TError,
TData
>;
};

export type GetSomeEndpointQueryResult = NonNullable<
Awaited<ReturnType<typeof getSomeEndpoint>>
>;
export type GetSomeEndpointQueryError = AxiosError<unknown>;

/**
* @summary Get something with header parameters
*/

export function useGetSomeEndpoint<
TData = Awaited<ReturnType<typeof getSomeEndpoint>>,
TError = AxiosError<unknown>,
>(
headers?: MaybeRef<GetSomeEndpointHeaders>,
options?: {
query?: Partial<
UseQueryOptions<
Awaited<ReturnType<typeof getSomeEndpoint>>,
TError,
TData
>
>;
axios?: AxiosRequestConfig;
},
queryClient?: QueryClient,
): UseQueryReturnType<TData, TError> & {
queryKey: DataTag<QueryKey, TData, TError>;
} {
const queryOptions = getGetSomeEndpointQueryOptions(headers, options);

const query = useQuery(queryOptions, queryClient) as UseQueryReturnType<
TData,
TError
> & { queryKey: DataTag<QueryKey, TData, TError> };

query.queryKey = unref(queryOptions).queryKey as DataTag<
QueryKey,
TData,
TError
>;

return query;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Generated by orval v8.11.0 🍺
* Do not edit manually.
* Issue 1026 - Vue Query header parameters
* OpenAPI spec version: 1.0.0
*/

export type GetSomeEndpointHeaders = {
'Language-Id'?: number;
'Country-Id'?: number;
TimeZone?: string;
};
9 changes: 9 additions & 0 deletions tests/__snapshots__/vue-query/issue-1026/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Generated by orval v8.11.0 🍺
* Do not edit manually.
* Issue 1026 - Vue Query header parameters
* OpenAPI spec version: 1.0.0
*/

export * from './getSomeEndpointHeaders';
export * from './someEndpointResult';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Generated by orval v8.11.0 🍺
* Do not edit manually.
* Issue 1026 - Vue Query header parameters
* OpenAPI spec version: 1.0.0
*/

export interface SomeEndpointResult {
value?: string;
}
33 changes: 33 additions & 0 deletions tests/api-generation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,36 @@ test('default issue-873 does not duplicate multi-tag operations across tag files
).not.toContain(marker);
}
});

test('vue-query issue-1026 keeps header params out of the query key getter', async () => {
// Regression for #1026: with `headers: true` the Vue Query key getter used to
// emit `headers = unref(headers);` even though `headers` is not one of its
// parameters, throwing `ReferenceError: headers is not defined` at runtime.
// The getter must never unref params (that would also break key reactivity);
// `headers` is only unref'd inside the HTTP function where it is a parameter.
// Keep this focused assertion alongside the snapshot so #1026 fails with a
// targeted message instead of a full-file snapshot diff.
const content = await readFile(
generated('vue-query', 'issue-1026', 'endpoints.ts'),
'utf8',
);

// Slice out the `getGetSomeEndpointQueryKey` declaration body.
const marker = 'export const getGetSomeEndpointQueryKey = (';
const start = content.indexOf(marker);
expect(start, `${marker} should be generated`).toBeGreaterThan(-1);
const end = content.indexOf('as const', start);
expect(end, `${marker} body should be terminated`).toBeGreaterThan(start);
const queryKeyFn = content.slice(start, end);
Comment on lines +193 to +199

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — I'm keeping the indexOf('as const') slicing here for consistency with the sibling react-query issue-708 test in this same file (lines 96-103), which already establishes exactly this pattern for the same kind of query-key-getter assertion. Switching only this test to a different slicing strategy would make the two diverge.

Also, this doesn't slice silently on missing as const: indexOf returns -1, and the expect(end, ...).toBeGreaterThan(start) guard right below fails with a clear "body should be terminated" message before the slice is used. If the slicing approach should change, it'd be better done for both tests together, which is out of scope for this regression-test PR.


// The getter must not reference `headers` as an identifier: that was the
// #1026 bug (`headers = unref(headers);`) and unref-ing a param would also
// break query-key reactivity. A word-boundary regex keeps the intent precise
// rather than matching `headers` as a loose substring.
expect(queryKeyFn).not.toMatch(/\bheaders\b/);

// Sanity check: the HTTP function still receives and unrefs `headers`, so the
// assertion above is not passing simply because headers support is missing.
expect(content).toContain('headers?: MaybeRef<GetSomeEndpointHeaders>');
expect(content).toContain('headers = unref(headers);');
});
15 changes: 15 additions & 0 deletions tests/configs/vue-query.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ export default defineConfig({
},
input: { target: '../specifications/petstore.yaml' },
},
issue1026: {
output: {
target: '../generated/vue-query/issue-1026/endpoints.ts',
schemas: '../generated/vue-query/issue-1026/model',
client: 'vue-query',
httpClient: 'axios',
mode: 'split',
headers: true,
clean: true,
formatter: 'prettier',
},
input: {
target: '../specifications/issue-1026.yaml',
},
},
// Unsupported for now, see for context: https://github.com/orval-labs/orval/pull/931#issuecomment-1752355686
// namedParameters: {
// output: {
Expand Down
41 changes: 41 additions & 0 deletions tests/specifications/issue-1026.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
openapi: 3.0.0
info:
title: Issue 1026 - Vue Query header parameters
version: 1.0.0
paths:
/api/v1/someEndPoint:
get:
operationId: getSomeEndpoint
tags:
- things
summary: Get something with header parameters
parameters:
- name: Language-Id
in: header
style: simple
schema:
type: integer
- name: Country-Id
in: header
style: simple
schema:
type: integer
- name: TimeZone
in: header
style: simple
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SomeEndpointResult'
components:
schemas:
SomeEndpointResult:
type: object
properties:
value:
type: string
Loading