Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openapi-generator): add openapi-generator for server and refacto… #84

Merged
merged 14 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions openapi/get-profile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
openapi: "3.0.0"

info:
version: 1.0.0
title: Get Profile
servers:
- url: http://localhost:8080/

paths:
/api/profile:
get:
summary: Get Profile Info
operationId: getProfile
tags:
- profile
responses:
"200":
description: Profile Details
content:
application/json:
schema:
$ref: "#/components/schemas/ProfileDetailsResponse"
default:
description: server error
content:
application/json:
schema:
$ref: "#/components/schemas/ServerError"

components:
schemas:
ProfileDetailsResponse:
type: object
required: [ username, role, fullName ]
properties:
username:
type: string
role:
type: string
fullName:
type: string
ServerError:
type: object
3 changes: 3 additions & 0 deletions server/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[**openapi/*.yml]
indent_size = 2

[*.{kt,kts}]
ktlint_standard = disabled
ktlint_standard_wrapping = enabled
Expand Down
44 changes: 44 additions & 0 deletions server/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
id("io.gitlab.arturbosch.detekt") version "1.23.6"
id("org.sonarqube") version "5.0.0.4638"
id("org.openapi.generator") version "7.8.0"
}

group = "mu.muse"
Expand Down Expand Up @@ -55,6 +56,8 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation(kotlin("stdlib-jdk8"))
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
implementation("io.swagger.core.v3:swagger-annotations:2.2.22")
implementation("jakarta.validation:jakarta.validation-api:3.1.0") // `useSpringBoot3` param requires it
}

tasks.named<Test>("test") {
Expand Down Expand Up @@ -89,3 +92,44 @@ sonar { // self hosted sonar qube
property("sonar.projectName", "muse-project")
}
}

sourceSets {
main {
kotlin {
srcDir(layout.buildDirectory.dir("openapi/src/main").get().toString())
}
}
}

openApiGenerate {
apiPackage = "mu.muse.rest.api"
modelPackage = "mu.muse.rest.dto"
generateApiTests = false
generateModelTests = false
generateApiDocumentation = false
generateModelDocumentation = false
inputSpecRootDirectory = "$rootProjectDir/openapi"
outputDir = layout.buildDirectory.dir("openapi").get().toString()
validateSpec = true
generatorName = "kotlin-spring"
configOptions = mapOf(
"idea" to "true",
"sourceFolder" to "src/main/kotlin",
"useSpringBoot3" to "true",
"serializationLibrary" to "jackson",
"useCoroutines" to "true",
"useTags" to "true",
"exceptionHandler" to "false",
"interfaceOnly" to "true",
"skipDefaultInterface" to "true",
"documentationProvider" to "none",
)
}

tasks.compileKotlin {
dependsOn("openApiGenerate")
}

tasks.test {
dependsOn("openApiGenerate")
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import mu.muse.rest.favorite.FavoriteEndpoint
import mu.muse.rest.instruments.CreateInstrumentEndpoint
import mu.muse.rest.instruments.DeleteInstrumentByIdEndpoint
import mu.muse.rest.instruments.EditInstrumentEndpoint
import mu.muse.rest.instruments.GetInstrumentsByCriteriaEndpoint
import mu.muse.rest.instruments.GetInstrumentByIdEndpoint
import mu.muse.rest.instruments.GetInstrumentManufacturersEndpoint
import mu.muse.rest.instruments.GetInstrumentMaterialsEndpoint
import mu.muse.rest.instruments.GetInstrumentTypesEndpoint
import mu.muse.rest.instruments.GetInstrumentsByCriteriaEndpoint
import mu.muse.rest.login.BasicLoginEndpoint
import mu.muse.rest.profile.GetProfileEndpoint
import mu.muse.rest.registration.RegistrationEndpoint
Expand All @@ -19,12 +19,12 @@ import mu.muse.usecase.CreateInstrument
import mu.muse.usecase.DeleteInstrumentById
import mu.muse.usecase.EditInstrument
import mu.muse.usecase.GetCountries
import mu.muse.usecase.GetInstrumentsByCriteriaPaginated
import mu.muse.usecase.GetInstrumentById
import mu.muse.usecase.GetInstrumentManufacturers
import mu.muse.usecase.GetInstrumentMaterials
import mu.muse.usecase.GetInstrumentTypes
import mu.muse.usecase.GetInstrumentsByCriteria
import mu.muse.usecase.GetInstrumentsByCriteriaPaginated
import mu.muse.usecase.GetProfile
import mu.muse.usecase.RegisterUser
import org.springframework.context.annotation.Bean
Expand Down
1 change: 0 additions & 1 deletion server/app/src/main/kotlin/mu/muse/rest/EndpointURL.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package mu.muse.rest
const val AUTH_BASIC_LOGIN = "/api/auth/login"

const val API = "/api"
const val API_PROFILE = "$API/profile"
const val API_INSTRUMENTS = "$API/instruments"
const val API_INSTRUMENT_BY_ID = "$API/instrument/{id:\\d+}" // id must be a number
const val API_DELETE_INSTRUMENT_BY_ID = "$API/instrument/{id:\\d+}/delete" // id must be a number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package mu.muse.rest.profile

import mu.muse.domain.user.Username
import mu.muse.rest.API_PROFILE
import mu.muse.rest.api.ProfileApi
import mu.muse.rest.dto.ProfileDetailsResponse
import mu.muse.usecase.GetProfile
import mu.muse.usecase.dto.ProfileDetails
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.http.ResponseEntity
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.RestController
import java.security.Principal

@RestController
class GetProfileEndpoint(private val showProfile: GetProfile) {
class GetProfileEndpoint(private val getProfile: GetProfile) : ProfileApi {

@GetMapping(API_PROFILE)
fun getProfile(principal: Principal): ProfileDetails {
override fun getProfile(): ResponseEntity<ProfileDetailsResponse> {
val principal = SecurityContextHolder.getContext().authentication
val username = Username.from(principal.name)
return showProfile.execute(username)
val profileDetails = getProfile.execute(username)
return profileDetails.toRestResponse()
}
}

fun ProfileDetails.toRestResponse() = ResponseEntity.ok().body(
ProfileDetailsResponse(
username = this.username,
role = this.role,
fullName = this.fullName,
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import mu.muse.usecase.dto.ProfileDetails
class GetProfileUseCase(private val userExtractor: UserExtractor) : GetProfile {

override fun execute(username: Username): ProfileDetails {
val user = userExtractor.findByUsername(username) ?: throw ShowProfileError.UserNotFound(username)
val user = userExtractor.findByUsername(username)
?: throw ShowProfileError.UserNotFound(username)
return ProfileDetails.from(user)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.tngtech.archunit.junit.AnalyzeClasses
import com.tngtech.archunit.junit.ArchTest
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition
import com.tngtech.archunit.library.Architectures
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition

@AnalyzeClasses(
packages = ["mu.muse"],
Expand All @@ -26,13 +25,6 @@ class CleanArchitectureGuard {
.adapter("persistence", "mu.muse.persistence..")
.adapter("rest", "mu.muse.rest..")

@ArchTest
@Suppress("VariableNaming")
val `endpoints must not depend on each other` = SlicesRuleDefinition.slices()
.matching("..rest.(*)..")
.should()
.notDependOnEachOther()

@ArchTest
@Suppress("VariableNaming")
val `server business logic should depends only on approved packages` = ArchRuleDefinition.classes()
Expand Down
2 changes: 1 addition & 1 deletion tools/scripts/server/runUnitTests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ set -e
currentDir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
rootDir="$currentDir/../../../"

(cd "$rootDir/server" && ./gradlew test)
(cd "$rootDir/server" && ./gradlew test --stacktrace)
Loading