Skip to content

Commit

Permalink
Add support for externalizing strings in openapi groups
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton Tkachenko committed Feb 25, 2024
1 parent 2601b3c commit 4618041
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
package org.springdoc.core.configuration;

import org.springdoc.core.customizers.SpecificationStringPropertiesCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -33,6 +35,8 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.PropertyResolver;

import java.util.List;

/**
* The type Spring doc specification string properties configuration.
*
Expand All @@ -46,6 +50,7 @@ public class SpringDocSpecificationStringPropertiesConfiguration {

/**
* Springdoc customizer that takes care of the specification string properties customization.
* Will be applied to general openapi schema.
*
* @return the springdoc customizer
*/
Expand All @@ -58,4 +63,41 @@ SpecificationStringPropertiesCustomizer specificationStringPropertiesCustomizer(
return new SpecificationStringPropertiesCustomizer(propertyResolverUtils);
}

/**
* Bean post processor that applies the specification string properties customization to
* grouped openapi schemas by using group name as a prefix for properties.
*
* @return the bean post processor
*/
@Bean
@ConditionalOnMissingBean
@Lazy(false)
SpecificationStringPropertiesCustomizerBeanPostProcessor specificationStringPropertiesCustomizerBeanPostProcessor(
PropertyResolver propertyResolverUtils
) {
return new SpecificationStringPropertiesCustomizerBeanPostProcessor(propertyResolverUtils);
}


private static class SpecificationStringPropertiesCustomizerBeanPostProcessor implements BeanPostProcessor {

private final PropertyResolver propertyResolverUtils;

public SpecificationStringPropertiesCustomizerBeanPostProcessor(
PropertyResolver propertyResolverUtils
) {
this.propertyResolverUtils = propertyResolverUtils;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof GroupedOpenApi groupedOpenApi) {
groupedOpenApi.addAllOpenApiCustomizer(List.of(new SpecificationStringPropertiesCustomizer(
propertyResolverUtils, groupedOpenApi.getGroup()
)));
}
return bean;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@
* <li>springdoc.specification-strings.paths.{operationId}.description - to set description of {operationId}</li>
* <li>springdoc.specification-strings.paths.{operationId}.summary - to set summary of {operationId}</li>
* </ul>
* <p>
* Support for groped openapi customization is similar to the above, but with a group name prefix.
* E.g.
* <ul>
* <li>springdoc.specification-strings.{group-name}.info.title - to set title of api-info</li>
* <li>springdoc.specification-strings.{group-name}.components.User.description - to set description of User model</li>
* <li>springdoc.specification-strings.{group-name}.paths.{operationId}.description - to set description of {operationId}</li>
* </ul>
*
* @author Anton Tkachenko [email protected]
*/
Expand All @@ -71,9 +79,16 @@ public class SpecificationStringPropertiesCustomizer implements GlobalOpenApiCus
private static final String SPECIFICATION_STRINGS_PREFIX = "springdoc.specification-strings.";

private final PropertyResolver propertyResolver;
private final String propertyPrefix;

public SpecificationStringPropertiesCustomizer(PropertyResolver resolverUtils) {
this.propertyResolver = resolverUtils;
this.propertyPrefix = SPECIFICATION_STRINGS_PREFIX;
}

public SpecificationStringPropertiesCustomizer(PropertyResolver propertyResolver, String groupName) {
this.propertyResolver = propertyResolver;
this.propertyPrefix = SPECIFICATION_STRINGS_PREFIX + groupName + ".";
}

@Override
Expand Down Expand Up @@ -140,7 +155,7 @@ private void setComponentsProperties(OpenAPI openApi) {
private void resolveString(
Consumer<String> setter, String node
) {
String nodeWithPrefix = SPECIFICATION_STRINGS_PREFIX + node;
String nodeWithPrefix = propertyPrefix + node;
String value = propertyResolver.getProperty(nodeWithPrefix);
if (StringUtils.isNotBlank(value)) {
setter.accept(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,17 @@

package test.org.springdoc.api.app212;

import org.junit.jupiter.api.Test;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.ActiveProfiles;
import test.org.springdoc.api.AbstractSpringDocTest;

import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
* The type Spring doc app 192 test.
* <p>
Expand All @@ -35,6 +42,23 @@ public class SpringDocApp212Test extends AbstractSpringDocTest {
*/
@SpringBootApplication
static class SpringDocTestApp {

@Bean
GroupedOpenApi apiGroupBeanName() {
return GroupedOpenApi.builder()
.group("apiGroupName")
.packagesToScan("test.org.springdoc.api.app212")
.build();
}
}

@Test
void getGroupedOpenapi_shouldCustomizeFromPropertiesWithGroupNamePrefix() throws Exception {
String result = mockMvc.perform(get("/v3/api-docs/apiGroupName"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
String expected = getContent("results/app212-grouped.json");
assertEquals(expected, result, true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,20 @@ springdoc:
persons:
description: Description of operationId 'persons'
summary: Summary of operationId 'persons'
apiGroupName:
info:
title: ApiGroupName info title
description: ApiGroupName info description
version: ApiGroupName info version
components:
schemas:
PersonDTO:
description: Description for PersonDTO component in ApiGroupName
properties:
name:
description: Description for 'name' property in ApiGroupName
example: Example value for 'name' property in ApiGroupName
paths:
persons:
description: Description of operationId 'persons' in ApiGroupName
summary: Summary of operationId 'persons' in ApiGroupName
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"openapi": "3.0.1",
"info": {
"title": "ApiGroupName info title",
"description": "ApiGroupName info description",
"version": "ApiGroupName info version"
},
"servers": [
{
"url": "http://localhost",
"description": "Generated server url"
}
],
"paths": {
"/persons": {
"get": {
"tags": [
"hello-controller"
],
"summary": "Summary of operationId 'persons' in ApiGroupName",
"description": "Description of operationId 'persons' in ApiGroupName",
"operationId": "persons",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/PersonDTO"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"PersonDTO": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Description for 'name' property in ApiGroupName",
"example": "Example value for 'name' property in ApiGroupName"
}
},
"description": "Description for PersonDTO component in ApiGroupName"
}
}
}
}

0 comments on commit 4618041

Please sign in to comment.