diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 94c7c41..63065e0 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -32,8 +32,8 @@ springBoot { } dependencies { - // SWAGGER - FRAMEWORK - implementation("io.springfox:springfox-boot-starter:3.0.0") + // Migrating from SpringFox + implementation("org.springdoc:springdoc-openapi-ui:1.5.13") // SPRING FRAMEWORK AND CORE implementation("org.springframework.boot:spring-boot-starter-web") diff --git a/api/src/main/kotlin/com/cubetiqs/web/config/SpringDocConfig.kt b/api/src/main/kotlin/com/cubetiqs/web/config/SpringDocConfig.kt new file mode 100644 index 0000000..e064c08 --- /dev/null +++ b/api/src/main/kotlin/com/cubetiqs/web/config/SpringDocConfig.kt @@ -0,0 +1,60 @@ +package com.cubetiqs.web.config + +import io.swagger.v3.oas.models.ExternalDocumentation +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.info.Info +import io.swagger.v3.oas.models.info.License +import org.springdoc.core.GroupedOpenApi +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class SpringDocConfig { + companion object { + private val PUBLIC_API_PATH get() = "/public/**" + private val ADMIN_API_PATH get() = "/admin/**" + private val DEFAULT_API_PATH get() = "/**" + } + + @Bean + fun defaultApi(): GroupedOpenApi { + return GroupedOpenApi.builder() + .group("default-api") + .pathsToMatch(DEFAULT_API_PATH) + .pathsToExclude("/error", PUBLIC_API_PATH, ADMIN_API_PATH) + .packagesToScan("com.cubetiqs.web") + .build() + } + + @Bean + fun publicApi(): GroupedOpenApi { + return GroupedOpenApi.builder() + .group("public-api") + .pathsToMatch(PUBLIC_API_PATH) + .build() + } + + @Bean + fun adminApi(): GroupedOpenApi { + return GroupedOpenApi.builder() + .group("admin-api") + .pathsToMatch(ADMIN_API_PATH) + .build() + } + + @Bean + fun cubetiqOpenAPI(): OpenAPI { + return OpenAPI() + .info( + Info().title("CUBETIQ Web API") + .description("CUBETIQ Spring Web API Application") + .version("v0.0.1") + .license(License().name("Apache 2.0").url("https://cubetiqs.com")) + ) + .externalDocs( + ExternalDocumentation() + .description("CUBETIQ Web Wiki Documentation") + .url("https://cubetiqs.com") + ) + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/config/SpringFoxConfig.kt b/api/src/main/kotlin/com/cubetiqs/web/config/SpringFoxConfig.kt deleted file mode 100644 index 6d5e46f..0000000 --- a/api/src/main/kotlin/com/cubetiqs/web/config/SpringFoxConfig.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.cubetiqs.web.config - -import com.cubetiqs.web.property.AppProperties -import com.cubetiqs.web.util.AppConstants -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.info.BuildProperties -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import springfox.documentation.builders.ApiInfoBuilder -import springfox.documentation.builders.PathSelectors -import springfox.documentation.builders.RequestHandlerSelectors -import springfox.documentation.service.* -import springfox.documentation.spi.DocumentationType -import springfox.documentation.spi.service.contexts.SecurityContext -import springfox.documentation.spring.web.plugins.Docket - -@Configuration -class SpringFoxConfig @Autowired constructor( - val buildProperties: BuildProperties, - val appProperties: AppProperties, -) { - companion object { - private const val AUTHORIZATION_KEY: String = "Bearer" - private const val AUTHORIZATION_HEADER: String = "Authorization" - } - - private fun defaultAuth(): List { - val authorizationScope = AuthorizationScope("global", "accessEverything") - val authorizationScopes: Array = arrayOfNulls(1) - authorizationScopes[0] = authorizationScope - return listOf(SecurityReference(AUTHORIZATION_KEY, authorizationScopes)) - } - - private fun securityContext(): SecurityContext? { - return SecurityContext.builder() - .securityReferences(defaultAuth()) - .build() - } - - @Bean - fun api(): Docket { - return Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.basePackage(AppConstants.BASE_PACKAGE_API_DOCS)) - .paths(PathSelectors.any()) - .build() - .securityContexts(listOf(securityContext())) - .securitySchemes(listOf(apiKey())) - .apiInfo(apiInfo()) - - // Allow to configurable custom pagination of request in swagger ui // - // .directModelSubstitute(Pageable::class.java, SwaggerPageable::class.java) - // .directModelSubstitute(UrlParamable::class.java, UrlParamableSwaggerView::class.java) - } - - private fun apiInfo(): ApiInfo { - return ApiInfoBuilder() - .description(appProperties.appDescription) - .title(appProperties.appName) - .version(buildProperties.version) - .contact( - Contact( - "CUBETIQ Solution", - "https://cubetiqs.com", - "ops@cubetiqs.com", - ) - ) - .build() - } - - private fun apiKey(): ApiKey { - return ApiKey( - AUTHORIZATION_KEY, - AUTHORIZATION_HEADER, - "header", - ) - } -} diff --git a/api/src/main/kotlin/com/cubetiqs/web/controller/ApiDoc.kt b/api/src/main/kotlin/com/cubetiqs/web/controller/ApiDoc.kt index 8f4456b..a5a7918 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/controller/ApiDoc.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/controller/ApiDoc.kt @@ -1,11 +1,11 @@ package com.cubetiqs.web.controller +import io.swagger.v3.oas.annotations.Hidden import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.servlet.view.RedirectView -import springfox.documentation.annotations.ApiIgnore -@ApiIgnore +@Hidden @Controller class ApiDoc { @GetMapping(value = [ "/api-doc", "/api-docs"]) diff --git a/api/src/main/kotlin/com/cubetiqs/web/controller/BaseController.kt b/api/src/main/kotlin/com/cubetiqs/web/controller/BaseController.kt index 8a23dcd..624b846 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/controller/BaseController.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/controller/BaseController.kt @@ -4,10 +4,10 @@ import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity interface BaseController { - fun response( - data: Any?, + fun response( + data: T?, status: HttpStatus = HttpStatus.OK, - ): ResponseEntity { + ): ResponseEntity { return ResponseEntity.status(status).body(data) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/controller/IndexController.kt b/api/src/main/kotlin/com/cubetiqs/web/controller/IndexController.kt index 0032504..492949c 100644 --- a/api/src/main/kotlin/com/cubetiqs/web/controller/IndexController.kt +++ b/api/src/main/kotlin/com/cubetiqs/web/controller/IndexController.kt @@ -1,6 +1,10 @@ package com.cubetiqs.web.controller +import com.cubetiqs.web.model.response.ApiInfoAuthorResponse +import com.cubetiqs.web.model.response.ApiInfoResponse +import com.cubetiqs.web.model.response.HealthResponse import com.cubetiqs.web.util.RouteConstants +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.info.BuildProperties import org.springframework.core.env.Environment @@ -8,9 +12,8 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import springfox.documentation.annotations.ApiIgnore -@ApiIgnore +@Tag(name = "Miscellaneous") @RestController @RequestMapping(RouteConstants.INDEX) class IndexController @Autowired constructor( @@ -18,25 +21,27 @@ class IndexController @Autowired constructor( private val environment: Environment, ) : BaseController { @GetMapping - fun index(): ResponseEntity { - val view = mutableMapOf() - view["info"] = "API Operation is running normally on ${environment.activeProfiles.joinToString(separator = ",")}" - view["name"] = buildProperties.name - view["service"] = buildProperties.artifact - view["version"] = buildProperties.version - view["date"] = buildProperties.time - view["commit"] = buildProperties["commitId"] - view["authors"] = listOf( - mapOf( - "name" to "Sambo Chea", - "email" to "sombochea@cubetiqs.com", - ), - mapOf( - "name" to "CUBETIQ OSS", - "email" to "oss@cubetiqs.com", - ) + fun index(): ResponseEntity { + val authors = listOf( + ApiInfoAuthorResponse(name = "Sambo Chea", email = "sombochea@cubetiqs.com"), + ApiInfoAuthorResponse(name = "CUBETIQ OSS", email = "oss@cubetiqs.com"), ) + val response = ApiInfoResponse( + info = "API Operation is running normally on ${environment.activeProfiles.joinToString(separator = ",")}", + name = buildProperties.name, + service = buildProperties.artifact, + version = buildProperties.version, + date = buildProperties.time.toString(), + commit = buildProperties["commitId"], + authors = authors, + ) + return response(response) + } - return response(view) + @GetMapping("/health") + fun health(): ResponseEntity { + return response( + HealthResponse.UP + ) } } \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/model/response/ApiInfoResponse.kt b/api/src/main/kotlin/com/cubetiqs/web/model/response/ApiInfoResponse.kt new file mode 100644 index 0000000..fec4996 --- /dev/null +++ b/api/src/main/kotlin/com/cubetiqs/web/model/response/ApiInfoResponse.kt @@ -0,0 +1,20 @@ +package com.cubetiqs.web.model.response + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(name = "ApiInfoResponse", description = "ApiInfoResponse") +data class ApiInfoResponse( + val name: String, + val info: String, + val service: String, + val version: String, + val date: String, + val commit: String, + val authors: Collection = listOf(), +) : BaseRequestModel + +@Schema(name = "ApiInfoAuthorResponse", description = "ApiInfoAuthorResponse") +data class ApiInfoAuthorResponse( + val name: String, + val email: String, +) : BaseRequestModel \ No newline at end of file diff --git a/api/src/main/kotlin/com/cubetiqs/web/model/response/HealthResponse.kt b/api/src/main/kotlin/com/cubetiqs/web/model/response/HealthResponse.kt new file mode 100644 index 0000000..7f718cc --- /dev/null +++ b/api/src/main/kotlin/com/cubetiqs/web/model/response/HealthResponse.kt @@ -0,0 +1,16 @@ +package com.cubetiqs.web.model.response + +import io.swagger.v3.oas.annotations.media.Schema + +@Schema(name = "HealthResponse", description = "HealthResponse") +data class HealthResponse( + @Schema(name = "status", description = "Status for the service") + val status: String, +) : BaseRequestModel { + companion object { + private const val STATUS_UP = "UP" + private const val STATUS_DOWN = "DOWN" + val UP get() = HealthResponse(STATUS_UP) + val DOWN get() = HealthResponse(STATUS_DOWN) + } +} diff --git a/api/src/main/resources/application.yml b/api/src/main/resources/application.yml index 76cdbcd..ded4c8b 100644 --- a/api/src/main/resources/application.yml +++ b/api/src/main/resources/application.yml @@ -13,4 +13,9 @@ cubetiq: logging: file: path: ${LOGGING_FILE_PATH:${cubetiq.app.data-dir}/logs/} - name: ${logging.file.path}/app.log \ No newline at end of file + name: ${logging.file.path}/app.log +springdoc: + api-docs: + enabled: true + swagger-ui: + path: /swagger-ui