Skip to content

Commit

Permalink
Support info extensions via environment (#423)
Browse files Browse the repository at this point in the history
* #395 support info-extensions definition in environment

* #395 changed extensionfields in ConfigDocket.Info to Map<String,String>

* #395 Integration tests for info/contact/license extensions in amqp-examples

* #395 reverted rename of asycnapi.json in amqp-examples

---------

Co-authored-by: Thomas Vahrst <[email protected]>
  • Loading branch information
sam0r040 and tvahrst authored Oct 27, 2023
1 parent 6e61df1 commit 89c7b00
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Optional;

@Slf4j
Expand Down Expand Up @@ -75,19 +76,29 @@ private AsyncApiDocket parseApplicationConfigProperties() {
return builder.build();
}

private static Info buildInfo(@Nullable SpringwolfConfigProperties.ConfigDocket.Info info) {
if (info == null || !StringUtils.hasText(info.getVersion()) || !StringUtils.hasText(info.getTitle())) {
private static Info buildInfo(@Nullable SpringwolfConfigProperties.ConfigDocket.Info configDocketInfo) {
if (configDocketInfo == null
|| !StringUtils.hasText(configDocketInfo.getVersion())
|| !StringUtils.hasText(configDocketInfo.getTitle())) {
throw new IllegalArgumentException("One or more required fields of the info object (title, version) "
+ "in application.properties with path prefix " + SpringwolfConfigConstants.SPRINGWOLF_CONFIG_PREFIX
+ " is not set.");
}

return Info.builder()
.version(info.getVersion())
.title(info.getTitle())
.description(info.getDescription())
.contact(info.getContact())
.license(info.getLicense())
Info asyncapiInfo = Info.builder()
.version(configDocketInfo.getVersion())
.title(configDocketInfo.getTitle())
.description(configDocketInfo.getDescription())
.contact(configDocketInfo.getContact())
.license(configDocketInfo.getLicense())
.build();

// copy extension fields from configDocketInfo to asyncapiInfo.
if (configDocketInfo.getExtensionFields() != null) {
Map<String, Object> extFieldsMap = Map.copyOf(configDocketInfo.getExtensionFields());
asyncapiInfo.setExtensionFields(extFieldsMap);
}

return asyncapiInfo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ public static class Info {
@NestedConfigurationProperty
@Nullable
private License license;

/**
* Extension properties for the Info block.
*/
@Nullable
private Map<String, String> extensionFields;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class DefaultAsyncApiDocketServiceIntegrationTest {
"springwolf.enabled=true",
"springwolf.docket.info.title=Info title was loaded from spring properties",
"springwolf.docket.info.version=1.0.0",
"springwolf.docket.info.extension-fields.x-api-name=api-name",
"springwolf.docket.base-package=io.github.stavshamir.springwolf.example",
"springwolf.docket.servers.test-protocol.protocol=test",
"springwolf.docket.servers.test-protocol.url=some-server:1234"
Expand All @@ -46,6 +47,7 @@ void testDocketContentShouldBeLoadedFromProperties() {
AsyncApiDocket docket = asyncApiDocketService.getAsyncApiDocket();
assertThat(docket).isNotNull();
assertThat(docket.getInfo().getTitle()).isEqualTo("Info title was loaded from spring properties");
assertThat(docket.getInfo().getExtensionFields().get("x-api-name")).isEqualTo("api-name");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties.ConfigDocket.Info;
import org.junit.jupiter.api.Test;

import java.util.Map;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -34,6 +35,7 @@ void testServiceShouldMapAllPropertiesToTheDocket() {
info.setDescription("some-description");
info.setLicense(new License("license-name", "license-url"));
info.setContact(new Contact("contact-name", "contact-url", "contact-email"));
info.setExtensionFields(Map.of("x-api-name", "api-name"));
configDocket.setInfo(info);

SpringwolfConfigProperties properties = new SpringwolfConfigProperties();
Expand All @@ -52,6 +54,8 @@ void testServiceShouldMapAllPropertiesToTheDocket() {
assertThat(asyncApiDocket.getInfo().getDescription()).isEqualTo(info.getDescription());
assertThat(asyncApiDocket.getInfo().getLicense()).isEqualTo(info.getLicense());
assertThat(asyncApiDocket.getInfo().getContact()).isEqualTo(info.getContact());
assertThat(asyncApiDocket.getInfo().getExtensionFields().get("x-api-name"))
.isEqualTo("api-name");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

@Configuration
public class AsyncApiConfiguration {

Expand Down Expand Up @@ -50,6 +52,12 @@ public AsyncApiDocket asyncApiDocket() {
.license(License.builder().name("Apache License 2.0").build())
.build();

// the builder for asyncapi info, contact and license doesn't support setting/adding extensions, so
// we add text extension explicitely
info.setExtensionFields(Map.of("x-api-audience", "company-internal"));
info.getContact().setExtensionFields(Map.of("x-phone", "+49 123 456789"));
info.getLicense().setExtensionFields(Map.of("x-desc", "some description"));

Server amqp = Server.builder()
.protocol("amqp")
.url(String.format("%s:%s", amqpHost, amqpPort))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#########
# Spring configuration
spring.application.name=Springwolf example project - Amqp
spring.application.name=Springwolf example project - AMQP


#########
Expand All @@ -19,10 +19,13 @@ springwolf.docket.info.title=${spring.application.name}
springwolf.docket.info.version=1.0.0
springwolf.docket.info.description=Springwolf example project to demonstrate springwolfs abilities
springwolf.docket.info.terms-of-service=http://asyncapi.org/terms
springwolf.docket.info.extension-fields.x-api-audience=company-internal
springwolf.docket.info.contact.name=springwolf
springwolf.docket.info.contact.email=[email protected]
springwolf.docket.info.contact.url=https://github.com/springwolf/springwolf-core
springwolf.docket.info.contact.extension-fields.x-phone=+49 123 456789
springwolf.docket.info.license.name=Apache License 2.0
springwolf.docket.info.license.extension-fields.x-desc=some description
springwolf.docket.servers.amqp.protocol=amqp
springwolf.docket.servers.amqp.url=${spring.rabbitmq.host}:${spring.rabbitmq.port}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.example.amqp;

import org.springframework.test.context.TestPropertySource;

/**
* Api integrationtest based on a SpringBoot application that defines a custom Docket bean. This contains Info and
* Server Informations as well as some explicit Producer and Consumer definitions.
*/
@TestPropertySource(properties = {"customAsyncApiDocketBean=true"})
public class ApiWithDocketBeanIntegrationTest extends BaseApiIntegrationTest {

@Override
protected String getExpectedApiFileName() {
return "/asyncapi.json";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.example.amqp;

import org.springframework.test.context.TestPropertySource;

/**
* Api integrationtest based on a SpringBoot application that defines all info and server properties via
* spring environment (from application.properties).
*/
@TestPropertySource(properties = {"customAsyncApiDocketBean=false"})
public class ApiWithDocketFromEnvironmentIntegrationTest extends BaseApiIntegrationTest {

@Override
protected String getExpectedApiFileName() {
return "/asyncapi_withdocketfromenvironment.json";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Api integrationtest base class defining a SpringBootTest and a test method which asserts the resulting asyncapi.
* Subclasses can customize this test with @TestPropertySources and custom expectation file names.
* @see ApiWithDocketBeanIntegrationTest
* @see ApiWithDocketFromEnvironmentIntegrationTest
*/
@SpringBootTest(
classes = {SpringwolfAmqpExampleApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApiIntegrationTest {
public abstract class BaseApiIntegrationTest {

@Autowired
private TestRestTemplate restTemplate;
Expand All @@ -31,9 +37,12 @@ void asyncApiResourceArtifactTest() throws JSONException, IOException {
String actual = restTemplate.getForObject(url, String.class);
System.out.println("Got: " + actual);

InputStream s = this.getClass().getResourceAsStream("/asyncapi.json");
String expectedApiFileName = getExpectedApiFileName();
InputStream s = this.getClass().getResourceAsStream(expectedApiFileName);
String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8);

assertEquals(expected, actual);
}

protected abstract String getExpectedApiFileName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
"contact": {
"name": "springwolf",
"url": "https://github.com/springwolf/springwolf-core",
"email": "[email protected]"
"email": "[email protected]",
"x-phone": "+49 123 456789"
},
"license": {
"name": "Apache License 2.0"
}
"name": "Apache License 2.0",
"x-desc": "some description"
},
"x-api-audience": "company-internal"
},
"defaultContentType": "application/json",
"servers": {
Expand Down
Loading

0 comments on commit 89c7b00

Please sign in to comment.