Common Swagger Configuration For Micro services n Spring Boot

Posted By : Himanshu Kumar | 31-Dec-2018

Introduction:-

 

In the event that you've at any point working with APIs, chances are, you've known about Swagger. Swagger is the most broadly utilized tooling ecosystem for creating APIs with the OpenAPI Specification (OAS). Swagger comprises of both open source and in addition proficient apparatuses, obliging pretty much every need and use case. Swagger used to comprise of the determination and an extensive biological system of?tools to actualize the detail. These instruments incorporate everything from front-end UIs, low-level code libraries and business API the board arrangements. In 2015, SmartBear Software gave the Swagger detail to the Linux Foundation and renamed the particular to the OpenAPI Specification. SmartBear additionally turned into the establishing individual from the OpenAPI Initiative (OAI), a body to oversee the improvement of the OAS in an open and straightforward way.

  

Aggregate Services into a Single Swagger:-

At this point, most organizations that have pointed their ship towards a type of administration engineering (in popular expressions, microservices) have additionally found an API traveler to facilitate the agonies of designers that are attempting to devour administrations that they don't know anything about. Swagger is a prevalent alternative with regards to self-reporting API pioneers. In Spring Boot, with merely two conditions and a little setup you can be running with a "Swagger" endpoint to hit that uncovered all accessible REST endpoints. This is extraordinary, no uncertainty. In any case, when you begin to grow to beyond what a bunch of administrations it can end up awkward to monitor each administration's Swagger page. Likewise, contingent upon how your heap balancer is set up it might be difficult to focus on an individual administration's Swagger page from behind the heap balancer. Therefore and a touch of interest, I chose to investigate conglomerating numerous swaggers into their very own administration. Whatever is left of this post will delineate how to approach doing this with the goal that you can have a solitary Swagger that looks something like the picture beneath.

 

Configure Single swagger for a microservice architecture using Eureka:-

For configuring multiple services into a single swagger we will use Eureka server to getting information of all microservices.

Java configuration swagger :

Swagger dependency :

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>


<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>

 

Step 1: - create an application in spring boot with above dependency and configure swaggers below classes.

ServiceDefinationContext.java which will get the service definition from the Eureka server and store it on in memory.

 

package com.example.demo.configuration;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import springfox.documentation.swagger.web.SwaggerResource;

@Component
@Scope(scopeName=ConfigurableBeanFactory.SCOPE_SINGLETON)
public class ServiceDefinitionsContext {
	
	private final ConcurrentHashMap<String,String> serviceDescriptions; 
	 
	 private ServiceDefinitionsContext(){
		 serviceDescriptions = new ConcurrentHashMap<>();
	 }
	 
	 public void addServiceDefinition(String serviceName, String serviceDescription){
		 serviceDescriptions.put(serviceName, serviceDescription);
	 }
	 
	 public String getSwaggerDefinition(String serviceId){
		 return this.serviceDescriptions.get(serviceId);
	 }
	 
	 public List<SwaggerResource> getSwaggerDefinitions(){
			return  serviceDescriptions.entrySet().stream().map( serviceDefinition -> {
				 SwaggerResource resource = new SwaggerResource();
				 resource.setLocation("/service/"+serviceDefinition.getKey());

				 resource.setName(serviceDefinition.getKey());
				 resource.setSwaggerVersion("2.0");	 
				 return resource;
			 }).collect(Collectors.toList());
		 }
}

 

ServiceDescriptionUpdater.java this class scheduled for keep update the swagger with recently added or removed services from the eureka server.

 

package com.example.demo.configuration;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class ServiceDescriptionUpdater {

	private static final Logger logger = LoggerFactory.getLogger(ServiceDescriptionUpdater.class);
	
	private static final String DEFAULT_SWAGGER_URL="/v2/api-docs";
	private static final String KEY_SWAGGER_URL="swagger_url";
	
	@Autowired
	private DiscoveryClient discoveryClient;

	private final RestTemplate template;
	
	public ServiceDescriptionUpdater(){
		this.template = new RestTemplate();
	}
	
	@Autowired
	private ServiceDefinitionsContext definitionContext;
	
	@Scheduled(fixedDelayString= "5000")
	public void refreshSwaggerConfigurations(){
		logger.debug("Starting Service Definition Context refresh");
		
		discoveryClient.getServices().stream().forEach(serviceId -> {
			logger.debug("Attempting service definition refresh for Service : {} ", serviceId);
			List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceId);
			if(serviceInstances == null || serviceInstances.isEmpty()){ //Should not be the case kept for failsafe
				logger.info("No instances available for service : {} ",serviceId);
			}else{
				ServiceInstance instance = serviceInstances.get(0);
				String swaggerURL =  getSwaggerURL( instance);
				
				Optional<Object> jsonData = getSwaggerDefinitionForAPI(serviceId, swaggerURL);
				
				if(jsonData.isPresent()){
					String content = getJSON(serviceId, jsonData.get());
					definitionContext.addServiceDefinition(serviceId, content);
				}else{
					logger.error("Skipping service id : {} Error : Could not get Swagegr definition from API ",serviceId);
				}
				
				logger.info("Service Definition Context Refreshed at :  {}",LocalDate.now());
			}
		});
	}
	
	private String getSwaggerURL( ServiceInstance instance){
		String swaggerURL = instance.getMetadata().get(KEY_SWAGGER_URL);
		return swaggerURL != null ? instance.getUri()+swaggerURL : instance.getUri()+DEFAULT_SWAGGER_URL;
	}
	
	private Optional<Object> getSwaggerDefinitionForAPI(String serviceName, String url){
		logger.debug("Accessing the SwaggerDefinition JSON for Service : {} : URL : {} ", serviceName, url);
		try{
			Object jsonData = template.getForObject(url, Object.class);
			return Optional.of(jsonData);
		}catch(RestClientException ex){
			logger.error("Error while getting service definition for service : {} Error : {} ", serviceName, ex.getMessage());
			return Optional.empty();
		}
		 
	}

	public String getJSON(String serviceId, Object jsonData){
		try {
			return new ObjectMapper().writeValueAsString(jsonData);
		} catch (JsonProcessingException e) {
			logger.error("Error : {} ", e.getMessage());
			return "";
		}
	}
}

 

SwaggerUIConfiguration.java this class will configure the resource for all microservices into this services and display the swagger-ui.

 

package com.example.demo.configuration;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
@Component
public class SwaggerUIConfiguration {
	@Autowired
	private ServiceDefinitionsContext definitionContext;
	
	@Bean
	public RestTemplate configureTempalte(){
		return new RestTemplate();
	}
	
    @Primary
    @Bean
    @Lazy
    public SwaggerResourcesProvider swaggerResourcesProvider(InMemorySwaggerResourcesProvider defaultResourcesProvider, RestTemplate temp) {
        return () -> {          
            List<SwaggerResource> resources = new ArrayList<>(defaultResourcesProvider.get());
            resources.clear();
            resources.addAll(definitionContext.getSwaggerDefinitions());
            return resources;
        };
    }
}


 

About Author

Author Image
Himanshu Kumar

Himanshu Kumar is a seasoned Backend Developer with extensive experience in the industry. He has a deep understanding and hands-on expertise in a range of technologies, including Core Java, Spring-Boot, Hibernate, Apache Kafka messaging queue, and blockchain development with Ethereum, Tron, and smart contract development. He is also well-versed in relational databases like MySQL and PostgreSQL. His proficiency in API implementation, web services, development testing, and deployment has contributed to the successful delivery of various client projects, such as Wethio Exchange, Hedgex Exchange, Envoychain payment gateway based on Stellar assets, and more. With a creative mind and excellent analytical skills, Himanshu enjoys staying up to date with the latest technologies and exploring new ideas to bring value to his work.

Request for Proposal

Name is required

Comment is required

Sending message..