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
44 changes: 43 additions & 1 deletion docs/content/docs/reference/configuration/output.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2216,7 +2216,9 @@ Override by OpenAPI tag (same options as `operations`).

**Type:** `Function`

Custom function to override generated operation names:
Custom function to override generated operation names.

**Return `string`** to control both the method name and the type-name base together:

```ts title="orval.config.ts"
export default defineConfig({
Expand All @@ -2232,6 +2234,46 @@ export default defineConfig({
});
```

**Return `[methodName, typeNameBase]`** to decouple method names from type-identifier names. This is useful for gateway-aggregated specs where multiple services share the same REST patterns (`GET /products`, `GET /orders`) — bare method names are safe per-tag (each service class scopes them), but type names (`*Params`, `*Body`, `*Error`, `*Result`) need to be globally unique to avoid barrel-level collisions with `tags-split` + `splitByTags` + `indexFiles`:

```ts title="orval.config.ts"
import { pascal } from '@orval/core';

export default defineConfig({
api: {
output: {
mode: 'tags-split',
schemas: { path: './model', splitByTags: true },
override: {
operationName: (_operation, route, verb) => {
const segments = route.split('/').filter(Boolean);
return [
`${verb}${pascal(segments.slice(2).join('-'))}`, // getProducts
`${verb}${pascal(segments.slice(1).join('-'))}`, // getCatalogProducts
];
},
},
},
},
});
```

Result:

```ts
// catalog/catalog.service.ts
class CatalogService {
getProducts = (params: GetCatalogProductsParams) => ...;
}

// inventory/inventory.service.ts
class InventoryService {
getProducts = (params: GetInventoryProductsParams) => ...;
}
```

The first element controls the function/hook name. The second controls the base for all operation-specific TypeScript type identifiers (`*Params`, `*Body`, `*Error`, `*Result`, `*Accept`, zod/hono/effect schema names).

---

## override.components
Expand Down
17 changes: 17 additions & 0 deletions packages/angular/src/http-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const createVerbOption = (
({
operationId: 'getPetById',
operationName: 'getPetById',
typeName: 'getPetById',
verb: 'get',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -554,6 +555,7 @@ describe('angular HttpClient generator', () => {
it('still emits the shared helper when at least one operation lacks paramsFilter', () => {
const verbWithFilter = createVerbOption({
operationName: 'a',
typeName: 'a',
queryParams: createQueryParams({
schema: { name: 'AParams', model: '', imports: [] },
}),
Expand All @@ -570,6 +572,7 @@ describe('angular HttpClient generator', () => {
});
const verbWithoutFilter = createVerbOption({
operationName: 'b',
typeName: 'b',
queryParams: createQueryParams({
schema: { name: 'BParams', model: '', imports: [] },
}),
Expand Down Expand Up @@ -871,6 +874,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'createPet',
operationName: 'createPet',
typeName: 'createPet',
verb: 'post',
route: '/pets',
pathRoute: '/pets',
Expand Down Expand Up @@ -912,6 +916,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'updatePet',
operationName: 'updatePet',
typeName: 'updatePet',
verb: 'put',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -961,6 +966,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'deletePet',
operationName: 'deletePet',
typeName: 'deletePet',
verb: 'delete',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -1032,6 +1038,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'authenticate',
operationName: 'authenticate',
typeName: 'authenticate',
verb: 'post',
route: '/api/auth',
pathRoute: '/api/auth',
Expand Down Expand Up @@ -1126,6 +1133,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'getPetFile',
operationName: 'getPetFile',
typeName: 'getPetFile',
response: baseResponse({
definition: { success: 'Pet | string', errors: 'Error' },
types: {
Expand Down Expand Up @@ -1159,6 +1167,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'updatePet',
operationName: 'updatePet',
typeName: 'updatePet',
verb: 'put',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -1220,6 +1229,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'confirmReservation',
operationName: 'confirmReservation',
typeName: 'confirmReservation',
verb: 'post',
route: '/reservations/${token}/confirm',
pathRoute: '/reservations/{token}/confirm',
Expand Down Expand Up @@ -1304,6 +1314,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'updatePet',
operationName: 'updatePet',
typeName: 'updatePet',
verb: 'put',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -1372,6 +1383,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'listPets',
operationName: 'listPets',
typeName: 'listPets',
route: '/pets',
pathRoute: '/pets',
params: [],
Expand Down Expand Up @@ -1421,6 +1433,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'deletePet',
operationName: 'deletePet',
typeName: 'deletePet',
verb: 'delete',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down Expand Up @@ -1481,6 +1494,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'listPets',
operationName: 'listPets',
typeName: 'listPets',
route: '/pets',
pathRoute: '/pets',
params: [],
Expand Down Expand Up @@ -1708,6 +1722,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'createPet',
operationName: 'createPet',
typeName: 'createPet',
verb: 'post',
route: '/pets',
pathRoute: '/pets',
Expand Down Expand Up @@ -1828,6 +1843,7 @@ describe('angular HttpClient generator', () => {
getPetFile: createVerbOption({
operationId: 'getPetFile',
operationName: 'getPetFile',
typeName: 'getPetFile',
response: baseResponse({
definition: { success: 'Pet | string', errors: 'Error' },
types: {
Expand Down Expand Up @@ -1910,6 +1926,7 @@ describe('angular HttpClient generator', () => {
const verbOption = createVerbOption({
operationId: 'listPets',
operationName: 'listPets',
typeName: 'listPets',
route: '/pets',
pathRoute: '/pets',
params: [],
Expand Down
17 changes: 8 additions & 9 deletions packages/angular/src/http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ export const getAngularDependencies: ClientDependenciesBuilder = () => [
*
* @returns A PascalCase helper type/const name for the operation's `Accept` values.
*/
export const getAcceptHelperName = (operationName: string) =>
`${pascal(operationName)}Accept`;
export const getAcceptHelperName = (typeName: string) =>
`${pascal(typeName)}Accept`;

/**
* Collects the distinct successful response content types for a single
Expand All @@ -155,11 +155,11 @@ const toAcceptHelperKey = (contentType: string): string =>
.toLowerCase();

const buildAcceptHelper = (
operationName: string,
typeName: string,
contentTypes: string[],
output: ContextSpec['output'],
): string => {
const acceptHelperName = getAcceptHelperName(operationName);
const acceptHelperName = getAcceptHelperName(typeName);
const unionValue = contentTypes
.map((contentType) => `'${contentType}'`)
.join(' | ');
Expand Down Expand Up @@ -200,9 +200,7 @@ export const buildAcceptHelpers = (
);
if (contentTypes.length <= 1) return [];

return [
buildAcceptHelper(verbOption.operationName, contentTypes, output),
];
return [buildAcceptHelper(verbOption.typeName, contentTypes, output)];
})
.join('\n\n');

Expand Down Expand Up @@ -306,6 +304,7 @@ export const generateHttpClientImplementation = (
headers,
queryParams,
operationName,
typeName,
response,
mutator,
body,
Expand Down Expand Up @@ -404,7 +403,7 @@ export const generateHttpClientImplementation = (
returnTypesRegistry.set(
operationName,
`export type ${pascal(
operationName,
typeName,
)}ClientResult = NonNullable<${resultAliasType}>`,
);

Expand Down Expand Up @@ -476,7 +475,7 @@ export const generateHttpClientImplementation = (
const uniqueContentTypes = getUniqueContentTypes(successTypes);
const hasMultipleContentTypes = uniqueContentTypes.length > 1;
const acceptTypeName = hasMultipleContentTypes
? getAcceptHelperName(operationName)
? getAcceptHelperName(typeName)
: undefined;

const needsObserveBranching = isRequestOptions && !hasMultipleContentTypes;
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/http-resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ const createVerbOption = (
return {
operationId: 'getPetById',
operationName: 'getPetById',
typeName: overrides.operationName ?? 'getPetById',
verb: 'get',
route: '/pets/${petId}',
pathRoute: '/pets/{petId}',
Expand Down
7 changes: 4 additions & 3 deletions packages/angular/src/http-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,8 @@ const buildHttpResourceFunction = (
route: string,
output: NormalizedOutputOptions,
): string => {
const { operationName, response, props, params, mutator } = verbOption;
const { operationName, typeName, response, props, params, mutator } =
verbOption;

const dataType = response.definition.success || 'unknown';
const omitParse = isZodSchemaOutput(output);
Expand Down Expand Up @@ -866,7 +867,7 @@ const buildHttpResourceFunction = (
resourceReturnTypesRegistry.set(
operationName,
`export type ${pascal(
operationName,
typeName,
)}ResourceResult = NonNullable<${overallReturnType}>`,
);
const uniqueContentTypes = getUniqueContentTypes(successTypes);
Expand Down Expand Up @@ -905,7 +906,7 @@ const buildHttpResourceFunction = (

if (uniqueContentTypes.length > 1) {
const defaultContentType = jsonContentType ?? defaultSuccess.contentType;
const acceptTypeName = getAcceptHelperName(operationName);
const acceptTypeName = getAcceptHelperName(typeName);
const requiredProps = signalProps.filter(
(_, index) => props[index]?.required && !props[index]?.default,
);
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ const makeVerb = (operationId: string, tags: string[]): GeneratorVerbOptions =>
({
operationId,
operationName: operationId,
typeName: operationId,
verb: 'get' as Verbs,
route: `/api/${operationId}`,
pathRoute: `/api/${operationId}`,
Expand Down
7 changes: 3 additions & 4 deletions packages/axios/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const generateAxiosImplementation = (
headers,
queryParams,
operationName,
typeName,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
response,
mutator,
body,
Expand Down Expand Up @@ -146,9 +147,7 @@ const generateAxiosImplementation = (
: '';

const returnType = (title?: string) =>
`export type ${pascal(
operationName,
)}Result = NonNullable<Awaited<ReturnType<${
`export type ${pascal(typeName)}Result = NonNullable<Awaited<ReturnType<${
title
? `ReturnType<typeof ${title}>['${operationName}']`
: `typeof ${operationName}`
Expand Down Expand Up @@ -194,7 +193,7 @@ const generateAxiosImplementation = (
});

const returnType = () =>
`export type ${pascal(operationName)}Result = AxiosResponse<${
`export type ${pascal(typeName)}Result = AxiosResponse<${
response.definition.success || 'unknown'
}>`;

Expand Down
Loading
Loading