Dynamically Use Spring Annotations for Redis and Elasticsearch - spring-boot

Consider the following class
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.redis.core.RedisHash;
#Document(indexName = "index_name")
#RedisHash("filter_name")
class Index {
}
I have a usecase where in usage of elasticsearch or redis would be by the user's choice, one way of implementation is having separate class for each, which works, but isn't elegent.
Is it possible to disable a specific annotation in runtime.

Related

Disabling actuator metrics

How can we disable actuator completely? If we disable the actuator endpoint does it mean that actuator has disabled that functionality as well? I doubt that.
I have nothing defined in my application.properties which means only /health and /info should be exposed. But in my code
package com.rbc.rbc.controllers;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
public class RestController1 {
SimpleMeterRegistry sm;
#GetMapping("/welcome")
public void test(){
SimpleMeterRegistry m=(SimpleMeterRegistry)(Metrics.globalRegistry.getRegistries().toArray())[0];
List<Meter> met=m.getMeters();
for (Meter mtr:met) {
System.out.println(mtr.getId().getName());
if(mtr.getId().getName().equals("http.server.requests")){
mtr.measure().forEach(mi -> {
System.out.println(mi.getStatistic());
System.out.println(mi.getValue());
});
}}
}
}
I am still able to see the metrics programmatically. Does that mean that only the endpoint is disabled but the metrics are still collected? Is there a way to disable it completely. I am saying so because I do not want the metrics to be collected I need only the health endpoint for now.

Hide a method in springdoc-openapi based on a condition

We are working on a spring boot project which uses the springdoc-openapi library to generate the swagger document. We have a requirement where we need to hide a few of the APIs in a controller.
Spring boot does provide a way to hide/show a controller using the #ConditionalOnProperty tag. but spring boot does not have a way to hide/show a method based on property.
Does springdoc-openapi provide a way to filter the operation after scanning all controllers? or any other way to hide/show some APIs on swagger based on a property.
You can override springdoc'S OperationService#isHidden behavior like that:
package alex.swaggerhidden.service;
import org.springdoc.core.GenericParameterService;
import org.springdoc.core.OperationService;
import org.springdoc.core.PropertyResolverUtils;
import org.springdoc.core.RequestBodyService;
import org.springdoc.core.SecurityService;
import org.springdoc.core.providers.JavadocProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Optional;
#Component
#Primary
public class MyOperationService extends OperationService {
private final boolean exposeHidden;
public MyOperationService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService, SecurityService securityParser, PropertyResolverUtils propertyResolverUtils, Optional<JavadocProvider> javadocProvider, #Value("${app.expose-hidden}") boolean exposeHidden) {
super(parameterBuilder, requestBodyService, securityParser, propertyResolverUtils, javadocProvider);
this.exposeHidden = exposeHidden;
}
#Override
public boolean isHidden(Method method) {
if (exposeHidden) {
return false;
}
return super.isHidden(method);
}
}
Now hidden behavior will be ignored if app.expose-hidden set to TRUE
app:
expose-hidden: true

MongoRepository Delete Method

Good afternoon fellow coders!
I have spent the last hour looking to delete a single document from my mongo "testCollection". I would like to make use of the MongoRepository delete / deleteAll methods. However, it does not remove the document. It persists regardless of how many times I run the test class method. No errors are reported and the user has readWrite permissions in the database. I am able to run the mongo command to remove the newly created test document.
I have read about using the mongo template and create it for the deletion to be performed. I'm happy to do that but I would rather keep it as simple as possible.
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoId;
#Data
#Document(collection = "testCollection")
public class TestClass {
#MongoId
private String id;
private String name;
public TestClass(String name) {
this.name = name;
}
}
Test Class Mongo Repository interface
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public List<TestClass> findAllByName(String name);
public void deleteAllByIdIn(List<TestClass> list);
}
Test Method
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
#RunWith(SpringRunner.class)
#TestPropertySource(value = {"classpath:application.properties"})
#AutoConfigureMockMvc
public class testClassTest {
#Autowired
private TestClassRepository testClassRepository;
#Test
public void crudTest() {
TestClass testObj = new TestClass("Test");
testClassRepository.save(testObj);
List<TestClass> testClassList = testClassRepository.findAllByName("Test");
Assert.assertEquals(1, testClassList.size());
TestClass test = testClassList.get(0);
testClassRepository.deleteAllByIdIn(testClassList);
// Fails this assertion: Found 1, expected 0.
Assert.assertEquals(0, testClassRepository.findAllByName("Test").size());
}
}
As anyone else experienced a similar issue? If so, how'd you go about resolving it?
Thanks!
Additions to original post:
Here is the mongo query generated by the MongoRepository. It appears that it is not actually adding the "remove" mongo command to the query. Query: { "name" : "Test"}, Fields: {}, Sort: {}
With a stroke of dumb luck I managed to figure out the issue. The problem was with the type of identifier annotation I was using. This explanation from another stackoverflow user (What is use of #MongoId in Spring Data MongoDB over #Id?) had me revisit this aspect of the model.
I switched the identifier annotation from #MongoId to #Id. Since I have both JPA and MongoDB annotations I needed to make sure I chose the one from the org.springframework.data.annotation package rather than the javax.persistance package.
Hope this explanation helps others!

Spring 5 reactive websocket and multiple endpoints?

We are doing a little hackathon at work and I wanted to try some new technology to get away from the usual controller.
I started using Spring Webflux with reactive WebSockets and everything is working fine so far. I configured my WebSocket handler as follows:
import my.handler.DomWebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import java.util.HashMap;
import java.util.Map;
#Configuration
public class AppConfig implements WebFluxConfigurer {
#Autowired
private WebSocketHandler domWebSocketHandler;
#Bean
public HandlerMapping webSocketMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/event-emitter", domWebSocketHandler);
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(1);
mapping.setUrlMap(map);
return mapping;
}
#Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
After some more research, I learned that it is best practice to work with one connection per client.
Furthermore using more than a web-socket per browsing session for the same application seems overkill since you can use pub/sub channels. See answer here
Is there a way to restrict the connections per client and use only one endpoint for all required client "requests" or would it be better to create additional endpoints (like you would with a normal controller)?
Thank you in advance for the help.

what is the idiomatic way to use ConfigurationProperties and EnableConfigurationProperties in tests?

i am trying to setup unit tests for some elements to be used within a spring(-boot) application, and i struggled with setup around ConfigurationProperties and EnableConfigurationProperties. the way i finally got it to work doesn't seem consistent with the examples that i have seen in that i have witnessed needing both ConfigurationProperties and EnableConfigurationProperties on my configuration class, which doesn't seem right, and i was hoping that someone might provide some guidance.
here is a simplified example:
JavaTestConfiguration.java
package com.kerz;
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 javax.validation.constraints.NotNull;
#Configuration
#ConfigurationProperties
#EnableConfigurationProperties
public class JavaTestConfiguration {
public void setFoo(String foo) {
this.foo = foo;
}
#NotNull
String foo;
#Bean
String foo() {
return foo;
}
}
JavaTestConfigurationTest.java
package com.kerz;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JavaTestConfiguration.class})
#TestPropertySource("classpath:test.properties")
public class JavaTestConfigurationTest {
#Autowired
String foo;
#Test
public void shouldWork() throws Exception {
assertEquals("foo", "bar", foo);
}
}
test.properties
foo=bar
Your test is more integration test if you are starting Spring context. Therefore you should test also production spring configuration.
I would advise not to create testing configuration. Use one production configuration for testing.
You are also using #TestPropertySource annotation, which is used when you need to define test specific properties. If you can test with PROD configuration do not use it.

Resources