diff --git a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDefinitionsContext.java b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDefinitionsContext.java
index 5751909..0925d67 100644
--- a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDefinitionsContext.java
+++ b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDefinitionsContext.java
@@ -1,5 +1,6 @@
package com.satish.central.docs.config.swagger;
+import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -11,37 +12,39 @@
import springfox.documentation.swagger.web.SwaggerResource;
/**
- *
* @author satish sharma
*
* In-Memory store to hold API-Definition JSON
*
*/
@Component
-@Scope(scopeName=ConfigurableBeanFactory.SCOPE_SINGLETON)
public class ServiceDefinitionsContext {
-
- private final ConcurrentHashMap 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 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());
- }
+
+ private final ConcurrentHashMap serviceDescriptions;
+
+ private ServiceDefinitionsContext() {
+ serviceDescriptions = new ConcurrentHashMap<>();
+ }
+
+ public void addServiceDefinition(String serviceName, String serviceDescription) {
+ serviceDescriptions.put(serviceName.toUpperCase(), serviceDescription);
+ }
+
+ public String getSwaggerDefinition(String serviceId) {
+ return this.serviceDescriptions.get(serviceId);
+ }
+
+ public List getSwaggerDefinitions() {
+ return serviceDescriptions.entrySet()
+ .parallelStream()
+ .map(service -> {
+ final SwaggerResource swaggerResource = new SwaggerResource();
+ swaggerResource.setLocation("/service/" + service.getKey());
+ swaggerResource.setName(service.getKey());
+ swaggerResource.setSwaggerVersion("2.0");
+ return swaggerResource;
+ })
+ .sorted(Comparator.comparing(SwaggerResource::getName))
+ .collect(Collectors.toList());
+ }
}
diff --git a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDescriptionUpdater.java b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDescriptionUpdater.java
index 3def0ea..5330dd8 100644
--- a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDescriptionUpdater.java
+++ b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/ServiceDescriptionUpdater.java
@@ -1,99 +1,127 @@
package com.satish.central.docs.config.swagger;
-import java.time.LocalDate;
-import java.util.List;
-import java.util.Optional;
-
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
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.core.ParameterizedTypeReference;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
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;
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.springframework.http.HttpMethod.GET;
/**
- *
* @author satish sharma
*
- * Periodically poll the service instaces and update the in memory store as key value pair
+ * Periodically poll the service instaces and update the in memory store as key value pair
*
*/
+@Slf4j
@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= "${swagger.config.refreshrate}")
- 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 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 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 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 "";
- }
- }
+
+ private static final String SWAGGER_RESOURCES = "/swagger-resources";
+
+ @Autowired
+ private DiscoveryClient discoveryClient;
+
+ private final RestTemplate template;
+
+ public ServiceDescriptionUpdater() {
+ this.template = new RestTemplate();
+ }
+
+ @Autowired
+ private ServiceDefinitionsContext definitionContext;
+
+ @Scheduled(fixedDelayString = "${swagger.config.refreshrate}")
+ public void refreshSwaggerConfigurations() {
+ log.debug("Starting Service Definition Context refresh");
+
+ discoveryClient.getServices().stream().forEach(serviceId -> {
+
+ log.debug("Attempting service definition refresh for Service : {} ", serviceId);
+ List serviceInstances = discoveryClient.getInstances(serviceId);
+
+ if (serviceInstances == null || serviceInstances.isEmpty()) { //Should not be the case kept for failsafe
+ log.info("No instances available for service : {} ", serviceId);
+ } else {
+ serviceInstances.stream()
+ .findFirst()
+ .ifPresent(instance -> {
+ getSwaggerURL(instance).parallelStream()
+ .forEach(swaggerResponse -> {
+ Optional jsonData = getSwaggerDefinitionForAPI(serviceId, instance, swaggerResponse.getLocation());
+ if (jsonData.isPresent()) {
+ String content = getJSON(serviceId, jsonData.get());
+ definitionContext.addServiceDefinition(serviceId + " - " + swaggerResponse.getName(), content);
+ } else {
+ log.error("Skipping service id : {} Error : Could not get Swagegr definition from API ", serviceId);
+ }
+ });
+ log.info("Service Definition Context Refreshed at : {}", LocalDate.now());
+ });
+ }
+ });
+ }
+
+ private Set getSwaggerURL(ServiceInstance instance) {
+ final String url = instance.getUri() + SWAGGER_RESOURCES;
+ try {
+
+ final ResponseEntity> response = template.exchange(url, GET, null, new ParameterizedTypeReference>() {
+ });
+
+ if (HttpStatus.OK.equals(response.getStatusCode())) {
+ return response.getBody().stream().collect(Collectors.toSet());
+ }
+ } catch (Exception e) {
+ log.error("Error while getting swagger definition for URL : {} Error : {} ", url, e.getMessage());
+ }
+ return Collections.EMPTY_SET;
+ }
+
+ private Optional getSwaggerDefinitionForAPI(String serviceName, ServiceInstance instance, String url) {
+ log.debug("Accessing the SwaggerDefinition JSON for Service : {} : URL : {} ", serviceName, url);
+ try {
+ Object jsonData = template.getForObject(instance.getUri() + url, Object.class);
+ return Optional.of(jsonData);
+ } catch (Exception ex) {
+ log.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) {
+ log.error("Error : {} ", e.getMessage());
+ return "";
+ }
+ }
+
+ @Getter
+ @Setter
+ public static class SwaggerResponse {
+ private String location, swaggerVersion, name, url;
+ }
+
}
diff --git a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/SwaggerUIConfiguration.java b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/SwaggerUIConfiguration.java
index d0779a3..dfb6d85 100644
--- a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/SwaggerUIConfiguration.java
+++ b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/config/swagger/SwaggerUIConfiguration.java
@@ -29,19 +29,15 @@ public class SwaggerUIConfiguration {
private ServiceDefinitionsContext definitionContext;
@Bean
- public RestTemplate configureTempalte(){
+ public RestTemplate createRestTemplate(){
return new RestTemplate();
}
-
- @Primary
+
@Bean
@Lazy
- public SwaggerResourcesProvider swaggerResourcesProvider(InMemorySwaggerResourcesProvider defaultResourcesProvider, RestTemplate temp) {
- return () -> {
- List resources = new ArrayList<>(defaultResourcesProvider.get());
- resources.clear();
- resources.addAll(definitionContext.getSwaggerDefinitions());
- return resources;
- };
+ @Primary
+ public SwaggerResourcesProvider createSwaggerResourcesProvider() {
+ return () -> definitionContext.getSwaggerDefinitions();
}
}
+
diff --git a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/web/ServiceDefinitionController.java b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/web/ServiceDefinitionController.java
index 1489054..0db4aa6 100644
--- a/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/web/ServiceDefinitionController.java
+++ b/centralized-swagger-docs/documentation-app/src/main/java/com/satish/central/docs/web/ServiceDefinitionController.java
@@ -1,12 +1,15 @@
package com.satish.central.docs.web;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.satish.central.docs.config.swagger.ServiceDefinitionsContext;
+import java.util.Optional;
+
/**
*
* @author satish sharma
@@ -19,11 +22,11 @@ public class ServiceDefinitionController {
@Autowired
private ServiceDefinitionsContext definitionContext;
-
- @GetMapping("/service/{servicename}")
- public String getServiceDefinition(@PathVariable("servicename") String serviceName){
-
- return definitionContext.getSwaggerDefinition(serviceName);
-
+
+ @GetMapping("/service/{serviceName}")
+ public ResponseEntity> getServiceDefinition(@PathVariable String serviceName){
+ return Optional.ofNullable(definitionContext.getSwaggerDefinition(serviceName))
+ .map(ResponseEntity::ok)
+ .orElseGet(ResponseEntity.notFound()::build);
}
}