Spring Boot inject List of Maps from application.yml - spring-boot

I am trying to inject List of Maps from Spring Boot config but getting an empty List. How to inject this correctly?
cacheConfigs:
- cacheOne:
test: test1
- cacheTwo:
test: test2
- cacheThree:
test: test3
- cacheFor:
test: test4
#ConfigurationProperties(prefix = "cacheConfigs")
public List<Map<String, String>> getCacheConfigs() {
return new ArrayList<>();
}

This was a "new" for me. I got this working by making cacheConfigs one level deeper and the used the new top level name as the #ConfigurationProperties param. Like this:
cache-configs-map:
cacheConfigs:
- cacheOne:
test: test1
- cacheTwo:
test: test2
- cacheThree:
test: test3
- cacheFor:
test: test4
Now, your configuration class looks like this:
#Configuration
public class Config{
#NoArgsConstructor #AllArgsConstructor( staticName = "of" )
#Getter #Setter
public static class C{
private List<Map<String, String>> cacheConfigs;
}
#Bean
#ConfigurationProperties(prefix = "cache-configs-map")
public C getC() {
return new C();
}
}

Related

Spring Boot Kotlin - Injecting a map from a YAML file

test.yml (location: resources/properties/)
edit:
field1: test
field2: test
field3: test
field4: test
PropertyConfig.kt
#Configuration
#PropertySource("classpath:properties/test.yml")
class PropertyConfig {
#Bean
#ConfigurationProperties(prefix = "edit")
fun testProperty() = mutableMapOf<String, String>()
}
#Service
class EditService(
private val testProperty: Map<String, String>
) {
fun print() {
println(testProperty) // empty
}
}
I want to receive the values below edit as a map.
I tried options for #ConfigurationProperties with prefix and value, but it doesn't work.
If I use properties file, it works well, but not yml file.
What am I missing? Thanks.
kotlinVersion = '1.6'; springBootVersion = '2.6.1'
You are missing the #ContructorBinding annotation (required as of Spring Boot 2.2.0). Please see this answer:
#ConstructorBinding
#ConfigurationProperties("")
data class PropertyConfig(
val edit: Map<String,String>
)
If you wanna use a non-standard yml file (not called application.yml or derivate), like in the example you provided, then you need to add also the #PropertySource annotation to your Configuration data class.
#ConstructorBinding
#ConfigurationProperties("")
#PropertySource(value = "classpath:test.yml")
data class PropertyConfig(
val edit: Map<String,String>
)
Something like this (without #Bean):
#ConfigurationProperties(prefix = "") // blank since "edit:" is root element
#ConstructorBinding
data class EditProperties(
val edit: Map<String, String> // property name must match to relevant root element in YAML
)
#Service
class EditService(private val properties: EditProperties) {
fun print() {
println(properties.edit)
}
}
Output:
{field1=test, field2=test, field3=test, field4=test}

Retrieve all key-value pairs from properties file in Spring Boot with a given prefix and suffix

I have been trying for sometime now. I want to read from the properties file and store as a hashmap.
Here is an example.
sample.properties
pref1.pref2.abc.suf = 1
pref1.pref2.def.suf = 2
...
...
Here is the Config class.
#ConfiguraionProperties
#PropertySource(value = "classpath:sample.properties")
public class Config{
#Autowired
private Environment env;
public HashMap<String, Integer> getAllProps(){
//TO-DO
}
}
I want to be able to return {"abc":1, "def":2};
I stumbled upon answers like using PropertySources, AbstractEnvironment etc., but still can't get my head around using it.
Thanks in advance!
The class
org.springframework.boot.actuate.endpoint.EnvironmentEndpoint
reads all configured properties and puts them in a Map.
This is used to return all properties and their values from a REST endpoint. See production ready endpoints
You can copy the 3 methods that build the Map with all properties from class EnvironmentEndpoint into your own. Then just iterate over the Map and select all properties by their key.
I have done that in one project, worked quite well.
its possible using spring boot #component, #PropertySource and #ConfigurationProperties
create a component like
#Component
#PropertySource(value = "classpath:filename.properties")
#ConfigurationProperties(prefix = "pref1")
public class Properties{
/**
*should be same in properties pref1.pref2.abc.suf = 1
*It will give u like abc.suf = 1 , def.suf = 2
*/
private Map<String,String> pref2;
//setter getter to use another place//
public Map<String, String> getPref2() {
return pref2;
}
public void setPref2(Map<String, String> pref2) {
this.pref2= pref2;
}
}
use in other class suing #autowired
public class PropertiesShow{
#Autowired
private Properties properties;
public void show(){
System.out.println(properties.getPref2());
}
}

How can I create propirty file for sql queries in spring boot?

I have some sql queries in my java code. Now I want to move it to property file. But I want create another property file for storing my sql queries. I have tried create:
sample.properties
And write in this file:
sql.getCallDetail = //sql query
Then in java class:
PropertySource(value = "sample.properties")
public class ImeiBuilderDefault implements ImeiBuilder {
#Value("${sql.getCallDetail}")
private String getCallDetail;
But my project not build.
Please go through below
** sql.properties **
queries.ex1="select * from ex1"
queries.ex2="select * from ex2"
#PropertySource(value="classpath:sql.properties")
#Configuration
#ConfigurationProperties
public class SqlQueries {
private Map<String,String> queries;
//getters and setters
}
**TEST CLASS **
#Component
public class TestSQLQueries {
#Autowired
private SqlQueries queries;
#PostConstruct
public void init(){
queries.getQueries().entrySet().stream().forEach(entry->{
System.out.println(entry.getKey()+" "+entry.getValue());
});
}
}
please find the demo project https://github.com/BarathArivazhagan/Spring-Task-Sample/tree/master/Spring-SQLQueries-props

What is the best way to test that a spring application context fails to start?

I use the spring-boot-starter-web and spring-boot-starter-test.
Let's say I have a class for binding configuration properties:
#ConfigurationProperties(prefix = "dummy")
public class DummyProperties {
#URL
private String url;
// getter, setter ...
}
Now I want to test that my bean validation is correct. The context should fail to start (with a specfic error message) if the property dummy.value is not set or if it contains an invalid URL. The context should start if the property contains a valid URL. (The test would show that #NotNull is missing.)
A test class would look like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
#IntegrationTest({ "dummy.url=123:456" })
public class InvalidUrlTest {
// my test code
}
This test would fail because the provided property is invalid. What would be the best way to tell Spring/JUnit: "yep, this error is expected". In plain JUnit tests I would use the ExpectedException.
The best way to test Spring application context is to use ApplicationContextRunner
It is described in Spring Boot Reference Documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html#boot-features-test-autoconfig
And there is a quick guide about it:
https://www.baeldung.com/spring-boot-context-runner
Sample usage
private static final String POSITIVE_CASE_CONFIG_FILE =
"classpath:some/path/positive-case-config.yml";
private static final String NEGATIVE_CASE_CONFIG_FILE =
"classpath:some/path/negative-case-config.yml";
#Test
void positiveTest() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(new ConfigDataApplicationContextInitializer())//1
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
.withUserConfiguration(MockBeansTestConfiguration.class)//3
.withPropertyValues("spring.config.location=" + POSITIVE_CASE_CONFIG_FILE)//4
.withConfiguration(AutoConfigurations.of(BookService.class));//5
contextRunner
.run((context) -> {
Assertions.assertThat(context).hasNotFailed();//6
});
}
#Test
void negativeTest() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(new ConfigDataApplicationContextInitializer())//1
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.DEBUG))//2
.withUserConfiguration(MockBeansTestConfiguration.class)//3
.withPropertyValues("spring.config.location=" + NEGATIVE_CASE_CONFIG_FILE)//4
.withConfiguration(AutoConfigurations.of(BookService.class));//5
contextRunner
.run((context) -> {
assertThat(context)
.hasFailed();
assertThat(context.getStartupFailure())
.isNotNull();
assertThat(context.getStartupFailure().getMessage())
.contains("Some exception message");
assertThat(extractFailureCauseMessages(context))
.contains("Cause exception message");
});
}
private List<String> extractFailureCauseMessages(AssertableApplicationContext context) {
var failureCauseMessages = new ArrayList<String>();
var currentCause = context.getStartupFailure().getCause();
while (!Objects.isNull(currentCause)) {//7
failureCauseMessages.add(currentCause.getMessage());
currentCause = currentCause.getCause();
}
return failureCauseMessages;
}
Explanation with examples of similar definitions from Junit5 Spring Boot Test Annotations:
Triggers loading of config files like application.properties or application.yml
Logs ConditionEvaluationReport using given log level when application context fails
Provides class that specifies mock beans, ie. we have #Autowired BookRepository in our BookService and we provide mock BookRepository in MockBeansTestConfiguration. Similar to #Import({MockBeansTestConfiguration.class}) in test class and #TestConfiguration in class with mock beans in normal Junit5 Spring Boot Test
Equivalent of #TestPropertySource(properties = { "spring.config.location=" + POSITIVE_CASE_CONFIG_FILE})
Triggers spring auto configuration for given class, not direct equivalent, but it is similar to using #ContextConfiguration(classes = {BookService.class}) or #SpringBootTest(classes = {BookService.class}) together with #Import({BookService.class}) in normal test
Assertions.class from AssertJ library, there should be static import for Assertions.assertThat, but I wanted to show where this method is from
There should be static import for Objects.isNull, but I wanted to show where this method is from
MockBeansTestConfiguration class:
#TestConfiguration
public class MockBeansTestConfiguration {
private static final Book SAMPLE_BOOK = Book.of(1L, "Stanisław Lem", "Solaris", "978-3-16-148410-0");
#Bean
public BookRepository mockBookRepository() {
var bookRepository = Mockito.mock(BookRepository.class);//1
Mockito.when(bookRepository.findByIsbn(SAMPLE_BOOK.getIsbn()))//2
.thenReturn(SAMPLE_BOOK);
return bookRepository;
}
}
Remarks:
1,2. There should be static import, but I wanted to show where this method is from
Why is that an integration test to begin with? Why are you starting a full blown Spring Boot app for that?
This looks like unit testing to me. That being said, you have several options:
Don't add #IntegrationTest and Spring Boot will not start a web server to begin with (use #PropertySource to pass value to your test but it feels wrong to pass an invalid value to your whole test class)
You can use spring.main.web-environment=false to disable the web server (but that's silly given the point above)
Write a unit test that process that DummyProperties of yours. You don't even need to start a Spring Boot application for that. Look at our own test suite
I'd definitely go with the last one. Maybe you have a good reason to have an integration test for that?
I think the easiest way is:
public class InvalidUrlTest {
#Rule
public DisableOnDebug testTimeout = new DisableOnDebug(new Timeout(5, TimeUnit.SECONDS));
#Rule
public ExpectedException expected = ExpectedException.none();
#Test
public void shouldFailOnStartIfUrlInvalid() {
// configure ExpectedException
expected.expect(...
MyApplication.main("--dummy.url=123:456");
}
// other cases
}

groovy spock mocking spring autowired beans

I have bean:
#Service
public class EQueueBookingService {
#Autowired
public EQueueBookingClient eQueueBookingClient;
And I try to write some test for this bean EQueueBookingService using Spock.
https://code.google.com/p/spock/wiki/SpockBasics
My mock is
class EQueueBookingServiceTest extends Specification {
#Autowired
EQueueBookingService testedService;
EQueueBookingClient eQueueBookingClient = Mock(EQueueBookingClient);
def setup() {
testedService.eQueueBookingClient = eQueueBookingClient;
}
and test method:
...
setup:
CancelBookingResponse response = new CancelBookingResponse();
...
eQueueBookingClient.cancelBooking(_, _) >> response;
when:
def result = testedService.cancelBooking(request);
then:
result != null && result.bookId == bookId
Why eQueueBookingClient doesn't mock?
When I debug it: in test - I see Mock instance, when I go to method - I see real bean instance.
Thanks a lot!
I've found solution!
need implement setter for this client and use it in setup like:
private EQueueBookingClient eQueueBookingClient = Mock(EQueueBookingClient);
def setup() {
testedService.setBookingClient(eQueueBookingClient);
}
If define client in service as public and use = it doesn't work;
For example:
testedService.eQueueBookingClient = eQueueBookingClient;//mocked instance doesn't work

Resources