diff --git a/.travis.yml b/.travis.yml index a8e3eae..9c2cc68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ language: java install: true -script: gradle build --continue +jdk: + - oraclejdk8 +script: ./gradlew build --continue +before_install: + - chmod u+x gradlew before_deploy: - - gradle generatePomFileForMavenPublication + - ./gradlew generatePomFileForMavenPublication deploy: key: secure: Koqvbo26cGgG5QHR9zSGojtvkVtRMxkd3rTejEyWHXnmjbDAnWVLeJf81pz6Vn2lrHbZkCHx+Ec4t3Cptq+v5fXlrgKtjYUORirMK4KWKT0ZLwE41oFo37Y8YVtRLlUiIk7BIteUbY6+p9HTlcQE60TCo9DugVX6Kn8c5tvBtukjckq3uL2/K+IIhtVbHCL6IEMKlQ0O0Wzvhe2m2qTIhAyCjnHgrTM9TxWYqxU5K0ShpL/OgySzXPcJPxy49jPDjp1FmtiMU/Lu5L7l1pzJ8vfWay7rfwJuu0VOy4cCno+8NFNEz5To08eDSIMsHovyqD7CnWkOOeEp9Vkd+D/Td1m2+7xX4UO1HqPmoTaSi2JrBo6FLDdfW+MWd5QGFsSP66hRSaEVV6AicgC1qreFqQQJKxl7XeLlXBt/HCN3qMhh0mKWggN76gnVV9yQY3n1QmtxLR3xgn8DC4xcAepYiDyUsxibVk1k5q1YHmGTpn2AfkrxhB5Dw97f/T8FrG2iO32NopieiugKh7Ln60TEsgDzsscyVic9OGEhQpCHVBI34jSO+/alh8ui+ULcNpreltJNk+tQAGejvMqDTEcYpAXKcuij2CzlFGchnBXnVnJquXP9i/s0s8k/VxARlrY8zUy2AM2lOq0LjNCEPm1whstv2lM2WmUi8mUwdzCFngI= diff --git a/README.MD b/README.MD index 8c9e3d3..ba0f8a3 100644 --- a/README.MD +++ b/README.MD @@ -1,5 +1,5 @@ # Swagger addon for Spring Cloud ZUUL -[![Build Status](https://travis-ci.org/lex-em/zuul-springfox-swagger.svg?branch=develop)](https://travis-ci.org/lex-em/zuul-springfox-swagger) +[![Build Status](https://travis-ci.org/lex-em/zuul-springfox-swagger.svg?branch=develop)](https://travis-ci.org/lex-em/zuul-springfox-swagger)[![Release](https://jitpack.io/v/ru.reliabletech/zuul-springfox-swagger.svg)](https://jitpack.io/#ru.reliabletech/zuul-springfox-swagger) ## Overview @@ -14,6 +14,7 @@ Repository contains library for using springfox swagger UI under ZUUL proxy Swagger addon for Spring Cloud ZUUL is open source software released under the [Apache 2.0 license][1]. ## Usage +### Configuration Plugin extends ZUUL configuration: ```yaml zuul: @@ -31,9 +32,29 @@ zuul: swaggerUrl: api-documentation protocol: https:// ``` +To turn plugin on just put annotation `@EnableZuulSpringfoxSwagger`: +```java +@Configuration +@EnableZuulSpringfoxSwagger +public class YourConfiguration { + ... +} +``` -For use it you should include dependency (** SNAPSHOTS DO NOT AVAILABLE AT THIS MOMENT [#1](/../../issues/1)**): +Also plugin needs Swagger2 and RestTemplate (load balanced is preferable) to be configured. + +If the project configuration do not contain that, then it will be configured automatically (since version 0.1.1). + +Since 0.2.0 version plugin supports versioned api (and other with route transformations via ServiceRouteMapper). +Any `org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper` bean will turn on this approach, but here some nuances, described in #3. +### Dependency ```groovy +repositories { + ... + jcenter() + // or + maven { url 'https://jitpack.io' } +} dependencies { ... compile "ru.reliabletech:zuul-springfox-swagger:${zuulSpringfoxSwaggerVersion}" @@ -41,17 +62,19 @@ dependencies { } ... ``` -And add annotation: -```java -@Configuration -@EnableZuulSpringfoxSwagger -public class YourConfiguration { +Also available snapshots in jitpack version notation develop-SNAPSHOT: +```groovy +repositories { ... + maven { url 'https://jitpack.io' } } +dependencies { + ... + compile "ru.reliabletech:zuul-springfox-swagger:develop-SNAPSHOT" + ... +} +... ``` -Also plugin needs Swagger2 and RestTemplate (load balanced is preferable) to be configured. - -If the project configuration do not contain that, then it will be configured automatically (since version 0.1.1). ## Build @@ -59,4 +82,4 @@ Gradle tasks may be running via `gradle` wrapper - `gradlew`(gradle v4.3.1) from * build: `gradle clean build` -[1]: http://www.apache.org/licenses/LICENSE-2.0.html \ No newline at end of file +[1]: http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/build.gradle b/build.gradle index 481bb7c..19faa03 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ +import java.time.LocalDate import java.time.format.DateTimeFormatter buildscript { @@ -16,25 +17,41 @@ apply plugin: 'maven-publish' // project properties group 'ru.reliabletech' -def artifact = 'zuul-springfox-swagger' -version '0.1.1' +def libName = 'zuul-springfox-swagger' +version '0.2.0' sourceCompatibility = 1.8 +def currentDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) + +//tasks +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} + +artifacts { + archives sourcesJar +} + +task expandDescriptor(type: Copy) { + from 'descriptor.json' + into 'build' + expand(version: project.version, date: currentDate) +} + +build.dependsOn expandDescriptor +compileJava.dependsOn(processResources) // plugin settings publishing { publications { maven(MavenPublication) { from components.java + artifact sourcesJar groupId project.group - artifactId artifact + artifactId libName version project.version - } - } -} -model { - tasks.generatePomFileForMavenPublication { - destination = file("$buildDir/libs/$artifact-${version}.pom") + } } } @@ -42,34 +59,16 @@ jar { manifest { attributes( "Group": group, - "Artifact-Id": artifact, + "Artifact-Id": libName, "Version": version, "Source-Compatibility": sourceCompatibility, - "Build-Time": new Date() + "Build-Time": currentDate ) } } -//tasks -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives sourcesJar -} - -task expandDescriptor(type: Copy) { - from 'descriptor.json' - into 'build' - expand(version: project.version, date: java.time.LocalDate.now().format(DateTimeFormatter.ISO_DATE)) -} - -build.dependsOn expandDescriptor - //dependency management -def springCloudVersion = 'Dalston.SR3' +def springCloudVersion = 'Finchley.SR1' dependencyManagement { imports { @@ -82,12 +81,11 @@ repositories { } dependencies { - compile 'org.springframework.cloud:spring-cloud-starter-eureka' - compile 'org.springframework.cloud:spring-cloud-starter-zuul' - compile 'org.projectlombok:lombok:1.16.18' - compile 'io.springfox:springfox-swagger2:2.7.0' + compileOnly 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' + compileOnly 'org.springframework.cloud:spring-cloud-starter-netflix-zuul' + annotationProcessor 'org.projectlombok:lombok:1.18.4' + implementation 'org.projectlombok:lombok:1.18.4' + compile 'io.springfox:springfox-swagger2:2.9.2' compile 'io.springfox.ui:springfox-swagger-ui-rfc6570:1.0.0' -} -task wrapper(type: Wrapper) { - gradleVersion = '4.3.1' + compileOnly 'org.springframework.boot:spring-boot-configuration-processor:1.5.9.RELEASE' } diff --git a/descriptor.json b/descriptor.json index 008ecd0..c799f5c 100644 --- a/descriptor.json +++ b/descriptor.json @@ -22,7 +22,8 @@ "gpgSign": false }, "files":[ - {"includePattern": "build/libs/(.*)", "uploadPattern": "ru/reliabletech/zuul-springfox-swagger/${version}/\$1"} + {"includePattern": "build/libs/(.*)", "uploadPattern": "ru/reliabletech/zuul-springfox-swagger/${version}/\$1"}, + {"includePattern": "build/publications/maven/pom-default.xml", "uploadPattern": "ru/reliabletech/zuul-springfox-swagger/${version}/pom-${version}.xml"} ], "publish": true } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cee9eba..6c6d336 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1-bin.zip diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..7b743c0 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +env: + GROUP: "ru.reliabletech" \ No newline at end of file diff --git a/src/main/java/ru/reliabletech/zuul/swagger/EnableZuulSpringfoxSwagger.java b/src/main/java/ru/reliabletech/zuul/swagger/EnableZuulSpringfoxSwagger.java index ef0952b..ce8e679 100644 --- a/src/main/java/ru/reliabletech/zuul/swagger/EnableZuulSpringfoxSwagger.java +++ b/src/main/java/ru/reliabletech/zuul/swagger/EnableZuulSpringfoxSwagger.java @@ -2,13 +2,11 @@ import org.springframework.context.annotation.Import; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; /** + * Enabling ZuulSpringfoxSwaggerPlugin annotation. + * * @author Alexandr Emelyanov * on 27.11.2017. */ diff --git a/src/main/java/ru/reliabletech/zuul/swagger/GenericSwaggerService.java b/src/main/java/ru/reliabletech/zuul/swagger/GenericSwaggerService.java deleted file mode 100644 index 1037afe..0000000 --- a/src/main/java/ru/reliabletech/zuul/swagger/GenericSwaggerService.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.reliabletech.zuul.swagger; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -/** - * @author Alexandr Emelyanov - * on 27.11.2017. - */ -@Component -public class GenericSwaggerService implements SwaggerService { - - @Autowired - private RestTemplate restTemplate; - @Autowired - private ServicesSwaggerInfo servicesSwaggerInfo; - - @Override - public ObjectNode getSwaggerDoc(String route) { - return restTemplate.getForObject("{service-url}/{doc-url}", - ObjectNode.class, - servicesSwaggerInfo.getServiceUrl(route), - servicesSwaggerInfo.getSwaggerUrl(route)); - } -} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/ServiceInfo.java b/src/main/java/ru/reliabletech/zuul/swagger/ServiceInfo.java deleted file mode 100644 index 0104d80..0000000 --- a/src/main/java/ru/reliabletech/zuul/swagger/ServiceInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package ru.reliabletech.zuul.swagger; - -import lombok.Data; -import org.springframework.util.StringUtils; - -import java.util.Optional; - -/** - * @author Alexandr Emelyanov - * on 27.11.2017. - */ -@Data -public class ServiceInfo { - - private static final String PROTOCOL_DEFAULT = "http://"; - private String path; - private String serviceId; - private String url; - private String swaggerUrl; - private String protocol = PROTOCOL_DEFAULT; - - public String getServiceUrl() { - return Optional.ofNullable(url) - .filter(url -> !StringUtils.isEmpty(url)) - .orElseGet(() -> String.format("%s%s/", protocol, serviceId)); - } -} \ No newline at end of file diff --git a/src/main/java/ru/reliabletech/zuul/swagger/SwaggerService.java b/src/main/java/ru/reliabletech/zuul/swagger/SwaggerService.java deleted file mode 100644 index c364438..0000000 --- a/src/main/java/ru/reliabletech/zuul/swagger/SwaggerService.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.reliabletech.zuul.swagger; - -import com.fasterxml.jackson.databind.node.ObjectNode; - -/** - * @author Alexandr Emelyanov - * on 27.11.2017. - */ -public interface SwaggerService { - - ObjectNode getSwaggerDoc(String route); - -} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSpringfoxSwaggerConfiguration.java b/src/main/java/ru/reliabletech/zuul/swagger/ZuulSpringfoxSwaggerConfiguration.java index 17e8024..d657e67 100644 --- a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSpringfoxSwaggerConfiguration.java +++ b/src/main/java/ru/reliabletech/zuul/swagger/ZuulSpringfoxSwaggerConfiguration.java @@ -1,18 +1,28 @@ package ru.reliabletech.zuul.swagger; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; +import ru.reliabletech.zuul.swagger.props.ServicesSwaggerInfo; +import ru.reliabletech.zuul.swagger.service.GenericRouteService; +import ru.reliabletech.zuul.swagger.service.RouteService; +import ru.reliabletech.zuul.swagger.service.ServiceRouteMapperRouteService; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** + * Main configuration for ZuulSpringfoxPlugin + * * @author Alexandr Emelyanov * on 27.11.2017. */ +@Configuration @ComponentScan(basePackageClasses = ZuulSpringfoxSwaggerConfiguration.class) @EnableConfigurationProperties(ServicesSwaggerInfo.class) public class ZuulSpringfoxSwaggerConfiguration { @@ -28,4 +38,25 @@ public RestTemplate restTemplate() { return new RestTemplate(); } + /** + * Configure automatically {@link ServiceRouteMapper } based {@link RouteService } + * + * @return + */ + @Bean + @ConditionalOnBean(ServiceRouteMapper.class) + public RouteService versionedRouteService() { + return new ServiceRouteMapperRouteService(); + } + + /** + * Default {@link RouteService} + * + * @return + */ + @Bean + @ConditionalOnMissingBean(RouteService.class) + public RouteService genericRouteService() { + return new GenericRouteService(); + } } diff --git a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerController.java b/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerController.java deleted file mode 100644 index 3481fbe..0000000 --- a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerController.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.reliabletech.zuul.swagger; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; - -/** - * @author Alexandr Emelyanov - * on 27.11.2017. - */ -@RestController -public class ZuulSwaggerController { - - @Autowired - private RestTemplate restTemplate; - @Autowired - private ServicesSwaggerInfo servicesSwaggerInfo; - @Autowired - private SwaggerService swaggerService; - - @GetMapping("/api-docs") - public JsonNode getApiDocs(@RequestParam("route") String route) { - String path = servicesSwaggerInfo.getServicePath(route) - .orElseThrow(NotFoundException::new) - .replace("/**", ""); - ObjectNode swaggerDocumentation = swaggerService.getSwaggerDoc(route); - swaggerDocumentation.set("host", new TextNode("")); - swaggerDocumentation.set("basePath", new TextNode(servicesSwaggerInfo.getPrefix() + path)); - return swaggerDocumentation; - } -} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerResourceProvider.java b/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerResourceProvider.java deleted file mode 100644 index 0c67ebc..0000000 --- a/src/main/java/ru/reliabletech/zuul/swagger/ZuulSwaggerResourceProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package ru.reliabletech.zuul.swagger; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import springfox.documentation.swagger.web.SwaggerResource; -import springfox.documentation.swagger.web.SwaggerResourcesProvider; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Alexandr Emelyanov - * on 27.11.2017. - */ -@Slf4j -@Primary -@Component -public class ZuulSwaggerResourceProvider implements SwaggerResourcesProvider { - - @Autowired - private RestTemplate restTemplate; - @Autowired - private ServicesSwaggerInfo servicesSwaggerInfo; - @Autowired - private SwaggerService swaggerService; - - @Override - public List get() { - return servicesSwaggerInfo.getRouteNames() - .stream() - .map(route -> { - ObjectNode swaggerDocumentation = swaggerService.getSwaggerDoc(route); - return generateSwaggerDocumentationResource(route, - "/api-docs?route=" + route, - swaggerDocumentation.get("swagger").asText()); - }) - .collect(Collectors.toList()); - } - - private SwaggerResource generateSwaggerDocumentationResource(String name, String location, String version) { - SwaggerResource swaggerResource = new SwaggerResource(); - swaggerResource.setName(name); - swaggerResource.setLocation(location); - swaggerResource.setSwaggerVersion(version); - return swaggerResource; - } - -} \ No newline at end of file diff --git a/src/main/java/ru/reliabletech/zuul/swagger/controller/ZuulSwaggerController.java b/src/main/java/ru/reliabletech/zuul/swagger/controller/ZuulSwaggerController.java new file mode 100644 index 0000000..45f8d0e --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/controller/ZuulSwaggerController.java @@ -0,0 +1,26 @@ +package ru.reliabletech.zuul.swagger.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.reliabletech.zuul.swagger.service.SwaggerService; + +/** + * Controller for swagger-ui documentation requests + * + * @author Alexandr Emelyanov + * on 27.11.2017. + */ +@RestController +public class ZuulSwaggerController { + + @Autowired + private SwaggerService swaggerService; + + @GetMapping("/api-docs") + public JsonNode getApiDocs(@RequestParam("route") String route) { + return swaggerService.getSwaggerDoc(route); + } +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/NotFoundException.java b/src/main/java/ru/reliabletech/zuul/swagger/exception/NotFoundException.java similarity index 85% rename from src/main/java/ru/reliabletech/zuul/swagger/NotFoundException.java rename to src/main/java/ru/reliabletech/zuul/swagger/exception/NotFoundException.java index 8481fac..d020d2c 100644 --- a/src/main/java/ru/reliabletech/zuul/swagger/NotFoundException.java +++ b/src/main/java/ru/reliabletech/zuul/swagger/exception/NotFoundException.java @@ -1,4 +1,4 @@ -package ru.reliabletech.zuul.swagger; +package ru.reliabletech.zuul.swagger.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/src/main/java/ru/reliabletech/zuul/swagger/props/ServiceInfo.java b/src/main/java/ru/reliabletech/zuul/swagger/props/ServiceInfo.java new file mode 100644 index 0000000..7d4e720 --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/props/ServiceInfo.java @@ -0,0 +1,24 @@ +package ru.reliabletech.zuul.swagger.props; + +import lombok.Data; + +/** + * Service info + * + * @author Alexandr Emelyanov + * on 27.11.2017. + */ +@Data +class ServiceInfo { + + private String path; + + private String serviceId; + + private String url; + + private String swaggerUri; + + private String protocol = ""; + +} \ No newline at end of file diff --git a/src/main/java/ru/reliabletech/zuul/swagger/ServicesSwaggerInfo.java b/src/main/java/ru/reliabletech/zuul/swagger/props/ServicesSwaggerInfo.java similarity index 56% rename from src/main/java/ru/reliabletech/zuul/swagger/ServicesSwaggerInfo.java rename to src/main/java/ru/reliabletech/zuul/swagger/props/ServicesSwaggerInfo.java index eeb27b7..f9c57ff 100644 --- a/src/main/java/ru/reliabletech/zuul/swagger/ServicesSwaggerInfo.java +++ b/src/main/java/ru/reliabletech/zuul/swagger/props/ServicesSwaggerInfo.java @@ -1,4 +1,4 @@ -package ru.reliabletech.zuul.swagger; +package ru.reliabletech.zuul.swagger.props; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -9,6 +9,8 @@ import java.util.Set; /** + * Extended Zuul configuration for plugin purposes + * * @author Alexandr Emelyanov * on 27.11.2017. */ @@ -17,11 +19,14 @@ public class ServicesSwaggerInfo { public static final String DEFAULT_SWAGGER_API_URL = "v2/api-docs"; + public static final String PROTOCOL_DEFAULT = "http://"; private String prefix; private String defaultSwaggerUrl = DEFAULT_SWAGGER_API_URL; + private String defaultProtocol = PROTOCOL_DEFAULT; + private Map routes = new HashMap<>(); public Set getRouteNames() { @@ -30,18 +35,24 @@ public Set getRouteNames() { public String getSwaggerUrl(String route) { return Optional.ofNullable(routes.get(route)) - .map(ServiceInfo::getSwaggerUrl) - .orElse(defaultSwaggerUrl); + .map(ServiceInfo::getSwaggerUri) + .orElse(defaultSwaggerUrl); + } + + public String getDefaultProtocol(String route) { + return Optional.ofNullable(routes.get(route)) + .map(ServiceInfo::getProtocol) + .orElse(defaultProtocol); } - public String getServiceUrl(String route) { + public Optional getServiceUrl(String route) { return Optional.ofNullable(routes.get(route)) - .map(ServiceInfo::getServiceUrl) - .orElse(""); + .map(ServiceInfo::getUrl) + .map(url -> String.format("%s%s/", getDefaultProtocol(route), url)); } public Optional getServicePath(String route) { return Optional.ofNullable(routes.get(route)) - .map(ServiceInfo::getPath); + .map(ServiceInfo::getPath); } } \ No newline at end of file diff --git a/src/main/java/ru/reliabletech/zuul/swagger/service/GenericRouteService.java b/src/main/java/ru/reliabletech/zuul/swagger/service/GenericRouteService.java new file mode 100644 index 0000000..97b22e7 --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/service/GenericRouteService.java @@ -0,0 +1,23 @@ +package ru.reliabletech.zuul.swagger.service; + +import org.springframework.beans.factory.annotation.Autowired; +import ru.reliabletech.zuul.swagger.props.ServicesSwaggerInfo; + +/** + * General implementation + * + * @author Alexandr Emelyanov + * on 15.02.19. + */ +public class GenericRouteService implements RouteService { + + @Autowired + private ServicesSwaggerInfo servicesSwaggerInfo; + + @Override + public String getPath(String route) { + return servicesSwaggerInfo + .getServicePath(route) + .orElse(route); + } +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/service/GenericSwaggerService.java b/src/main/java/ru/reliabletech/zuul/swagger/service/GenericSwaggerService.java new file mode 100644 index 0000000..9c89576 --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/service/GenericSwaggerService.java @@ -0,0 +1,51 @@ +package ru.reliabletech.zuul.swagger.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import ru.reliabletech.zuul.swagger.exception.NotFoundException; +import ru.reliabletech.zuul.swagger.props.ServicesSwaggerInfo; + +/** + * General implementation + * + * @author Alexandr Emelyanov + * on 27.11.2017. + */ +@Component +public class GenericSwaggerService implements SwaggerService { + + @Autowired + private RestTemplate restTemplate; + @Autowired + private ServicesSwaggerInfo servicesSwaggerInfo; + @Autowired + private RouteService routeService; + + @Override + public ObjectNode getSwaggerDoc(String route) { + ObjectNode swaggerDocumentation = getOriginalSwaggerDoc(route); + swaggerDocumentation.set("host", new TextNode("")); + swaggerDocumentation.set("basePath", new TextNode(servicesSwaggerInfo.getPrefix() + routeService.getPath(route))); + return swaggerDocumentation; + } + + @Override + public ObjectNode getOriginalSwaggerDoc(String route) { + String serviceUrl = servicesSwaggerInfo.getServiceUrl(route) + .orElseGet(() -> servicesSwaggerInfo.getDefaultProtocol() + route); + String url = String.format("%s/%s", + serviceUrl, + servicesSwaggerInfo.getSwaggerUrl(route)); + try { + return restTemplate.getForObject(url, ObjectNode.class); + } catch (IllegalStateException e) { + if (e.getMessage() == null || !e.getMessage().startsWith("No instances available for")) { + throw e; + } + throw new NotFoundException(); + } + } +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/service/RouteService.java b/src/main/java/ru/reliabletech/zuul/swagger/service/RouteService.java new file mode 100644 index 0000000..db7e79f --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/service/RouteService.java @@ -0,0 +1,18 @@ +package ru.reliabletech.zuul.swagger.service; + +/** + * Service for mapping routes to proxy path + * + * @author Alexandr Emelyanov + * on 15.02.19. + */ +public interface RouteService { + + /** + * Perform route mapping to proxy path + * + * @param route + * @return + */ + String getPath(String route); +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/service/ServiceRouteMapperRouteService.java b/src/main/java/ru/reliabletech/zuul/swagger/service/ServiceRouteMapperRouteService.java new file mode 100644 index 0000000..f3cbcbf --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/service/ServiceRouteMapperRouteService.java @@ -0,0 +1,26 @@ +package ru.reliabletech.zuul.swagger.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper; +import ru.reliabletech.zuul.swagger.props.ServicesSwaggerInfo; + +/** + * Realization? based on {@link ServiceRouteMapper } + * + * @author Alexandr Emelyanov + * on 15.02.19. + */ +public class ServiceRouteMapperRouteService implements RouteService { + + @Autowired + private ServicesSwaggerInfo servicesSwaggerInfo; + @Autowired + private ServiceRouteMapper serviceRouteMapper; + + @Override + public String getPath(String route) { + return servicesSwaggerInfo + .getServicePath(route) + .orElseGet(() -> serviceRouteMapper.apply(route)); + } +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/service/SwaggerService.java b/src/main/java/ru/reliabletech/zuul/swagger/service/SwaggerService.java new file mode 100644 index 0000000..8cddbe1 --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/service/SwaggerService.java @@ -0,0 +1,40 @@ +package ru.reliabletech.zuul.swagger.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Service for operations over connected services swagger documentations + * + * @author Alexandr Emelyanov + * on 27.11.2017. + */ +public interface SwaggerService { + + /** + * Obtain original documentation of service, represented by route + * + * @param route + * @return + */ + ObjectNode getOriginalSwaggerDoc(String route); + + /** + * Obtain modified for proxy's swagger-ui documentation of service, represented by route + * + * @param route + * @return + */ + ObjectNode getSwaggerDoc(String route); + + /** + * Obtain swagger documentation version for service, represented by route + * + * @param route + * @return + */ + default String getSwaggerVersion(String route) { + ObjectNode swaggerDocumentation = getOriginalSwaggerDoc(route); + return swaggerDocumentation == null ? "" : swaggerDocumentation.get("swagger").asText(); + } + +} diff --git a/src/main/java/ru/reliabletech/zuul/swagger/swagger/ZuulSwaggerResourceProvider.java b/src/main/java/ru/reliabletech/zuul/swagger/swagger/ZuulSwaggerResourceProvider.java new file mode 100644 index 0000000..672bed8 --- /dev/null +++ b/src/main/java/ru/reliabletech/zuul/swagger/swagger/ZuulSwaggerResourceProvider.java @@ -0,0 +1,59 @@ +package ru.reliabletech.zuul.swagger.swagger; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import ru.reliabletech.zuul.swagger.props.ServicesSwaggerInfo; +import ru.reliabletech.zuul.swagger.service.SwaggerService; +import springfox.documentation.swagger.web.SwaggerResource; +import springfox.documentation.swagger.web.SwaggerResourcesProvider; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Provide swagger resources for swagger-ui using discovery client available services list and configured statically route mappings + * + * @author Alexandr Emelyanov + * on 27.11.2017. + */ +@Slf4j +@Primary +@Component +public class ZuulSwaggerResourceProvider implements SwaggerResourcesProvider { + + @Autowired + private ServicesSwaggerInfo servicesSwaggerInfo; + @Autowired + private SwaggerService swaggerService; + @Autowired + private DiscoveryClient discoveryClient; + + @Override + public List get() { + return Stream.concat(discoveryClient.getServices().stream(), servicesSwaggerInfo.getRouteNames().stream()) + .map(this::generateSwaggerDocumentationResource) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private SwaggerResource generateSwaggerDocumentationResource(String route) { + String swaggerVersion; + try { + swaggerVersion = swaggerService.getSwaggerVersion(route); + } catch (Exception e) { + log.error(String.format("Some error during obtain swagger documentation for route %s", route), e); + return null; + } + SwaggerResource swaggerResource = new SwaggerResource(); + swaggerResource.setName(route); + swaggerResource.setLocation("/api-docs?route=" + route); + swaggerResource.setSwaggerVersion(swaggerVersion); + return swaggerResource; + } + +} \ No newline at end of file