From 458ebdb427e2b8b059df69dbbc223ce8e575a1d5 Mon Sep 17 00:00:00 2001
From: Alice Jonsson <10475857+AllieJonsson@users.noreply.github.com>
Date: Mon, 13 Jan 2025 09:06:19 +0100
Subject: [PATCH 1/3] fix(core): only generate one operation if it has multiple
tags
---
.../pages/reference/configuration/output.md | 30 ++++
packages/core/src/types.ts | 2 +
packages/core/src/writers/target-tags.ts | 134 +++++++++---------
tests/configs/default.config.ts | 15 ++
tests/specifications/multiple-tags.yaml | 84 +++++++++++
5 files changed, 198 insertions(+), 67 deletions(-)
create mode 100644 tests/specifications/multiple-tags.yaml
diff --git a/docs/src/pages/reference/configuration/output.md b/docs/src/pages/reference/configuration/output.md
index e30b8d8c1..6cddb6e31 100644
--- a/docs/src/pages/reference/configuration/output.md
+++ b/docs/src/pages/reference/configuration/output.md
@@ -1625,6 +1625,36 @@ Type: `Object`.
Exactly the same as the `override.operations` but this time you can do it by tags
+#### multiTagResolver
+
+Type: `Function`.
+
+```ts
+// type signature
+(tags: string[]) => string;
+```
+
+When using mode `tags` or `tags-split` and an operation have multiple tags, only one of them will be used when generating.
+This setting allows you to decide which tag to use. Default is the first tag.
+
+Example:
+
+```js
+module.exports = {
+ petstore: {
+ output: {
+ override: {
+ multiTagResolver: (tags) => {
+ const indexOfFavoriteTag = tags.include('myFavoriteTag');
+ if (indexOfFavoriteTag >= 0) return tags[indexOfFavoriteTag];
+ return tags[0];
+ },
+ },
+ },
+ },
+};
+```
+
#### operationName
Type: `Function`.
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 4792e49bb..69ee226a0 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -77,6 +77,7 @@ export type NormalizedOverrideOutput = {
mutator?: NormalizedMutator;
operations: { [key: string]: NormalizedOperationOptions };
tags: { [key: string]: NormalizedOperationOptions };
+ multiTagResolver?: (tags: string[]) => string;
mock?: OverrideMockOptions;
contentType?: OverrideOutputContentType;
header: false | ((info: InfoObject) => string[] | string);
@@ -360,6 +361,7 @@ export type OverrideOutput = {
mutator?: Mutator;
operations?: { [key: string]: OperationOptions };
tags?: { [key: string]: OperationOptions };
+ multiTagResolver?: (tags: string[]) => string;
mock?: OverrideMockOptions;
contentType?: OverrideOutputContentType;
header?: boolean | ((info: InfoObject) => string[] | string);
diff --git a/packages/core/src/writers/target-tags.ts b/packages/core/src/writers/target-tags.ts
index 62f10afff..ee650af8e 100644
--- a/packages/core/src/writers/target-tags.ts
+++ b/packages/core/src/writers/target-tags.ts
@@ -16,79 +16,79 @@ const addDefaultTagIfEmpty = (operation: GeneratorOperation) => ({
const generateTargetTags = (
currentAcc: { [key: string]: GeneratorTargetFull },
operation: GeneratorOperation,
+ options: NormalizedOutputOptions,
): {
[key: string]: GeneratorTargetFull;
} => {
- return operation.tags.map(kebab).reduce((acc, tag) => {
- const currentOperation = acc[tag];
-
- if (!currentOperation) {
- acc[tag] = {
- imports: operation.imports,
- importsMock: operation.importsMock,
- mutators: operation.mutator ? [operation.mutator] : [],
- clientMutators: operation.clientMutators ?? [],
- formData: operation.formData ? [operation.formData] : [],
- formUrlEncoded: operation.formUrlEncoded
- ? [operation.formUrlEncoded]
- : [],
- paramsSerializer: operation.paramsSerializer
- ? [operation.paramsSerializer]
- : [],
- implementation: operation.implementation,
- implementationMock: {
- function: operation.implementationMock.function,
- handler: operation.implementationMock.handler,
- handlerName: ' ' + operation.implementationMock.handlerName + '()',
- },
- };
-
- return acc;
- }
-
- acc[tag] = {
- implementation:
- currentOperation.implementation + operation.implementation,
- imports: [...currentOperation.imports, ...operation.imports],
- importsMock: [...currentOperation.importsMock, ...operation.importsMock],
- implementationMock: {
- function:
- currentOperation.implementationMock.function +
- operation.implementationMock.function,
- handler:
- currentOperation.implementationMock.handler +
- operation.implementationMock.handler,
- handlerName:
- currentOperation.implementationMock.handlerName +
- ',\n ' +
- operation.implementationMock.handlerName +
- '()',
- },
- mutators: operation.mutator
- ? [...(currentOperation.mutators ?? []), operation.mutator]
- : currentOperation.mutators,
- clientMutators: operation.clientMutators
- ? [
- ...(currentOperation.clientMutators ?? []),
- ...operation.clientMutators,
- ]
- : currentOperation.clientMutators,
- formData: operation.formData
- ? [...(currentOperation.formData ?? []), operation.formData]
- : currentOperation.formData,
+ const tag = options.override.multiTagResolver
+ ? options.override.multiTagResolver(operation.tags)
+ : operation.tags[0];
+ const currentOperation = currentAcc[tag];
+
+ if (!currentOperation) {
+ currentAcc[tag] = {
+ imports: operation.imports,
+ importsMock: operation.importsMock,
+ mutators: operation.mutator ? [operation.mutator] : [],
+ clientMutators: operation.clientMutators ?? [],
+ formData: operation.formData ? [operation.formData] : [],
formUrlEncoded: operation.formUrlEncoded
- ? [...(currentOperation.formUrlEncoded ?? []), operation.formUrlEncoded]
- : currentOperation.formUrlEncoded,
+ ? [operation.formUrlEncoded]
+ : [],
paramsSerializer: operation.paramsSerializer
- ? [
- ...(currentOperation.paramsSerializer ?? []),
- operation.paramsSerializer,
- ]
- : currentOperation.paramsSerializer,
+ ? [operation.paramsSerializer]
+ : [],
+ implementation: operation.implementation,
+ implementationMock: {
+ function: operation.implementationMock.function,
+ handler: operation.implementationMock.handler,
+ handlerName: ' ' + operation.implementationMock.handlerName + '()',
+ },
};
- return acc;
- }, currentAcc);
+ return currentAcc;
+ }
+
+ currentAcc[tag] = {
+ implementation: currentOperation.implementation + operation.implementation,
+ imports: [...currentOperation.imports, ...operation.imports],
+ importsMock: [...currentOperation.importsMock, ...operation.importsMock],
+ implementationMock: {
+ function:
+ currentOperation.implementationMock.function +
+ operation.implementationMock.function,
+ handler:
+ currentOperation.implementationMock.handler +
+ operation.implementationMock.handler,
+ handlerName:
+ currentOperation.implementationMock.handlerName +
+ ',\n ' +
+ operation.implementationMock.handlerName +
+ '()',
+ },
+ mutators: operation.mutator
+ ? [...(currentOperation.mutators ?? []), operation.mutator]
+ : currentOperation.mutators,
+ clientMutators: operation.clientMutators
+ ? [
+ ...(currentOperation.clientMutators ?? []),
+ ...operation.clientMutators,
+ ]
+ : currentOperation.clientMutators,
+ formData: operation.formData
+ ? [...(currentOperation.formData ?? []), operation.formData]
+ : currentOperation.formData,
+ formUrlEncoded: operation.formUrlEncoded
+ ? [...(currentOperation.formUrlEncoded ?? []), operation.formUrlEncoded]
+ : currentOperation.formUrlEncoded,
+ paramsSerializer: operation.paramsSerializer
+ ? [
+ ...(currentOperation.paramsSerializer ?? []),
+ operation.paramsSerializer,
+ ]
+ : currentOperation.paramsSerializer,
+ };
+ return currentAcc;
};
export const generateTargetForTags = (
@@ -101,7 +101,7 @@ export const generateTargetForTags = (
.map(addDefaultTagIfEmpty)
.reduce(
(acc, operation, index, arr) => {
- const targetTags = generateTargetTags(acc, operation);
+ const targetTags = generateTargetTags(acc, operation, options);
if (index === arr.length - 1) {
return Object.entries(targetTags).reduce<
diff --git a/tests/configs/default.config.ts b/tests/configs/default.config.ts
index d233eda9f..1dae76adc 100644
--- a/tests/configs/default.config.ts
+++ b/tests/configs/default.config.ts
@@ -209,4 +209,19 @@ export default defineConfig({
target: '../specifications/petstore.yaml',
},
},
+ multipleTags: {
+ output: {
+ target: '../generated/default/multiple-tags/endpoints.ts',
+ schemas: '../generated/default/multiple-tags/model',
+ mode: 'tags',
+ override: {
+ multiTagResolver: (tags) => {
+ return tags[tags.length - 1];
+ },
+ },
+ },
+ input: {
+ target: '../specifications/multiple-tags.yaml',
+ },
+ },
});
diff --git a/tests/specifications/multiple-tags.yaml b/tests/specifications/multiple-tags.yaml
new file mode 100644
index 000000000..4fdf607f8
--- /dev/null
+++ b/tests/specifications/multiple-tags.yaml
@@ -0,0 +1,84 @@
+openapi: '3.0.0'
+info:
+ version: 1.0.0
+ title: Swagger Petstore
+ license:
+ name: MIT
+servers:
+ - url: http://petstore.swagger.io/v1
+paths:
+ /pets:
+ get:
+ summary: List all pets
+ operationId: listPets
+ tags:
+ - dogs
+ - cats
+ responses:
+ '200':
+ description: A paged array of pets
+ content:
+ application/hal+json:
+ schema:
+ $ref: '#/components/schemas/Pets'
+ /pets/dog/{dogId}:
+ get:
+ summary: Info for a specific dog
+ operationId: showDogById
+ tags:
+ - dog
+ parameters:
+ - name: dogId
+ in: path
+ required: true
+ description: The id of the dog to retrieve
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Expected response to a valid request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Dog'
+ /pets/cat/{catId}:
+ get:
+ summary: Info for a specific cat
+ operationId: showCatById
+ tags:
+ - cat
+ parameters:
+ - name: catId
+ in: path
+ required: true
+ description: The id of the cat to retrieve
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Expected response to a valid request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Cat'
+components:
+ schemas:
+ Pet:
+ type: object
+ oneOf:
+ - $ref: '#/components/schemas/Dog'
+ - $ref: '#/components/schemas/Cat'
+ Dog:
+ type: object
+ properties:
+ barksPerMinute:
+ type: integer
+ Cat:
+ type: object
+ properties:
+ petsRequested:
+ type: integer
+ Pets:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
From 4ca87ec07d4314de17792ea922540592cf948401 Mon Sep 17 00:00:00 2001
From: Alice Jonsson <10475857+AllieJonsson@users.noreply.github.com>
Date: Mon, 13 Jan 2025 10:24:41 +0100
Subject: [PATCH 2/3] Update tests/specifications/multiple-tags.yaml
Co-authored-by: Shodai Suzuki
---
tests/specifications/multiple-tags.yaml | 40 -------------------------
1 file changed, 40 deletions(-)
diff --git a/tests/specifications/multiple-tags.yaml b/tests/specifications/multiple-tags.yaml
index 4fdf607f8..f38f8da69 100644
--- a/tests/specifications/multiple-tags.yaml
+++ b/tests/specifications/multiple-tags.yaml
@@ -21,46 +21,6 @@ paths:
application/hal+json:
schema:
$ref: '#/components/schemas/Pets'
- /pets/dog/{dogId}:
- get:
- summary: Info for a specific dog
- operationId: showDogById
- tags:
- - dog
- parameters:
- - name: dogId
- in: path
- required: true
- description: The id of the dog to retrieve
- schema:
- type: string
- responses:
- '200':
- description: Expected response to a valid request
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Dog'
- /pets/cat/{catId}:
- get:
- summary: Info for a specific cat
- operationId: showCatById
- tags:
- - cat
- parameters:
- - name: catId
- in: path
- required: true
- description: The id of the cat to retrieve
- schema:
- type: string
- responses:
- '200':
- description: Expected response to a valid request
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Cat'
components:
schemas:
Pet:
From 976deaa3e7b29e2cd17be2fc223e4f36bb127526 Mon Sep 17 00:00:00 2001
From: Alice Jonsson <10475857+AllieJonsson@users.noreply.github.com>
Date: Mon, 13 Jan 2025 11:19:58 +0100
Subject: [PATCH 3/3] fix: kebab name and remove setting. trim spec
---
.../pages/reference/configuration/output.md | 30 -------------------
packages/core/src/types.ts | 2 --
packages/core/src/writers/target-tags.ts | 4 +--
tests/configs/default.config.ts | 5 ----
tests/specifications/multiple-tags.yaml | 19 ++----------
5 files changed, 3 insertions(+), 57 deletions(-)
diff --git a/docs/src/pages/reference/configuration/output.md b/docs/src/pages/reference/configuration/output.md
index 6cddb6e31..e30b8d8c1 100644
--- a/docs/src/pages/reference/configuration/output.md
+++ b/docs/src/pages/reference/configuration/output.md
@@ -1625,36 +1625,6 @@ Type: `Object`.
Exactly the same as the `override.operations` but this time you can do it by tags
-#### multiTagResolver
-
-Type: `Function`.
-
-```ts
-// type signature
-(tags: string[]) => string;
-```
-
-When using mode `tags` or `tags-split` and an operation have multiple tags, only one of them will be used when generating.
-This setting allows you to decide which tag to use. Default is the first tag.
-
-Example:
-
-```js
-module.exports = {
- petstore: {
- output: {
- override: {
- multiTagResolver: (tags) => {
- const indexOfFavoriteTag = tags.include('myFavoriteTag');
- if (indexOfFavoriteTag >= 0) return tags[indexOfFavoriteTag];
- return tags[0];
- },
- },
- },
- },
-};
-```
-
#### operationName
Type: `Function`.
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 69ee226a0..4792e49bb 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -77,7 +77,6 @@ export type NormalizedOverrideOutput = {
mutator?: NormalizedMutator;
operations: { [key: string]: NormalizedOperationOptions };
tags: { [key: string]: NormalizedOperationOptions };
- multiTagResolver?: (tags: string[]) => string;
mock?: OverrideMockOptions;
contentType?: OverrideOutputContentType;
header: false | ((info: InfoObject) => string[] | string);
@@ -361,7 +360,6 @@ export type OverrideOutput = {
mutator?: Mutator;
operations?: { [key: string]: OperationOptions };
tags?: { [key: string]: OperationOptions };
- multiTagResolver?: (tags: string[]) => string;
mock?: OverrideMockOptions;
contentType?: OverrideOutputContentType;
header?: boolean | ((info: InfoObject) => string[] | string);
diff --git a/packages/core/src/writers/target-tags.ts b/packages/core/src/writers/target-tags.ts
index ee650af8e..a7ce5852e 100644
--- a/packages/core/src/writers/target-tags.ts
+++ b/packages/core/src/writers/target-tags.ts
@@ -20,9 +20,7 @@ const generateTargetTags = (
): {
[key: string]: GeneratorTargetFull;
} => {
- const tag = options.override.multiTagResolver
- ? options.override.multiTagResolver(operation.tags)
- : operation.tags[0];
+ const tag = kebab(operation.tags[0]);
const currentOperation = currentAcc[tag];
if (!currentOperation) {
diff --git a/tests/configs/default.config.ts b/tests/configs/default.config.ts
index 1dae76adc..921feb362 100644
--- a/tests/configs/default.config.ts
+++ b/tests/configs/default.config.ts
@@ -214,11 +214,6 @@ export default defineConfig({
target: '../generated/default/multiple-tags/endpoints.ts',
schemas: '../generated/default/multiple-tags/model',
mode: 'tags',
- override: {
- multiTagResolver: (tags) => {
- return tags[tags.length - 1];
- },
- },
},
input: {
target: '../specifications/multiple-tags.yaml',
diff --git a/tests/specifications/multiple-tags.yaml b/tests/specifications/multiple-tags.yaml
index f38f8da69..0968fc92a 100644
--- a/tests/specifications/multiple-tags.yaml
+++ b/tests/specifications/multiple-tags.yaml
@@ -12,8 +12,8 @@ paths:
summary: List all pets
operationId: listPets
tags:
- - dogs
- cats
+ - dogs
responses:
'200':
description: A paged array of pets
@@ -23,22 +23,7 @@ paths:
$ref: '#/components/schemas/Pets'
components:
schemas:
- Pet:
- type: object
- oneOf:
- - $ref: '#/components/schemas/Dog'
- - $ref: '#/components/schemas/Cat'
- Dog:
- type: object
- properties:
- barksPerMinute:
- type: integer
- Cat:
- type: object
- properties:
- petsRequested:
- type: integer
Pets:
type: array
items:
- $ref: '#/components/schemas/Pet'
+ type: object