From 735d80d72fa1cd1944d768bfa0654131d203a073 Mon Sep 17 00:00:00 2001 From: Yavuz Tas Date: Fri, 22 May 2020 19:45:26 +0200 Subject: [PATCH 1/4] Code samples for Validate Spring Boot Configuration Parameters at Startup --- .../validation/AppConfiguration.java | 16 +++ .../validation/AppProperties.java | 34 ++++++ .../ReportEmailAddressValidator.java | 27 +++++ .../validation/ReportProperties.java | 51 +++++++++ .../reflectoring/validation/ReportType.java | 5 + .../validation/ValidationApplication.java | 15 +++ .../application-validation.properties | 5 + .../AppPropertiesInvalidInputTest.java | 104 ++++++++++++++++++ .../AppPropertiesValidInputTest.java | 32 ++++++ 9 files changed, 289 insertions(+) create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportEmailAddressValidator.java create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportProperties.java create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportType.java create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java create mode 100644 spring-boot/configuration/src/main/resources/application-validation.properties create mode 100644 spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java create mode 100644 spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java new file mode 100644 index 0000000..e2063bd --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java @@ -0,0 +1,16 @@ +package io.reflectoring.validation; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(AppProperties.class) +class AppConfiguration { + + @Bean + public static ReportEmailAddressValidator configurationPropertiesValidator() { + return new ReportEmailAddressValidator(); + } + +} diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java new file mode 100644 index 0000000..4957d46 --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java @@ -0,0 +1,34 @@ +package io.reflectoring.validation; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; + +@Validated +@ConfigurationProperties(prefix = "app.properties") +class AppProperties { + + @NotEmpty + private String name; + + @Valid + private ReportProperties report; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ReportProperties getReport() { + return report; + } + + public void setReport(ReportProperties report) { + this.report = report; + } +} \ No newline at end of file diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportEmailAddressValidator.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportEmailAddressValidator.java new file mode 100644 index 0000000..430702c --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportEmailAddressValidator.java @@ -0,0 +1,27 @@ +package io.reflectoring.validation; + +import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; + +class ReportEmailAddressValidator implements Validator { + + private static final String EMAIL_DOMAIN = "@analysisapp.com"; + + public boolean supports(Class clazz) { + return ReportProperties.class.isAssignableFrom(clazz); + } + + public void validate(Object target, Errors errors) { + + ValidationUtils.rejectIfEmptyOrWhitespace(errors, "emailAddress", "field.required"); + + ReportProperties reportProperties = (ReportProperties) target; + if (!reportProperties.getEmailAddress().endsWith(EMAIL_DOMAIN)) { + errors.rejectValue("emailAddress", "field.domain.required", + new Object[]{EMAIL_DOMAIN}, + "The email address must contain [" + EMAIL_DOMAIN + "] domain"); + } + + } +} \ No newline at end of file diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportProperties.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportProperties.java new file mode 100644 index 0000000..a594335 --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportProperties.java @@ -0,0 +1,51 @@ +package io.reflectoring.validation; + +import javax.validation.constraints.Email; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +class ReportProperties { + + private Boolean sendEmails = Boolean.FALSE; + + private ReportType type = ReportType.HTML; + + @Min(value = 7) + @Max(value = 30) + private Integer intervalInDays; + + @Email + private String emailAddress; + + public Boolean getSendEmails() { + return sendEmails; + } + + public void setSendEmails(Boolean sendEmails) { + this.sendEmails = sendEmails; + } + + public ReportType getType() { + return type; + } + + public void setType(ReportType type) { + this.type = type; + } + + public Integer getIntervalInDays() { + return intervalInDays; + } + + public void setIntervalInDays(Integer intervalInDays) { + this.intervalInDays = intervalInDays; + } + + public String getEmailAddress() { + return emailAddress; + } + + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } +} diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportType.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportType.java new file mode 100644 index 0000000..7ed75b7 --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ReportType.java @@ -0,0 +1,5 @@ +package io.reflectoring.validation; + +enum ReportType { + HTML, PLAIN_TEXT +} diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java new file mode 100644 index 0000000..15d63a5 --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java @@ -0,0 +1,15 @@ +package io.reflectoring.validation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@PropertySource("classpath:application-validation.properties") +public class ValidationApplication { + + public static void main(String[] args) { + SpringApplication.run(ValidationApplication.class, args); + } + +} diff --git a/spring-boot/configuration/src/main/resources/application-validation.properties b/spring-boot/configuration/src/main/resources/application-validation.properties new file mode 100644 index 0000000..686ba0a --- /dev/null +++ b/spring-boot/configuration/src/main/resources/application-validation.properties @@ -0,0 +1,5 @@ +app.properties.name=Analysis Application +app.properties.report.send-emails=true +app.properties.report.type=PLAIN_TEXT +app.properties.report.interval-in-days=14 +app.properties.report.email-address=manager@analysisapp.com \ No newline at end of file diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java new file mode 100644 index 0000000..d43a018 --- /dev/null +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java @@ -0,0 +1,104 @@ +package io.reflectoring.validation; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindException; +import org.springframework.boot.context.properties.bind.validation.BindValidationException; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.StandardEnvironment; + +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * We create Spring Application dynamically to catch and test application context startup exceptions + */ +class AppPropertiesInvalidInputTest { + + SpringApplication application; + Properties properties; + + @BeforeEach + void setup() { + // create Spring Application dynamically + application = new SpringApplication(ValidationApplication.class); + + // setting test properties for our Spring Application + properties = new Properties(); + + ConfigurableEnvironment environment = new StandardEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + propertySources.addFirst(new PropertiesPropertySource("application-test", properties)); + application.setEnvironment(environment); + } + + @Test + void whenGivenNameEmpty_thenNotEmptyValidationFails() { + + properties.put("app.properties.name", ""); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties' on field 'name'") + .hasStackTraceContaining("[must not be empty]"); + + } + + @Test + void whenGivenReportIntervalInDaysMoreThan30_thenMaxValidationFails() { + + properties.put("app.properties.report.interval-in-days", "31"); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.intervalInDays'") + .hasStackTraceContaining("[must be less than or equal to 30]"); + + } + + @Test + void whenGivenReportIntervalInDaysLessThan7_thenMinValidationFails() { + + properties.put("app.properties.report.interval-in-days", "6"); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.intervalInDays'") + .hasStackTraceContaining("[must be greater than or equal to 7]"); + + } + + @Test + void whenGivenReportEmailAddressIsNotWellFormed_thenEmailValidationFails() { + + properties.put("app.properties.report.email-address", "manager.analysisapp.com"); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.emailAddress'") + .hasStackTraceContaining("[must be a well-formed email address]"); + + } + + @Test + void whenGivenReportEmailAddressDoesNotContainAnalysisappDomain_thenCustomEmailValidatorFails() { + + properties.put("app.properties.report.email-address", "manager@notanalysisapp.com"); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties.report' on field 'emailAddress'") + .hasStackTraceContaining("[The email address must contain [@analysisapp.com] domain]"); + + } + +} diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java new file mode 100644 index 0000000..a188ac6 --- /dev/null +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java @@ -0,0 +1,32 @@ +package io.reflectoring.validation; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(properties = { + "app.properties.name=My Test App", + "app.properties.report.send-emails=true", + "app.properties.report.type=PLAIN_TEXT", + "app.properties.report.interval-in-days=14", + "app.properties.report.email-address=manager@analysisapp.com" +}, classes = {AppConfiguration.class, AppProperties.class, ReportProperties.class}) +class AppPropertiesValidInputTest { + + @Autowired + AppProperties appProperties; + + @Test + void propertiesAreLoaded() { + assertThat(appProperties).isNotNull(); + assertThat(appProperties.getName()).isEqualTo("My Test App"); + assertThat(appProperties.getReport()).isNotNull(); + assertThat(appProperties.getReport().getSendEmails()).isTrue(); + assertThat(appProperties.getReport().getType()).isEqualTo(ReportType.PLAIN_TEXT); + assertThat(appProperties.getReport().getIntervalInDays()).isEqualTo(14); + assertThat(appProperties.getReport().getEmailAddress()).isEqualTo("manager@analysisapp.com"); + } + +} From a418f21ff1da1f0e626d86d09f4b5f138ab56db6 Mon Sep 17 00:00:00 2001 From: Yavuz Tas Date: Tue, 26 May 2020 05:50:40 +0200 Subject: [PATCH 2/4] add third-party component properties example and update the tests --- .../validation/AppConfiguration.java | 10 +++++++++ .../validation/AppProperties.java | 4 ++-- .../ThirdPartyComponentProperties.java | 21 +++++++++++++++++++ .../application-validation.properties | 4 +++- ...t.java => PropertiesInvalidInputTest.java} | 17 +++++++++++++-- ...est.java => PropertiesValidInputTest.java} | 19 +++++++++++++---- 6 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/validation/thirdparty/ThirdPartyComponentProperties.java rename spring-boot/configuration/src/test/java/io/reflectoring/validation/{AppPropertiesInvalidInputTest.java => PropertiesInvalidInputTest.java} (86%) rename spring-boot/configuration/src/test/java/io/reflectoring/validation/{AppPropertiesValidInputTest.java => PropertiesValidInputTest.java} (67%) diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java index e2063bd..729847a 100644 --- a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppConfiguration.java @@ -1,8 +1,11 @@ package io.reflectoring.validation; +import io.reflectoring.validation.thirdparty.ThirdPartyComponentProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; @Configuration @EnableConfigurationProperties(AppProperties.class) @@ -13,4 +16,11 @@ class AppConfiguration { return new ReportEmailAddressValidator(); } + @Bean + @Validated + @ConfigurationProperties(prefix = "app.third-party.properties") + public ThirdPartyComponentProperties thirdPartyComponentProperties() { + return new ThirdPartyComponentProperties(); + } + } diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java index 4957d46..ac26f2c 100644 --- a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java @@ -4,13 +4,13 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotBlank; @Validated @ConfigurationProperties(prefix = "app.properties") class AppProperties { - @NotEmpty + @NotBlank private String name; @Valid diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/thirdparty/ThirdPartyComponentProperties.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/thirdparty/ThirdPartyComponentProperties.java new file mode 100644 index 0000000..768842b --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/thirdparty/ThirdPartyComponentProperties.java @@ -0,0 +1,21 @@ +package io.reflectoring.validation.thirdparty; + +import javax.validation.constraints.NotBlank; + +/** + * We assume that this bean comes from another jar file + */ +public class ThirdPartyComponentProperties { + + @NotBlank + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-boot/configuration/src/main/resources/application-validation.properties b/spring-boot/configuration/src/main/resources/application-validation.properties index 686ba0a..30926fc 100644 --- a/spring-boot/configuration/src/main/resources/application-validation.properties +++ b/spring-boot/configuration/src/main/resources/application-validation.properties @@ -2,4 +2,6 @@ app.properties.name=Analysis Application app.properties.report.send-emails=true app.properties.report.type=PLAIN_TEXT app.properties.report.interval-in-days=14 -app.properties.report.email-address=manager@analysisapp.com \ No newline at end of file +app.properties.report.email-address=manager@analysisapp.com +# third-party component properties +app.third-party.properties.name=Third Party! \ No newline at end of file diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java similarity index 86% rename from spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java rename to spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java index d43a018..1f958db 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesInvalidInputTest.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * We create Spring Application dynamically to catch and test application context startup exceptions */ -class AppPropertiesInvalidInputTest { +class PropertiesInvalidInputTest { SpringApplication application; Properties properties; @@ -45,7 +45,7 @@ class AppPropertiesInvalidInputTest { .isInstanceOf(ConfigurationPropertiesBindException.class) .hasRootCauseInstanceOf(BindValidationException.class) .hasStackTraceContaining("Field error in object 'app.properties' on field 'name'") - .hasStackTraceContaining("[must not be empty]"); + .hasStackTraceContaining("[must not be blank]"); } @@ -101,4 +101,17 @@ class AppPropertiesInvalidInputTest { } + @Test + void whenGivenThirdPartyComponentNameIsEmpty_thenNotEmptyValidationFails() { + + properties.put("app.third-party.properties.name", ""); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.third-party.properties' on field 'name'") + .hasStackTraceContaining("[must not be blank]"); + + } + } diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java similarity index 67% rename from spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java rename to spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java index a188ac6..bdda921 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/validation/AppPropertiesValidInputTest.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java @@ -1,5 +1,6 @@ package io.reflectoring.validation; +import io.reflectoring.validation.thirdparty.ThirdPartyComponentProperties; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -11,15 +12,19 @@ import static org.assertj.core.api.Assertions.assertThat; "app.properties.report.send-emails=true", "app.properties.report.type=PLAIN_TEXT", "app.properties.report.interval-in-days=14", - "app.properties.report.email-address=manager@analysisapp.com" -}, classes = {AppConfiguration.class, AppProperties.class, ReportProperties.class}) -class AppPropertiesValidInputTest { + "app.properties.report.email-address=manager@analysisapp.com", + "app.third-party.properties.name=Third Party!" +}, classes = {AppConfiguration.class}) +class PropertiesValidInputTest { @Autowired AppProperties appProperties; + @Autowired + ThirdPartyComponentProperties thirdPartyComponentProperties; + @Test - void propertiesAreLoaded() { + void appPropertiesAreLoaded() { assertThat(appProperties).isNotNull(); assertThat(appProperties.getName()).isEqualTo("My Test App"); assertThat(appProperties.getReport()).isNotNull(); @@ -29,4 +34,10 @@ class AppPropertiesValidInputTest { assertThat(appProperties.getReport().getEmailAddress()).isEqualTo("manager@analysisapp.com"); } + @Test + void thirdPartyComponentPropertiesAreLoaded() { + assertThat(thirdPartyComponentProperties).isNotNull(); + assertThat(thirdPartyComponentProperties.getName()).isEqualTo("Third Party!"); + } + } From 381fe85e39a44b572e5c3e6f65d3c9424bb2091c Mon Sep 17 00:00:00 2001 From: Yavuz Tas Date: Tue, 26 May 2020 08:09:11 +0200 Subject: [PATCH 3/4] add a sample custom validator for AppProperties and add tests --- .../validation/AppProperties.java | 21 ++++++++++++++++++- .../PropertiesInvalidInputTest.java | 17 +++++++++++++-- .../validation/PropertiesValidInputTest.java | 4 ++-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java index ac26f2c..18c2eec 100644 --- a/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java +++ b/spring-boot/configuration/src/main/java/io/reflectoring/validation/AppProperties.java @@ -1,6 +1,8 @@ package io.reflectoring.validation; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; import javax.validation.Valid; @@ -8,7 +10,7 @@ import javax.validation.constraints.NotBlank; @Validated @ConfigurationProperties(prefix = "app.properties") -class AppProperties { +class AppProperties implements Validator { @NotBlank private String name; @@ -16,6 +18,23 @@ class AppProperties { @Valid private ReportProperties report; + private static final String APP_BASE_NAME = "Application"; + + public boolean supports(Class clazz) { + return AppProperties.class.isAssignableFrom(clazz); + } + + public void validate(Object target, Errors errors) { + + AppProperties appProperties = (AppProperties) target; + if (!appProperties.getName().endsWith(APP_BASE_NAME)) { + errors.rejectValue("name", "field.name.malformed", + new Object[]{APP_BASE_NAME}, + "The application name must contain [" + APP_BASE_NAME + "] base name"); + } + + } + public String getName() { return name; } diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java index 1f958db..e725d5d 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java @@ -37,7 +37,7 @@ class PropertiesInvalidInputTest { } @Test - void whenGivenNameEmpty_thenNotEmptyValidationFails() { + void whenGivenNameEmpty_thenNotBlankValidationFails() { properties.put("app.properties.name", ""); @@ -49,6 +49,19 @@ class PropertiesInvalidInputTest { } + @Test + void whenGivenNameDoesNotContainBaseName_thenCustomAppPropertiesValidatorFails() { + + properties.put("app.properties.name", "My App"); + + assertThatThrownBy(application::run) + .isInstanceOf(ConfigurationPropertiesBindException.class) + .hasRootCauseInstanceOf(BindValidationException.class) + .hasStackTraceContaining("Field error in object 'app.properties' on field 'name'") + .hasStackTraceContaining("[The application name must contain [Application] base name]"); + + } + @Test void whenGivenReportIntervalInDaysMoreThan30_thenMaxValidationFails() { @@ -102,7 +115,7 @@ class PropertiesInvalidInputTest { } @Test - void whenGivenThirdPartyComponentNameIsEmpty_thenNotEmptyValidationFails() { + void whenGivenThirdPartyComponentNameIsEmpty_thenNotBlankValidationFails() { properties.put("app.third-party.properties.name", ""); diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java index bdda921..0ca1cef 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesValidInputTest.java @@ -8,7 +8,7 @@ import org.springframework.boot.test.context.SpringBootTest; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(properties = { - "app.properties.name=My Test App", + "app.properties.name=My Test Application", "app.properties.report.send-emails=true", "app.properties.report.type=PLAIN_TEXT", "app.properties.report.interval-in-days=14", @@ -26,7 +26,7 @@ class PropertiesValidInputTest { @Test void appPropertiesAreLoaded() { assertThat(appProperties).isNotNull(); - assertThat(appProperties.getName()).isEqualTo("My Test App"); + assertThat(appProperties.getName()).isEqualTo("My Test Application"); assertThat(appProperties.getReport()).isNotNull(); assertThat(appProperties.getReport().getSendEmails()).isTrue(); assertThat(appProperties.getReport().getType()).isEqualTo(ReportType.PLAIN_TEXT); From 97bf254d85ab424cf207fab68ef44edb871de0f3 Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Wed, 27 May 2020 06:33:15 +1000 Subject: [PATCH 4/4] update to Java 13, fixing build --- .../awshelloworld/HelloWorldController.java | 6 +-- .../rds-in-private-subnet/database.yml | 6 +++ .../rds-in-private-subnet.drawio | 2 +- .../rds-in-private-subnet.svg | 3 -- .../rds-in-private-subnet/service.yml | 14 +++-- spring-boot/configuration/build.gradle | 5 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- spring-boot/configuration/gradlew | 0 .../java/io/reflectoring/Application.java | 14 +++++ .../src/main/resources/application.properties | 1 - .../ConfigurationApplication.java | 0 .../mail/MailModuleTestWithAllProperties.java | 51 +++++++++---------- .../PropertiesInvalidInputTest.java | 9 ++-- .../validation/ValidationApplication.java | 0 14 files changed, 62 insertions(+), 51 deletions(-) delete mode 100644 aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.svg mode change 100644 => 100755 spring-boot/configuration/gradlew create mode 100644 spring-boot/configuration/src/main/java/io/reflectoring/Application.java rename spring-boot/configuration/src/{main => test}/java/io/reflectoring/configuration/ConfigurationApplication.java (100%) rename spring-boot/configuration/src/{main => test}/java/io/reflectoring/validation/ValidationApplication.java (100%) diff --git a/aws/aws-rds-hello-world/src/main/java/io/reflectoring/awshelloworld/HelloWorldController.java b/aws/aws-rds-hello-world/src/main/java/io/reflectoring/awshelloworld/HelloWorldController.java index 304dec2..ccaef3c 100644 --- a/aws/aws-rds-hello-world/src/main/java/io/reflectoring/awshelloworld/HelloWorldController.java +++ b/aws/aws-rds-hello-world/src/main/java/io/reflectoring/awshelloworld/HelloWorldController.java @@ -4,16 +4,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController -public class HelloWorldController { +class HelloWorldController { private final UserRepository userRepository; - public HelloWorldController(UserRepository userRepository) { + HelloWorldController(UserRepository userRepository) { this.userRepository = userRepository; } @GetMapping("/hello") - public String helloWorld(){ + String helloWorld(){ Iterable users = userRepository.findAll(); diff --git a/aws/cloudformation/rds-in-private-subnet/database.yml b/aws/cloudformation/rds-in-private-subnet/database.yml index f7784a8..c68f6fe 100644 --- a/aws/cloudformation/rds-in-private-subnet/database.yml +++ b/aws/cloudformation/rds-in-private-subnet/database.yml @@ -25,6 +25,7 @@ Resources: SecretStringTemplate: !Join ['', ['{"username": "', !Ref 'DBUsername' ,'"}']] GenerateStringKey: "password" PasswordLength: 32 + ExcludeCharacters: '"@/\' DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup @@ -75,6 +76,11 @@ Outputs: Value: !GetAtt 'PostgresInstance.Endpoint.Port' Export: Name: !Join [ ':', [ !Ref 'AWS::StackName', 'EndpointPort' ] ] + DBName: + Description: The name of the database that is created within the PostgreSQL instance. + Value: !Ref DBName + Export: + Name: !Join [ ':', [ !Ref 'AWS::StackName', 'DBName' ] ] Secret: Description: Reference to the secret containing the password to the database. Value: !Ref 'Secret' diff --git a/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.drawio b/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.drawio index b896d1b..e2d0bd4 100644 --- a/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.drawio +++ b/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.drawio @@ -1 +1 @@ -7VpZb9s4EP41fqyhW/JjfKUBWqyx3t22TwYtMTIRWXRp+sqv36FEXaR8pHESGIhhIJohRQ5nvm84pNOxB8v9PUOrxXca4aRjGdG+Yw87lmXargt/hOaQa3zfyRUxI5HsVCmm5BlLpSG1GxLhdaMjpzThZNVUhjRNccgbOsQY3TW7PdKkOesKxVhTTEOU6NofJOILqTW9XtXwFZN4IacOLD9vWKKis1zJeoEiuqup7FHHHjBKef603A9wIpxX+CV/b3yktTSM4ZRf8sICh9b++cH/MXyeP3nO9+j3bP6lMG6Lko1c8X+TgTSYHwovrChJeeZJtw9feGlgdFxoGQipa7mKQpX9psLUJTFGU6HKflNhqsObyvymamBNoUmN4Q1lfqNmIHztPt3whKR4UGLOAGXMUEQgFgOaUAa6lKbgvf6CLxOQTHjcLQjH0xUKhVd3wBfQPdKUS9SbViFLx4t3ADUr8bzcx4JgXbRbO92Y0c0qm/IBcN/aOtuuQvE6Z/QJFyZ1LNtygsB0xEQkSRRTt5hxAtC/S0gsRuVUTIKklOBHLkYE+0kaf8ukoW1Im2tT3N31/X4A+gitFziS7tHhKhEsZsX7mkrC9x7TJebsAF2KVk+iVeaSQIq7ipieI3WLGiet4j0kk0FcDl3xBR4kZV5AH1tjz2QzT0go2LOZp5h/Mun2mbTG4YYRfphVnacZraS5l3EM9KPe2B551yNaOc+1iWa7TaKZls40M2hhmumZGULehGymr7FtNJiCYorZlgAUVK61pDgtVu6dNwi8ug/NowFSEaWEoxxKCeQ1AhIoAXFbAmK3BcQyup71RgFxtXh0LC8RS57DQywe7lYrSIeIEyBX0caKxm8URboW3IfSELOiBUwrh9NC3Jo/GnQoQvkNzXEyoWuS2WIP55RzujxLxhCCBrY0sk9LJrG7qFrpLIGFzebFOrQ8MHYD13aOJ7orAMZyFMD4QTcI9O2yp0PGfyu4BOfh8iCcnW2bHlqKMKTz9SrziIqSe8TxDh1uDCRErm8WS/PbseG9KTbKRFHDhu9+LDZ6R1L7P2j9dFk81b3RtsZifs2Pwsd+MDLq/BsSBgPlIU8pEx5Q4zI03AFsQC07yGP2eQdEnStw0HqVu+OR7IUd7RUPw2u6YSHO650+iG2VDw7X1wGbYypgayna/ZaNy3+rkt3Sa3YNYDiKceFZAQca0xQlo0pbDwtOoztx0SCCm9DwSaiSeSYXMc5ggxgv+kkuw5tjkpQJQytNHLfvCZhC2ZlGJbIgFuzwsy78EgJU2VIc7uuNw0NdmmBGwI8CWqeLxRwkp/woKQvrijE/n/aFT08Cpl7KGDL6DCewo26bdzFtkJDDTcTpqgJfr4k9KJuaI+SLlC/Vr06Uccr6Vw5kGwo6cydoA2UALZf4Csx6n5i9ALNnoeh+HBQt41pYVI4CtvnOWGwr424Ki3vCfxZzw3MNiSBVQBTCoSZcL3W6F8K193FwtY1eE64GnCPtP0OsU/wGUCDWMbpG7WNehF+IvSj3y27yIu34Cpx2wh2z8kx/eMgtuC6Z9Lr31JnoJo861ssPOeVPJdX1WvHji32l87FalfptRyC/13X10tR1Xl+a7pPNeIb/2n51Dr9+8/m/z/h+88XUwDBhZAsnxc/r5M/r5I+9TtaY1cK/o2Rzex98ndzKNut86v17KO4gHlIoPlJxv3xTCZhFax0z1si3s9/mTl4vvSranuM0o+07ZRDrudXS4x2cqFNeFetbP/O3FoJ/Vny2HNuP0+N9ak1TqTUtp7yrfnGtqWzs2Vi92kcZ98XF5uXHKRCrf+7Iu1f/ImOP/gc= \ No newline at end of file +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 \ No newline at end of file diff --git a/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.svg b/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.svg deleted file mode 100644 index d49a192..0000000 --- a/aws/cloudformation/rds-in-private-subnet/rds-in-private-subnet.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
VPC
VPC
Public subnet
Public subnet
ECS Service
Application
Load
Balancer
Application...
Internet 
Gateway
Internet...
ECS Task
ECS Task
Internet
Internet
Private subnet
Private subnet
RDS Instance
RDS Instance
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/aws/cloudformation/rds-in-private-subnet/service.yml b/aws/cloudformation/rds-in-private-subnet/service.yml index 4393811..fc40462 100644 --- a/aws/cloudformation/rds-in-private-subnet/service.yml +++ b/aws/cloudformation/rds-in-private-subnet/service.yml @@ -9,12 +9,6 @@ Parameters: DatabaseStackName: Type: String Description: The name of the database stack with the database this service should connect to. - DBName: - Type: String - Description: The database name. - DBUsername: - Type: String - Description: The database username. ServiceName: Type: String Description: A human-readable name for the service. @@ -117,9 +111,13 @@ Resources: - ':' - Fn::ImportValue: !Join [':', [!Ref 'DatabaseStackName', 'EndpointPort']] - '/' - - !Ref 'DBName' + - Fn::ImportValue: !Join [':', [!Ref 'DatabaseStackName', 'DBName']] - Name: SPRING_DATASOURCE_USERNAME - Value: !Ref 'DBUsername' + Value: !Join + - '' + - - '{{resolve:secretsmanager:' + - Fn::ImportValue: !Join [':', [!Ref 'DatabaseStackName', 'Secret']] + - ':SecretString:username}}' - Name: SPRING_DATASOURCE_PASSWORD Value: !Join - '' diff --git a/spring-boot/configuration/build.gradle b/spring-boot/configuration/build.gradle index 54562d1..1d7d91a 100644 --- a/spring-boot/configuration/build.gradle +++ b/spring-boot/configuration/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.1.3.RELEASE' + id 'org.springframework.boot' version '2.3.0.RELEASE' id 'java' } @@ -7,7 +7,7 @@ apply plugin: 'io.spring.dependency-management' group = 'io.reflectoring' version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' +sourceCompatibility = '13' repositories { mavenCentral() @@ -23,6 +23,7 @@ dependencies { testImplementation('org.junit.jupiter:junit-jupiter:5.4.0') testImplementation('org.springframework.boot:spring-boot-starter-test'){ exclude group: 'junit', module: 'junit' + exclude group: 'org.junit.vintage' } } diff --git a/spring-boot/configuration/gradle/wrapper/gradle-wrapper.properties b/spring-boot/configuration/gradle/wrapper/gradle-wrapper.properties index 44e7c4d..21e622d 100644 --- a/spring-boot/configuration/gradle/wrapper/gradle-wrapper.properties +++ b/spring-boot/configuration/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/spring-boot/configuration/gradlew b/spring-boot/configuration/gradlew old mode 100644 new mode 100755 diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/Application.java b/spring-boot/configuration/src/main/java/io/reflectoring/Application.java new file mode 100644 index 0000000..0c90412 --- /dev/null +++ b/spring-boot/configuration/src/main/java/io/reflectoring/Application.java @@ -0,0 +1,14 @@ +package io.reflectoring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-boot/configuration/src/main/resources/application.properties b/spring-boot/configuration/src/main/resources/application.properties index bc88f39..0dc5de8 100644 --- a/spring-boot/configuration/src/main/resources/application.properties +++ b/spring-boot/configuration/src/main/resources/application.properties @@ -1,5 +1,4 @@ myapp.mail.enabled=true -myapp myapp.mail.pauseBetweenMails=5s myapp.mail.maxAttachmentSize=1MB myapp.mail.smtpServers[0]=server1 diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/configuration/ConfigurationApplication.java b/spring-boot/configuration/src/test/java/io/reflectoring/configuration/ConfigurationApplication.java similarity index 100% rename from spring-boot/configuration/src/main/java/io/reflectoring/configuration/ConfigurationApplication.java rename to spring-boot/configuration/src/test/java/io/reflectoring/configuration/ConfigurationApplication.java diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/configuration/mail/MailModuleTestWithAllProperties.java b/spring-boot/configuration/src/test/java/io/reflectoring/configuration/mail/MailModuleTestWithAllProperties.java index 731d8f6..f9b6c65 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/configuration/mail/MailModuleTestWithAllProperties.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/configuration/mail/MailModuleTestWithAllProperties.java @@ -1,37 +1,36 @@ package io.reflectoring.configuration.mail; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.util.unit.DataSize; + import java.time.Duration; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.util.unit.DataSize; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(properties = { - "myapp.mail.enabled=asd", - "myapp.mail.defaultSubject=hello", - "myapp.mail.pauseBetweenMails=5s", - "myapp.mail.maxAttachmentSize=1MB", - "myapp.mail.smtpServers[0]=server1", - "myapp.mail.smtpServers[1]=server2", - "myapp.mail.maxAttachmentWeight=5kg" + "myapp.mail.enabled=asd", + "myapp.mail.defaultSubject=hello", + "myapp.mail.pauseBetweenMails=5s", + "myapp.mail.maxAttachmentSize=1MB", + "myapp.mail.smtpServers[0]=server1", + "myapp.mail.smtpServers[1]=server2", + "myapp.mail.maxAttachmentWeight=5kg" }) class MailModuleTestWithAllProperties { - @Autowired(required = false) - private MailModuleProperties mailModuleProperties; + @Autowired(required = false) + private MailModuleProperties mailModuleProperties; - @Test - void propertiesAreLoaded() { - assertThat(mailModuleProperties).isNotNull(); - assertThat(mailModuleProperties.getDefaultSubject()).isEqualTo("hello"); - assertThat(mailModuleProperties.getEnabled()).isTrue(); - assertThat(mailModuleProperties.getPauseBetweenMails()).isEqualByComparingTo(Duration.ofSeconds(5)); - assertThat(mailModuleProperties.getMaxAttachmentSize()).isEqualByComparingTo(DataSize.ofMegabytes(1)); - assertThat(mailModuleProperties.getSmtpServers()).hasSize(2); - assertThat(mailModuleProperties.getMaxAttachmentWeight().getGrams()).isEqualTo(5000L); - } + @Test + void propertiesAreLoaded() { + assertThat(mailModuleProperties).isNotNull(); + assertThat(mailModuleProperties.getDefaultSubject()).isEqualTo("hello"); + assertThat(mailModuleProperties.getEnabled()).isTrue(); + assertThat(mailModuleProperties.getPauseBetweenMails()).isEqualByComparingTo(Duration.ofSeconds(5)); + assertThat(mailModuleProperties.getMaxAttachmentSize()).isEqualByComparingTo(DataSize.ofMegabytes(1)); + assertThat(mailModuleProperties.getSmtpServers()).hasSize(2); + assertThat(mailModuleProperties.getMaxAttachmentWeight().getGrams()).isEqualTo(5000L); + } } \ No newline at end of file diff --git a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java index e725d5d..a3b1be1 100644 --- a/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java +++ b/spring-boot/configuration/src/test/java/io/reflectoring/validation/PropertiesInvalidInputTest.java @@ -70,8 +70,7 @@ class PropertiesInvalidInputTest { assertThatThrownBy(application::run) .isInstanceOf(ConfigurationPropertiesBindException.class) .hasRootCauseInstanceOf(BindValidationException.class) - .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.intervalInDays'") - .hasStackTraceContaining("[must be less than or equal to 30]"); + .hasStackTraceContaining("rejected value [31]"); } @@ -83,8 +82,7 @@ class PropertiesInvalidInputTest { assertThatThrownBy(application::run) .isInstanceOf(ConfigurationPropertiesBindException.class) .hasRootCauseInstanceOf(BindValidationException.class) - .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.intervalInDays'") - .hasStackTraceContaining("[must be greater than or equal to 7]"); + .hasStackTraceContaining("rejected value [6]"); } @@ -96,8 +94,7 @@ class PropertiesInvalidInputTest { assertThatThrownBy(application::run) .isInstanceOf(ConfigurationPropertiesBindException.class) .hasRootCauseInstanceOf(BindValidationException.class) - .hasStackTraceContaining("Field error in object 'app.properties' on field 'report.emailAddress'") - .hasStackTraceContaining("[must be a well-formed email address]"); + .hasStackTraceContaining("rejected value [manager.analysisapp.com]"); } diff --git a/spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java b/spring-boot/configuration/src/test/java/io/reflectoring/validation/ValidationApplication.java similarity index 100% rename from spring-boot/configuration/src/main/java/io/reflectoring/validation/ValidationApplication.java rename to spring-boot/configuration/src/test/java/io/reflectoring/validation/ValidationApplication.java