From 460d543a89aacc910e6def95dee8c1249cc07d88 Mon Sep 17 00:00:00 2001 From: "Badr.NassLahsen" Date: Sun, 31 Mar 2024 13:50:00 +0200 Subject: [PATCH] =?UTF-8?q?Improve=20support=20for=20externalizing=20strin?= =?UTF-8?q?gs=20in=20generated=20openapi=20schema=20via=E2=80=A6=20#2418?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customizers/SpecPropertiesCustomizer.java | 123 +++++++++++++----- .../api/v30/app212/HelloController.java | 26 +++- .../src/test/resources/application-212.yml | 18 +-- .../results/3.0.1/app212-grouped.json | 70 +++++++++- .../test/resources/results/3.0.1/app212.json | 71 +++++++++- 5 files changed, 253 insertions(+), 55 deletions(-) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java index e3632e7a7..f87f36eca 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecPropertiesCustomizer.java @@ -33,6 +33,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.PathItem.HttpMethod; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; @@ -84,12 +85,25 @@ */ public class SpecPropertiesCustomizer implements GlobalOpenApiCustomizer { + /** + * The Open api properties. + */ private final OpenAPI openApiProperties; + /** + * Instantiates a new Spec properties customizer. + * + * @param springDocConfigProperties the spring doc config properties + */ public SpecPropertiesCustomizer(SpringDocConfigProperties springDocConfigProperties) { this.openApiProperties = springDocConfigProperties.getOpenApi(); } + /** + * Instantiates a new Spec properties customizer. + * + * @param openApiProperties the open api properties + */ public SpecPropertiesCustomizer(OpenAPI openApiProperties) { this.openApiProperties = openApiProperties; } @@ -99,6 +113,12 @@ public void customise(OpenAPI openApi) { customizeOpenApi(openApi, openApiProperties); } + /** + * Customize open api. + * + * @param openApi the open api + * @param openApiProperties the open api properties + */ private void customizeOpenApi(OpenAPI openApi, OpenAPI openApiProperties) { if (openApiProperties != null) { Info infoProperties = openApiProperties.getInfo(); @@ -108,12 +128,19 @@ private void customizeOpenApi(OpenAPI openApi, OpenAPI openApiProperties) { Components componentsProperties = openApiProperties.getComponents(); if (componentsProperties != null) customizeComponents(openApi, componentsProperties); + Paths pathsProperties = openApiProperties.getPaths(); if (pathsProperties != null) customizePaths(openApi, pathsProperties); } } + /** + * Customize paths. + * + * @param openApi the open api + * @param pathsProperties the paths properties + */ private void customizePaths(OpenAPI openApi, Paths pathsProperties) { Paths paths = openApi.getPaths(); if (paths == null) { @@ -126,20 +153,30 @@ private void customizePaths(OpenAPI openApi, Paths pathsProperties) { } PathItem pathItemProperties = pathsProperties.get(path); if (pathItemProperties != null) { - resolveString(pathItem::setDescription, pathItemProperties::getDescription); - resolveString(pathItem::setSummary, pathItemProperties::getSummary); - List operations = pathItem.readOperations(); - List operationsProperties = pathItemProperties.readOperations(); - for (int i = 0; i < operations.size(); i++) { - Operation operation = operations.get(i); - Operation operationProperties = operationsProperties.get(i); - resolveString(operation::setDescription, operationProperties::getDescription); - resolveString(operation::setSummary, operationProperties::getSummary); - } + resolveString(pathItem::description, pathItemProperties::getDescription); + resolveString(pathItem::summary, pathItemProperties::getSummary); + + Map operationMap = pathItem.readOperationsMap(); + Map operationMapProperties = pathItemProperties.readOperationsMap(); + + operationMapProperties.forEach((httpMethod, operationProperties) -> { + Operation operationToCustomize = operationMap.get(httpMethod); + if (operationToCustomize != null) { + resolveString(operationToCustomize::description, operationProperties::getDescription); + resolveString(operationToCustomize::summary, operationProperties::getSummary); + resolveSet(operationToCustomize::tags, operationProperties::getTags); + } + }); }}); } } - + + /** + * Customize components. + * + * @param openApi the open api + * @param componentsProperties the components properties + */ private void customizeComponents(OpenAPI openApi, Components componentsProperties) { Components components = openApi.getComponents(); if (components == null || CollectionUtils.isEmpty(components.getSchemas())) { @@ -158,8 +195,9 @@ private void customizeComponents(OpenAPI openApi, Components componentsPropertie properties.forEach((propKey, propSchema) -> { Schema propSchemaProperties = (Schema) schemaProperties.getProperties().get(propKey); if (propSchemaProperties != null) { - resolveString(propSchema::setDescription, propSchemaProperties::getDescription); - resolveString(propSchema::setExample, propSchemaProperties::getExample); + resolveString(propSchema::description, propSchemaProperties::getDescription); + resolveString(propSchema::title, propSchemaProperties::getTitle); + resolveString(propSchema::example, propSchemaProperties::getExample); } }); } @@ -167,6 +205,12 @@ private void customizeComponents(OpenAPI openApi, Components componentsPropertie } } + /** + * Customize info. + * + * @param openApi the open api + * @param infoProperties the info properties + */ private void customizeInfo(OpenAPI openApi, Info infoProperties) { Info info = openApi.getInfo(); if (info != null) { @@ -174,28 +218,29 @@ private void customizeInfo(OpenAPI openApi, Info infoProperties) { resolveString(info::description, infoProperties::getDescription); resolveString(info::version, infoProperties::getVersion); resolveString(info::termsOfService, infoProperties::getTermsOfService); - } - else - openApi.info(infoProperties); + resolveString(info::summary, infoProperties::getSummary); - License license = info.getLicense(); - License licenseProperties = infoProperties.getLicense(); - if (license != null) { - resolveString(license::name, licenseProperties::getName); - resolveString(license::url, licenseProperties::getUrl); - } - else - info.license(licenseProperties); - - Contact contact = info.getContact(); - Contact contactProperties = infoProperties.getContact(); - if (contact != null) { - resolveString(contact::name, contactProperties::getName); - resolveString(contact::email, contactProperties::getEmail); - resolveString(contact::url, contactProperties::getUrl); + License license = info.getLicense(); + License licenseProperties = infoProperties.getLicense(); + if (license != null) { + resolveString(license::name, licenseProperties::getName); + resolveString(license::url, licenseProperties::getUrl); + } + else + info.license(licenseProperties); + + Contact contact = info.getContact(); + Contact contactProperties = infoProperties.getContact(); + if (contact != null) { + resolveString(contact::name, contactProperties::getName); + resolveString(contact::email, contactProperties::getEmail); + resolveString(contact::url, contactProperties::getUrl); + } + else + info.contact(contactProperties); } else - info.contact(contactProperties); + openApi.info(infoProperties); } @@ -212,4 +257,18 @@ private void resolveString(Consumer setter, Supplier getter) { } } + /** + * Resolve set. + * + * @param setter the setter + * @param getter the getter + */ + private void resolveSet(Consumer setter, Supplier getter) { + List value = getter.get(); + if (!CollectionUtils.isEmpty(value)) { + setter.accept(value); + } + } + + } diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app212/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app212/HelloController.java index ac920cd55..582d8a75a 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app212/HelloController.java +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app212/HelloController.java @@ -18,15 +18,33 @@ package test.org.springdoc.api.v30.app212; +import test.org.springdoc.api.v30.app217.PersonDTO; + import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { - @GetMapping(value = "/persons") - public PersonDTO persons() { - return new PersonDTO("John"); - } + @GetMapping(value = "/persons1") + public PersonDTO persons1() { + return new PersonDTO("John"); + } + + @GetMapping(value = "/persons2") + public PersonDTO persons2() { + return new PersonDTO("John"); + } + + @GetMapping(value = "/persons3") + public PersonDTO persons3() { + return new PersonDTO("John"); + } + + @PostMapping(value = "/persons3") + public PersonDTO persons33() { + return new PersonDTO("John"); + } } diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/application-212.yml b/springdoc-openapi-starter-webmvc-api/src/test/resources/application-212.yml index 304c67039..7d7e706dc 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/application-212.yml +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/application-212.yml @@ -13,13 +13,18 @@ springdoc: description: Description for 'name' property example: Example value for 'name' property paths: - /persons: + /persons3: + post: + tags: + - hello + summary: Summary of Post operationId 'persons' + description: Description of Post operationId 'persons' get: tags: - - hello-controller - operationId: persons - summary: Summary of operationId 'persons' - description: Description of operationId 'persons' + - hello + summary: Summary of Get operationId 'persons' + description: Description of Get operationId 'persons' + group-configs: - group: apiGroupName open-api: @@ -39,9 +44,6 @@ springdoc: paths: /persons: get: - tags: - - hello-controller - operationId: persons summary: Summary of operationId 'persons' in ApiGroupName description: Description of operationId 'persons' in ApiGroupName diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212-grouped.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212-grouped.json index 72ac0c1f1..6ff5ff701 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212-grouped.json +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212-grouped.json @@ -12,14 +12,74 @@ } ], "paths": { - "/persons": { + "/persons3": { + "get": { + "tags": [ + "hello" + ], + "summary": "Summary of Get operationId 'persons'", + "description": "Description of Get operationId 'persons'", + "operationId": "persons3", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + }, + "post": { + "tags": [ + "hello" + ], + "summary": "Summary of Post operationId 'persons'", + "description": "Description of Post operationId 'persons'", + "operationId": "persons33", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + } + }, + "/persons2": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "persons2", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + } + }, + "/persons1": { "get": { "tags": [ "hello-controller" ], - "summary": "Summary of operationId 'persons' in ApiGroupName", - "description": "Description of operationId 'persons' in ApiGroupName", - "operationId": "persons", + "operationId": "persons1", "responses": { "200": { "description": "OK", @@ -50,4 +110,4 @@ } } } -} \ No newline at end of file +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212.json index 4576d3406..457a8edbd 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212.json +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app212.json @@ -12,17 +12,77 @@ } ], "paths": { - "/persons": { + "/persons3": { + "get": { + "tags": [ + "hello" + ], + "summary": "Summary of Get operationId 'persons'", + "description": "Description of Get operationId 'persons'", + "operationId": "persons3", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + }, + "post": { + "tags": [ + "hello" + ], + "summary": "Summary of Post operationId 'persons'", + "description": "Description of Post operationId 'persons'", + "operationId": "persons33", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + } + }, + "/persons2": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "persons2", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + } + }, + "/persons1": { "get": { "tags": [ "hello-controller" ], - "summary": "Summary of operationId 'persons'", - "description": "Description of operationId 'persons'", - "operationId": "persons", + "operationId": "persons1", "responses": { "200": { - "description":"OK", + "description": "OK", "content": { "*/*": { "schema": { @@ -51,4 +111,3 @@ } } } -