Spring REST doc at centralized portal - springfox

I have tried doing some poc on springfox swagger with spring boot. It does generate swagger ui on the same host and port as my application is running.
http://localhost:8080/swagger-ui.html
My application is composed of multiple microservices deployed on a cloud infrastructure. This way i may end up having multiple swagger hub ui as
http://microservice1:8080/swagger-ui.html
http://microservice2:8081/swagger-ui.html
http://microservice3:8082/swagger-ui.html
How i can host all of my springfox swagger hub application on same host. So that i can have a consolidate webpage to have all my api documentation at single place.
For spring rest doc, i could generate a single html document using asciidoctor for my microservice. Again i had different html docs for different microservices.
Is this feature available with spring rest doc? or in spring cloud where consolidate all my documents in one single web application.

Create a Zuul filter concept. Create a Zuul filter service and add swagger2 dependency in your pom.xml and create a configuration class in this service as mentioned below
#EnableSwagger2
#Configuration
#Component
#Primary
public class SwaggerConfig implements SwaggerResourcesProvider {
#Override
public List<SwaggerResource> get() {
List resources = new ArrayList<>();
resources.add(swaggerResource("microservice1", "/microservice1/v2/api-docs", "2.0"));
resources.add(swaggerResource("microservice2", "/microservice2/v2/api-docs", "2.0"));
resources.add(swaggerResource("microservice3", "/microservice3/v2/api-docs", "2.0"));
return resources;
}
private SwaggerResource swaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
Add below mentioned configuration in the other three microservices(microservice1,microservice2,microservice3..)
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.stackoverflow.login.controller"))
.paths(PathSelectors.regex("/.*")).build().apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder().title("Spring Boot REST API").description("Employee Management REST API")
.contact(new Contact("stackoverflow", "www.stackoverflow.com", ""))
.license("Apache 2.0").licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html").version("1.0.0")
.build();
}
}

Related

Spring-boot Redis JMS JUnit

I am using Redis Server for message broker in my spring boot application.
Is there any simple way to Junit my publish and receive API?
e.g :
Publisher :
public String publish(Object domainObj) {
template.convertAndSend(topic.getTopic(), domainObj.toString());
return "Event Published";
}
Receiver :
public class Receiver implements MessageListener {
#Override
public void onMessage(Message message, byte[] bytes) {
System.out.println("Consumed Message {}" + message);
}
}
I am using JedisConnectionFactory and RedisMessageListenerContainer and RedisTemplate for my implementation
#Configuration
#EnableRedisRepositories
public class RedisConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName("localhost");
configuration.setPort(6379);
return new JedisConnectionFactory(configuration);
}
#Bean
public RedisTemplate<String, Object> template() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new JdkSerializationRedisSerializer());
template.setValueSerializer(new JdkSerializationRedisSerializer());
template.setEnableTransactionSupport(true);
template.afterPropertiesSet();
return template;
}
#Bean
public ChannelTopic topic() {
return new ChannelTopic("common-channel");
}
#Bean
public MessageListenerAdapter messageListenerAdapter() {
return new MessageListenerAdapter(new Receiver());
}
#Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory());
container.addMessageListener(messageListenerAdapter(), topic());
return container;
}
Unit Testing Receiver and Publisher implementation is quite straight.
JUnit 5 coupled with Mockito extension should do the job.
For example for testing that :
public String publish(Object domainObj) {
template.convertAndSend(topic.getTopic(), domainObj.toString());
return "Event Published";
}
I expect that topic and template be fields of the current class.
These fields could be set by constructor.
So you could write something that check that convertAndSend() is eventually executed with the correct parameters :
#Mock
RedisTemplate<String, Object> templateMock;
#Test
void publish(){
Topic topicFixture = new Topic(...);
Object domainObjFixture = new FooBar(...);
Publisher publisher = new Publisher(templateMock, topicFixture);
//when
publisher.publish(domainObjFixture);
// then
Mockito.verify(templateMock)
.convertAndSend(topicFixture.getTopic(), domainObjFixture);
}
But I don't think that the unit test of these two classes be enough because it never tests the final things : the JMS processing performed by Redis backend.
Particularly, the RedisConfig part that you set with specific things as serializers that have important side effects on the processing.
For my part, I try to always write integration or partial integration tests for Redis backend stuffs to ensure a good no regression harness.
The java embedded-redis library is good for that. It allows to start a redis server
on localhost (works on Windows as well as on Linux).
Starting and stopping the redis server is as simple as :
RedisServer redisServer = new RedisServer(6379);
redisServer.start();
// do some work
redisServer.stop();
Move the start() in the #BeforeEach and the stop() in the #AfterEach and the server is ready.
Then it still requires some adjustments to ensure that the redis configuration specified in Spring is well setup during the tests while using your local redis server and not the "real" redis server. Not always simple to set but great when it is done !
The simplest way to unit test this is to use embedded-redis module. What you do is in BeforeAll you can start embedded Redis and stop the embedded Redis in AfterAll method.
You can also PostConstruct PreDestroy annotations to accomplish this.
If you're looking for Junit5 then you can find the code in my repo here
See BootstrapRedis annotation and their usage here
https://github.com/sonus21/rqueue/blob/7ef545c15985ef91ba719f070f7cc80745525047/rqueue-core/src/test/java/com/github/sonus21/rqueue/core/RedisScriptFactoryTest.java#L40

Springdoc-openapi Global Headers not getting added in Swagger-UI when using GroupedOpenApi bean [duplicate]

When using GroupedOpenApi to define an API group, the common set of parameters that are added to every endpoint is not present in the parameters list.
Below are the respective codes
#Bean
public GroupedOpenApi v1Apis() {
return GroupedOpenApi.builder().group("v1 APIs")
// hide all v2 APIs
.pathsToExclude("/api/v2/**", "/v2/**")
// show all v1 APIs
.pathsToMatch("/api/v1/**", "/v1/**")
.build();
}
And the class to add the Standard Headers to all the endpoints
#Component
public class GlobalHeaderAdder implements OperationCustomizer {
#Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
operation.addParametersItem(new Parameter().$ref("#/components/parameters/ClientID"));
operation.addSecurityItem(new SecurityRequirement().addList("Authorization"));
List<Parameter> parameterList = operation.getParameters();
if (parameterList!=null && !parameterList.isEmpty()) {
Collections.rotate(parameterList, 1);
}
return operation;
}
}
Actual Output
Expected Output
Workaround
Adding the paths to be included/excluded in the application properties file solves the error. But something at the code level will be much appreciated.
Attach the required OperationCustomizerobject while building the Api Group.
#Bean
public GroupedOpenApi v1Apis(GlobalHeaderAdder globalHeaderAdder) {
return GroupedOpenApi.builder().group("v1 APIs")
// hide all v2 APIs
.pathsToExclude("/api/v2/**", "/v2/**")
// show all v1 APIs
.pathsToMatch("/api/v1/**", "/v1/**")
.addOperationCustomizer(globalHeaderAdded)
.build();
}
Edit: Answer updated with reference to #Value not providing values from application properties Spring Boot
Alternative to add and load OperationCustomizer in the case you declare yours open api groups by properties springdoc.group-configs[0].group= instead definition by Java code in a Spring Configuration GroupedOpenApi.builder().
#Bean
public Map<String, GroupedOpenApi> configureGroupedsOpenApi(Map<String, GroupedOpenApi> groupedsOpenApi, OperationCustomizer operationCustomizer) {
groupedsOpenApi.forEach((id, groupedOpenApi) -> groupedOpenApi.getOperationCustomizers()
.add(operationCustomizer));
return groupedsOpenApi;
}

How to write Junit for a service component class using webtestclient in Spring 5

I'm using Springboot2, Spring5 and reactive-webflux in my Java microservices.
I have a service class which I want to test using webtestclient:-
#Service("authenticationProvider")
public class CommonAuthenticationProvider implements AuthenticationProvider {
#Override
public AccessToken getUserAccessToken(Tuple2<String, WebClient> serviceConnectionDetails, MultiValueMap<String, String> queryParams) {
return serviceConnectionDetails._2
.post()
.uri(builder -> builder
.path(serviceConnectionDetails._1)
.queryParams(queryParams)
.build())
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(AccessToken.class)
.block();
}
}
Here serviceConnectionDetails._2 is a webclient instance. I want to write a JUnit test to mock this webclient and test the method getUserAccessToken(). Please assist as I had tried many things like mockmvc, mockRestServiceServer but nothing worked. Later I came to know that, I cannot user mockRestServiceServer since it used to mock RestTemplate and not WebClient. I can test controller class methods using webtestclient but not this at service class
This should be supported in a future Spring Framework version with MockRestServiceServer; see SPR-15286.
For now, the only solution is to use a separate library for that, such as okhttp's MockWebServer.

DataSourceInitializer is not working on Spring boot 1.2

I am new to Spring boot.I want to add some sql while database is creating like seed data.
#Value("classpath:com/foo/sql/db-test-data.sql")
private Resource dataScript;
#Bean
public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) {
final DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource);
initializer.setDatabasePopulator(databasePopulator());
return initializer;
}
private DatabasePopulator databasePopulator() {
final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(dataScript);
return populator;
}
props.put("hibernate.query.substitutions", "true 1, false 0");
props.put("hibernate.hbm2ddl.auto", "create-drop");
props.put("hibernate.show_sql", "false");
props.put("hibernate.format_sql", "true");
I have perform this action.But it not working on spring boot.Can any one help me.
Sometimes spring-boot gets more in the way than it helps; IMHO this is especially so with web applications.
What you can do to get around this is to rename the bean that you define.
#Bean("springBootPleaseStopTellingMeHowYouThinkDataSourceInitializer")
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
// build it.
}
Now, to turn off the built in bit that looks for data.sql in application.properties
spring.datasource.initialize=false
There, now boot is booted out of the way.
You can take advantage of Spring Boot database initialization capabilities. The simplest way is to place a "data.sql" file in the root of the classpath. So you just need to:
Change your sql file name to "data.sql".
Place it in "src/main/resources".
Spring Boot will automatically pick up the file and use it to initialize the database on startup.
You can check the documentation if you need to customize the file name, location, etc.

Does Spring Support JSON Configuration?

Does anyone know if Spring has any extensions that allow for configuring its ApplicationContext via JSON (or really any other format) rather than XML? I couldn't find anything in the official docs, but I was wondering if there were any other open source extensions that could allow this.
Just to be clear, I'm not talking about configuring SpringMVC to set up a RESTful JSON-based web service or anything like that, just if it's possible to do Spring app configuration via JSON instead of XML.
As far as I know there is no project to support JSON as configuration source. It should be relatively easy to kick-start, (Spring container has no dependency on XML, it is just a way to construct bean definitions). However it is much more work than you might think.
Note that Spring provides xml-schema to assist you in writing correct XML. You won't get that much in JSON. Also many DSLs were built on top of Spring XML and custom namespaces support (spring-integration, mule-esb and others use it).
If you hate XML (many do), try out Java Configuration, available since 3.0 and improved in 3.1:
#Configuration
public class MyBeans {
#Bean
public Foo foo() {
return new Foo();
}
#Bean
public Bar bar() {
return new Bar(foo());
}
#Bean
public Buzz buzz() {
Buzz buzz = new Buzz();
buzz.setFoo(foo());
return buzz;
}
}
Interesting fact: thanks to some fancy proxying, foo() is called exactly once here, even though referenced twice.
Try JSConf library available on maven central, it's support Properties, HOCON and JSON format.
You can inject values from external file to your service and more !
Sample usage of JavaConfig :
You data stored on file app.conf
{
"root":{
"simpleConf":{
"url":"Hello World",
"port":12,
"aMap":{
"key1":"value1",
"key2":"value2"
},
"aList":[
"value1",
"value2"
]
}}
You service where your configuration must be inject
#Service("service")
public class Service {
#Autowired
private ConfigBean configBean;
}
Declare a interface to access your configuration values from your service
#ConfigurationProperties("root/simpleConf")
public interface ConfigBean {
String getUrl();
int getPort();
Map getAMap();
List getAList();
}
And your Spring configuration bean :
#Configuration
public class ContextConfiguration {
#Bean
public static ConfigurationFactory configurationFactory() {
return new ConfigurationFactory().withResourceName("app.conf") //
.withScanPackage("org.jsconf.core.sample.bean");
}
}