chore(gateway, ping-service, security): streamline configurations, remove redundancies, and improve resilience
- Removed `MdcCorrelationFilter` and simplified correlation ID management using Micrometer Tracing. - Updated `SecurityConfig` in `gateway` with enhanced role-based access and standardized JWT validation. - Added new `@Profile` annotations in `ping-service` to exclude certain components during testing. - Refactored and removed legacy `application-keycloak.yaml` and consolidated settings into the primary `application.yaml`. - Adjusted Gradle scripts to clean up dependency declarations and improve modularity. - Simplified CORS and Gateway route configurations for better maintainability.
This commit is contained in:
+2
@@ -3,6 +3,7 @@ package at.mocode.ping.application
|
||||
import at.mocode.ping.domain.Ping
|
||||
import at.mocode.ping.domain.PingRepository
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
@@ -14,6 +15,7 @@ import kotlin.uuid.Uuid
|
||||
* Hier darf Spring (@Service, @Transactional) verwendet werden, da es "Application Logic" ist.
|
||||
*/
|
||||
@Service
|
||||
@Profile("!test") // Nicht im Test-Profil laden, damit wir Mocks nutzen können
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
class PingService(
|
||||
private val repository: PingRepository
|
||||
|
||||
+6
-2
@@ -14,6 +14,10 @@ class PingJpaEntity(
|
||||
val message: String,
|
||||
val createdAt: Instant
|
||||
) {
|
||||
// Default constructor for JPA
|
||||
protected constructor() : this(UUID.randomUUID(), "", Instant.now())
|
||||
// The default constructor for JPA
|
||||
// Protected is fine for Hibernate, but the Kotlin compiler might complain about visibility.
|
||||
// We can make it private or internal if needed, but protected is standard.
|
||||
// To suppress the warning "effectively private", we can just leave it as is or make it public/internal.
|
||||
// Let's try making it internal to satisfy Kotlin while keeping it hidden from public API.
|
||||
internal constructor() : this(UUID.randomUUID(), "", Instant.now())
|
||||
}
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ package at.mocode.ping.infrastructure.persistence
|
||||
|
||||
import at.mocode.ping.domain.Ping
|
||||
import at.mocode.ping.domain.PingRepository
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Repository
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
@@ -10,6 +11,7 @@ import kotlin.uuid.toKotlinUuid
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
@Repository
|
||||
@Profile("!test") // Nicht im Test-Profil laden, damit wir Mocks nutzen können
|
||||
class PingRepositoryAdapter(
|
||||
private val jpaRepository: SpringDataPingRepository
|
||||
) : PingRepository {
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ class PingController(
|
||||
)
|
||||
|
||||
// Fallback
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
fun fallbackPing(simulate: Boolean, ex: Exception): EnhancedPingResponse {
|
||||
logger.warn("Circuit breaker fallback triggered: {}", ex.message)
|
||||
return EnhancedPingResponse(
|
||||
|
||||
+24
-11
@@ -2,16 +2,22 @@ package at.mocode.ping.service
|
||||
|
||||
import at.mocode.ping.application.PingUseCase
|
||||
import at.mocode.ping.domain.Ping
|
||||
import at.mocode.ping.infrastructure.persistence.PingRepositoryAdapter
|
||||
import at.mocode.ping.infrastructure.web.PingController
|
||||
import at.mocode.ping.test.TestPingServiceApplication
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.context.annotation.Primary
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||
@@ -22,21 +28,29 @@ import java.time.Instant
|
||||
*/
|
||||
@WebMvcTest(
|
||||
controllers = [PingController::class],
|
||||
excludeAutoConfiguration = [
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration::class,
|
||||
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration::class
|
||||
]
|
||||
properties = ["spring.aop.proxy-target-class=true"]
|
||||
)
|
||||
@Import(PingControllerIntegrationTest.TestConfig::class)
|
||||
@ContextConfiguration(classes = [TestPingServiceApplication::class])
|
||||
@ActiveProfiles("test")
|
||||
@Import(PingControllerIntegrationTest.PingControllerIntegrationTestConfig::class)
|
||||
class PingControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@TestConfiguration
|
||||
class TestConfig {
|
||||
@Bean
|
||||
@Autowired
|
||||
@Qualifier("pingUseCaseIntegrationMock")
|
||||
private lateinit var pingUseCase: PingUseCase
|
||||
|
||||
@Configuration
|
||||
class PingControllerIntegrationTestConfig {
|
||||
@Bean("pingUseCaseIntegrationMock")
|
||||
@Primary
|
||||
fun pingUseCase(): PingUseCase = mockk(relaxed = true)
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingRepositoryAdapter(): PingRepositoryAdapter = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -46,8 +60,7 @@ class PingControllerIntegrationTest {
|
||||
|
||||
// For endpoints that require the use-case, the relaxed mock is sufficient,
|
||||
// but we still provide deterministic ping data.
|
||||
val useCase = TestConfig().pingUseCase()
|
||||
every { useCase.executePing(any()) } returns Ping(
|
||||
every { pingUseCase.executePing(any()) } returns Ping(
|
||||
message = "Simple Ping",
|
||||
timestamp = Instant.parse("2023-10-01T10:00:00Z")
|
||||
)
|
||||
|
||||
+23
-17
@@ -1,8 +1,10 @@
|
||||
package at.mocode.ping.service
|
||||
|
||||
import at.mocode.ping.domain.Ping
|
||||
import at.mocode.ping.infrastructure.web.PingController
|
||||
import at.mocode.ping.application.PingUseCase
|
||||
import at.mocode.ping.domain.Ping
|
||||
import at.mocode.ping.infrastructure.persistence.PingRepositoryAdapter
|
||||
import at.mocode.ping.infrastructure.web.PingController
|
||||
import at.mocode.ping.test.TestPingServiceApplication
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@@ -10,12 +12,16 @@ import io.mockk.verify
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.context.annotation.Primary
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.MvcResult
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch
|
||||
@@ -30,12 +36,11 @@ import java.time.Instant
|
||||
*/
|
||||
@WebMvcTest(
|
||||
controllers = [PingController::class],
|
||||
excludeAutoConfiguration = [
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration::class,
|
||||
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration::class
|
||||
]
|
||||
properties = ["spring.aop.proxy-target-class=true"]
|
||||
)
|
||||
@Import(PingControllerTest.TestConfig::class)
|
||||
@ContextConfiguration(classes = [TestPingServiceApplication::class])
|
||||
@ActiveProfiles("test")
|
||||
@Import(PingControllerTest.PingControllerTestConfig::class)
|
||||
@AutoConfigureMockMvc
|
||||
class PingControllerTest {
|
||||
|
||||
@@ -43,15 +48,21 @@ class PingControllerTest {
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Autowired
|
||||
@Qualifier("pingUseCaseMock")
|
||||
private lateinit var pingUseCase: PingUseCase
|
||||
|
||||
@Autowired
|
||||
private lateinit var objectMapper: ObjectMapper
|
||||
|
||||
@TestConfiguration
|
||||
class TestConfig {
|
||||
@Bean
|
||||
@Configuration
|
||||
class PingControllerTestConfig {
|
||||
@Bean("pingUseCaseMock")
|
||||
@Primary
|
||||
fun pingUseCase(): PingUseCase = mockk(relaxed = true)
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
fun pingRepositoryAdapter(): PingRepositoryAdapter = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -77,10 +88,7 @@ class PingControllerTest {
|
||||
.andExpect(status().isOk)
|
||||
.andReturn()
|
||||
|
||||
// In some environments the JSONPath matcher fails to parse the response body.
|
||||
// We still validate the serialized output contains the expected fields.
|
||||
val body = result.response.contentAsString
|
||||
System.out.println("[DEBUG_LOG] /ping/simple response status=${result.response.status} contentType=${result.response.contentType} body=$body")
|
||||
val json = objectMapper.readTree(body)
|
||||
assertThat(json.has("status")).isTrue
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
@@ -107,7 +115,6 @@ class PingControllerTest {
|
||||
.andReturn()
|
||||
|
||||
val body = result.response.contentAsString
|
||||
System.out.println("[DEBUG_LOG] /ping/enhanced response status=${result.response.status} contentType=${result.response.contentType} body=$body")
|
||||
val json = objectMapper.readTree(body)
|
||||
assertThat(json.has("status")).isTrue
|
||||
assertThat(json["status"].asText()).isEqualTo("pong")
|
||||
@@ -128,7 +135,6 @@ class PingControllerTest {
|
||||
.andReturn()
|
||||
|
||||
val body = result.response.contentAsString
|
||||
System.out.println("[DEBUG_LOG] /ping/health response status=${result.response.status} contentType=${result.response.contentType} body=$body")
|
||||
val json = objectMapper.readTree(body)
|
||||
assertThat(json.has("status")).isTrue
|
||||
assertThat(json["status"].asText()).isEqualTo("up")
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package at.mocode.ping.test
|
||||
|
||||
import at.mocode.ping.infrastructure.web.PingController
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy
|
||||
import org.springframework.context.annotation.Import
|
||||
|
||||
/**
|
||||
* Eine spezielle Application-Klasse für Tests.
|
||||
* Sie liegt in einem separaten Package, damit sie nicht automatisch von @WebMvcTest gefunden wird,
|
||||
* sondern explizit importiert werden muss.
|
||||
*
|
||||
* WICHTIG: Wir scannen HIER NICHT das 'at.mocode.ping' Package!
|
||||
* Das verhindert, dass echte Services und Repositories geladen werden.
|
||||
* Wir scannen nur die Security-Infrastruktur.
|
||||
*
|
||||
* Den Controller importieren wir explizit, damit er verfügbar ist.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = ["at.mocode.infrastructure.security"]
|
||||
)
|
||||
@Import(PingController::class)
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true) // Erzwingt CGLIB Proxies für Controller
|
||||
class TestPingServiceApplication
|
||||
@@ -1,6 +1,8 @@
|
||||
spring:
|
||||
application:
|
||||
name: ping-service-test
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
cloud:
|
||||
consul:
|
||||
enabled: false
|
||||
|
||||
Reference in New Issue
Block a user