refactor: 빌드 로직 개선, gradle 파일 경량화
This commit is contained in:
@@ -51,11 +51,6 @@ jobs:
|
|||||||
- name: Run the pull-request gate
|
- name: Run the pull-request gate
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: ./gradlew :ci :verifyPublicPathSnapshot :verifyDependencyLocks --warning-mode=fail --stacktrace
|
run: ./gradlew :ci :verifyPublicPathSnapshot :verifyDependencyLocks --warning-mode=fail --stacktrace
|
||||||
# build-logic is an included build: its own suite is not reachable from the root project's
|
|
||||||
# `check`, so the convention plugins every leaf applies would otherwise ship untested.
|
|
||||||
- name: Test the build-logic convention plugins
|
|
||||||
working-directory: src
|
|
||||||
run: ./gradlew -p build-logic test --stacktrace
|
|
||||||
# Named as its own step because nothing else runs it: `check` does not depend on
|
# Named as its own step because nothing else runs it: `check` does not depend on
|
||||||
# graphqlStableTest, so the lane's required-class guard — the check that its module-boundary
|
# graphqlStableTest, so the lane's required-class guard — the check that its module-boundary
|
||||||
# suite has not silently stopped being discovered — would protect nothing in CI.
|
# suite has not silently stopped being discovered — would protect nothing in CI.
|
||||||
@@ -66,6 +61,17 @@ jobs:
|
|||||||
working-directory: src
|
working-directory: src
|
||||||
run: ./gradlew :conditionalTransportQualification --stacktrace
|
run: ./gradlew :conditionalTransportQualification --stacktrace
|
||||||
|
|
||||||
|
build-logic:
|
||||||
|
# Included-build tests are independent of the main project task graph. Running them as a
|
||||||
|
# separate blocking job keeps plugin TestKit work off the quality-gates critical path.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
|
||||||
|
- uses: ./.github/actions/setup-gradle-java
|
||||||
|
- name: Test the build-logic convention plugins
|
||||||
|
working-directory: src
|
||||||
|
run: ./gradlew -p build-logic test --stacktrace
|
||||||
|
|
||||||
redis-sdk:
|
redis-sdk:
|
||||||
# Milestone A of the Redis wrapper/typed API plan: policy catalog, typed API parity, permit
|
# Milestone A of the Redis wrapper/typed API plan: policy catalog, typed API parity, permit
|
||||||
# provenance, connection isolation, and the executor guard. There is no real-server lane yet.
|
# provenance, connection isolation, and the executor guard. There is no real-server lane yet.
|
||||||
@@ -149,6 +155,7 @@ jobs:
|
|||||||
release-gate:
|
release-gate:
|
||||||
needs:
|
needs:
|
||||||
- quality-gates
|
- quality-gates
|
||||||
|
- build-logic
|
||||||
- redis-sdk
|
- redis-sdk
|
||||||
- jpa-candidate-evidence
|
- jpa-candidate-evidence
|
||||||
- optional-platforms
|
- optional-platforms
|
||||||
@@ -161,6 +168,7 @@ jobs:
|
|||||||
- name: Require every current blocking job to succeed
|
- name: Require every current blocking job to succeed
|
||||||
env:
|
env:
|
||||||
QUALITY_RESULT: ${{ needs.quality-gates.result }}
|
QUALITY_RESULT: ${{ needs.quality-gates.result }}
|
||||||
|
BUILD_LOGIC_RESULT: ${{ needs.build-logic.result }}
|
||||||
REDIS_RESULT: ${{ needs.redis-sdk.result }}
|
REDIS_RESULT: ${{ needs.redis-sdk.result }}
|
||||||
JPA_CANDIDATE_RESULT: ${{ needs.jpa-candidate-evidence.result }}
|
JPA_CANDIDATE_RESULT: ${{ needs.jpa-candidate-evidence.result }}
|
||||||
OPTIONAL_PLATFORMS_RESULT: ${{ needs.optional-platforms.result }}
|
OPTIONAL_PLATFORMS_RESULT: ${{ needs.optional-platforms.result }}
|
||||||
@@ -169,6 +177,7 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
for result in \
|
for result in \
|
||||||
"${QUALITY_RESULT}" \
|
"${QUALITY_RESULT}" \
|
||||||
|
"${BUILD_LOGIC_RESULT}" \
|
||||||
"${REDIS_RESULT}" \
|
"${REDIS_RESULT}" \
|
||||||
"${JPA_CANDIDATE_RESULT}" \
|
"${JPA_CANDIDATE_RESULT}" \
|
||||||
"${OPTIONAL_PLATFORMS_RESULT}" \
|
"${OPTIONAL_PLATFORMS_RESULT}" \
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
name: jpa-release
|
name: jpa-release
|
||||||
|
|
||||||
# The release gate. src/config/jpa/release-registry.json is the source: every gate it declares has a
|
# The release registry is the gate-task source: jpaReleaseQualification reads its blocking gates,
|
||||||
# job or an assertion here, JpaReleaseRenderingTest holds this file's matrix and promotion lists to
|
# while JpaReleaseRenderingTest holds this file's matrix and promotion lists to the registry's Stable
|
||||||
# the registry's Stable majors, and verifyJpaReleaseGateTasks resolves each gate's task against the
|
# majors and verifyJpaReleaseGateTasks resolves each declared task against the real Gradle graph.
|
||||||
# real Gradle graph. So a gate removed from the registry, or a major demoted in it, fails the build
|
# CI therefore owns release scheduling, not a second JPA gate-task inventory.
|
||||||
# rather than quietly ceasing to be checked.
|
|
||||||
#
|
#
|
||||||
# The matrix below is therefore not free to drift: editing it without editing the registry fails the
|
# The matrix below is therefore not free to drift: editing it without editing the registry fails the
|
||||||
# unit lane.
|
# unit lane.
|
||||||
@@ -44,13 +43,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
|
||||||
- uses: ./.github/actions/setup-gradle-java
|
- uses: ./.github/actions/setup-gradle-java
|
||||||
- name: Run the full JPA release gate on PostgreSQL ${{ matrix.postgresql }}
|
- name: Run the JPA database qualification set on PostgreSQL ${{ matrix.postgresql }}
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: >-
|
run: >-
|
||||||
./gradlew
|
./gradlew
|
||||||
jpaReleaseGate
|
jpaReleaseQualification
|
||||||
-Pjpa.matrix.versions=${{ matrix.postgresql }}
|
-Pjpa.matrix.versions=${{ matrix.postgresql }}
|
||||||
|
|
||||||
--stacktrace
|
--stacktrace
|
||||||
- name: Record which major this evidence covers
|
- name: Record which major this evidence covers
|
||||||
if: always()
|
if: always()
|
||||||
@@ -61,7 +59,7 @@ jobs:
|
|||||||
echo "sha=${{ github.sha }}"
|
echo "sha=${{ github.sha }}"
|
||||||
echo "ref=${{ github.ref }}"
|
echo "ref=${{ github.ref }}"
|
||||||
echo "postgresql-major=${{ matrix.postgresql }}"
|
echo "postgresql-major=${{ matrix.postgresql }}"
|
||||||
echo "task=jpaReleaseGate"
|
echo "task-set=jpa-database-qualification"
|
||||||
} > "build/jpa-release-evidence/manifest-${{ matrix.postgresql }}.properties"
|
} > "build/jpa-release-evidence/manifest-${{ matrix.postgresql }}.properties"
|
||||||
- name: Upload the release evidence
|
- name: Upload the release evidence
|
||||||
if: always()
|
if: always()
|
||||||
@@ -120,9 +118,11 @@ jobs:
|
|||||||
working-directory: src
|
working-directory: src
|
||||||
run: >-
|
run: >-
|
||||||
./gradlew
|
./gradlew
|
||||||
|
:verifyJpaReleaseGateTasks
|
||||||
|
:verifyJpaReadinessRegistry
|
||||||
:verifyCleanArchitectureDependencies
|
:verifyCleanArchitectureDependencies
|
||||||
checkstyleMain
|
checkstyleMain
|
||||||
:app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*'
|
:app-bootstrap:architectureTest
|
||||||
:adapter:outbound:persistence-jpa:test --tests '*JpaReleaseManifestTest'
|
:adapter:outbound:persistence-jpa:test --tests '*JpaReleaseManifestTest'
|
||||||
|
|
||||||
--stacktrace
|
--stacktrace
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ Docker-dependent lanes fail closed rather than skipping, matching the existing
|
|||||||
| `settings.gradle.kts` module registration | Fail-closed 19-leaf registry | No registry change: leaf identity, Gradle path, allowed dependencies, and runtime memberships are unchanged. |
|
| `settings.gradle.kts` module registration | Fail-closed 19-leaf registry | No registry change: leaf identity, Gradle path, allowed dependencies, and runtime memberships are unchanged. |
|
||||||
| `infra/jpa/{postgres,roles,toxiproxy}` | Repository already owns `infra/` | Created at the same repository-relative paths. |
|
| `infra/jpa/{postgres,roles,toxiproxy}` | Repository already owns `infra/` | Created at the same repository-relative paths. |
|
||||||
| `docs/jpa/**`, `docs/adr/ADR-JPA-*`, `.github/workflows/jpa-*.yml` | Repository already owns `docs/` and `.github/workflows/` | Created at the same repository-relative paths. |
|
| `docs/jpa/**`, `docs/adr/ADR-JPA-*`, `.github/workflows/jpa-*.yml` | Repository already owns `docs/` and `.github/workflows/` | Created at the same repository-relative paths. |
|
||||||
| `build.gradle.kts` release aggregate `jpaReleaseGate` | Root is `src/build.gradle` | Registered there against the repository lane names in §3. |
|
| release blocking aggregate | `.github/workflows/jpa-release.yml` | CI names the blocking JPA lanes directly; Gradle only defines how each lane runs. |
|
||||||
| Per-task `git add` + `git commit` | `AGENTS.md`: commit policy is `human-only`; agents do not stage, commit, amend, or push | Implementation is delivered unstaged. This is the only plan step intentionally not executed, and it is recorded here. |
|
| Per-task `git add` + `git commit` | `AGENTS.md`: commit policy is `human-only`; agents do not stage, commit, amend, or push | Implementation is delivered unstaged. This is the only plan step intentionally not executed, and it is recorded here. |
|
||||||
| Querydsl as an optional module dependency | Querydsl is not part of this repository's dependency set | `querydsl` is implemented against the plan's contracts with the Querydsl types kept behind `compileOnly`, so the Stable runtime classpath never carries Querydsl and a deployment opting in adds the artifact itself. |
|
| Querydsl as an optional module dependency | Querydsl is not part of this repository's dependency set | `querydsl` is implemented against the plan's contracts with the Querydsl types kept behind `compileOnly`, so the Stable runtime classpath never carries Querydsl and a deployment opting in adds the artifact itself. |
|
||||||
| Hibernate Envers as a module dependency | Envers is not part of this repository's dependency set | Same treatment as Querydsl: `compileOnly` + explicit opt-in, matching the plan's "Envers is opt-in and never enabled by a global base class". |
|
| Hibernate Envers as a module dependency | Envers is not part of this repository's dependency set | Same treatment as Querydsl: `compileOnly` + explicit opt-in, matching the plan's "Envers is opt-in and never enabled by a global base class". |
|
||||||
|
|||||||
@@ -13,6 +13,29 @@
|
|||||||
> 어떤 binder도 그것을 읽지 않았다 — 문서대로 설정한 배포는 아무것도 바뀌지 않았고 아무 말도 듣지
|
> 어떤 binder도 그것을 읽지 않았다 — 문서대로 설정한 배포는 아무것도 바뀌지 않았고 아무 말도 듣지
|
||||||
> 못했다 (MSG-008).
|
> 못했다 (MSG-008).
|
||||||
|
|
||||||
|
## Application publish bridge identity
|
||||||
|
|
||||||
|
Application의 canonical integration event를 platform publish pipeline으로 보낼 때는
|
||||||
|
`app.messaging.producer-id`를 명시한다. 같은 값의 환경변수 이름은
|
||||||
|
`APP_MESSAGING_PRODUCER_ID`다. 이 값은 host/pod 이름이 아니라 배포와 무관하게 유지되는 논리적
|
||||||
|
producing-service identity다.
|
||||||
|
|
||||||
|
값이 없으면 `IntegrationEventPublishPort` bridge 자체를 만들지 않는다. `spring.application.name`이나
|
||||||
|
현재 process 이름으로 추론하지 않는다. 기존 legacy `OutboxEvent`/realtime 경로는 별도 cutover가
|
||||||
|
끝날 때까지 `app.messaging.broker` 경로를 유지한다.
|
||||||
|
|
||||||
|
## Outbox canonical transport-only cutover
|
||||||
|
|
||||||
|
`APP_OUTBOX_CANONICAL_TRANSPORT_ENABLED` / `ca-skeleton.outbox.canonical-transport-enabled`은
|
||||||
|
기존 `outbox_event` writer/claim/status authority를 유지한 채 canonical row의 **transport만** platform
|
||||||
|
publish path로 보내는 compatibility gate다. 기본값은 `false`이며 `POLLING_V2`를 활성화하지 않는다.
|
||||||
|
|
||||||
|
`true`일 때는 `OutboxAppendPort`가 `ValidatedIntegrationEvent`의 exact envelope bytes와 canonical
|
||||||
|
metadata를 기존 outbox row에 저장하고, claim된 canonical row는 `IntegrationEventPublishPort`로 간다.
|
||||||
|
legacy row는 계속 `MessageBroker`를 사용한다. 따라서 mixed-row compatibility 기간에는 relay가 켜져
|
||||||
|
있다면 `app.messaging.broker`도 계속 필요하며, canonical path를 위해 `IntegrationEventPublishPort`도
|
||||||
|
추가로 필요하다. legacy backlog가 0이라는 별도 증거 없이 broker 요구를 제거하지 않는다.
|
||||||
|
|
||||||
## Destination profile
|
## Destination profile
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -3325,6 +3325,21 @@ env_keys:
|
|||||||
compatibility_impact: behavior-change
|
compatibility_impact: behavior-change
|
||||||
required_test: adapter-contract:messaging-broker-selection
|
required_test: adapter-contract:messaging-broker-selection
|
||||||
|
|
||||||
|
- name: APP_MESSAGING_PRODUCER_ID
|
||||||
|
# source: canonical messaging platform bridge 2026-09-18
|
||||||
|
# Explicit logical producing-service identity for IntegrationEventPublishPort.
|
||||||
|
# Blank/absent = canonical platform bridge is not exposed; identity is never inferred.
|
||||||
|
type: string
|
||||||
|
default: null
|
||||||
|
allowed_values: null
|
||||||
|
classification: public-config
|
||||||
|
required: false
|
||||||
|
reload_policy: restart-only
|
||||||
|
owner_branch: feature-integration-adapter-templates
|
||||||
|
validation: none
|
||||||
|
compatibility_impact: additive
|
||||||
|
required_test: adapter-contract:messaging-platform-producer-id
|
||||||
|
|
||||||
- name: APP_MESSAGING_KAFKA_BROKERS
|
- name: APP_MESSAGING_KAFKA_BROKERS
|
||||||
# source: feature-domain-event-outbox-contract — "Kafka는 optional integration adapter"
|
# source: feature-domain-event-outbox-contract — "Kafka는 optional integration adapter"
|
||||||
# (broker 활성화 시 endpoint 필요)
|
# (broker 활성화 시 endpoint 필요)
|
||||||
@@ -4358,6 +4373,22 @@ env_keys:
|
|||||||
validation: boolean
|
validation: boolean
|
||||||
compatibility_impact: behavior-change
|
compatibility_impact: behavior-change
|
||||||
required_test: adapter-contract:outbox-capability-disabled-safe
|
required_test: adapter-contract:outbox-capability-disabled-safe
|
||||||
|
- name: APP_OUTBOX_CANONICAL_TRANSPORT_ENABLED
|
||||||
|
# source: MSG-015 transport-only cutover 2026-09-18
|
||||||
|
# Enables canonical outbox rows/platform transport without switching publication authority.
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
allowed_values:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
|
classification: public-config
|
||||||
|
required: false
|
||||||
|
reload_policy: restart-only
|
||||||
|
owner_branch: feature-integration-adapter-templates
|
||||||
|
validation: boolean
|
||||||
|
compatibility_impact: additive
|
||||||
|
required_test: app-bootstrap:outbox-canonical-transport-gate
|
||||||
|
|
||||||
- name: APP_OUTBOX_RELAY_ENABLED
|
- name: APP_OUTBOX_RELAY_ENABLED
|
||||||
# source: five-adapter-runtime-remediation §6.3 MSG-INT-001 — starts the relay scheduler.
|
# source: five-adapter-runtime-remediation §6.3 MSG-INT-001 — starts the relay scheduler.
|
||||||
# Requires APP_OUTBOX_ENABLED, APP_PERSISTENCE_JPA_ENABLED and APP_MESSAGING_ENABLED with a
|
# Requires APP_OUTBOX_ENABLED, APP_PERSISTENCE_JPA_ENABLED and APP_MESSAGING_ENABLED with a
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# JPA Evidence Gradle Model Decoupling Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Remove execution-time Gradle `Project`/`Task`/`TaskState` access from JPA evidence generation without changing evidence semantics.
|
||||||
|
|
||||||
|
**Architecture:** A shared `JpaEvidenceExecutionService` consumes Gradle task-completion events for non-Test task claims. The JPA evidence plugin snapshots/configures JUnit result directories, provenance, environment/profile values, and dependency versions as typed task inputs. `GenerateJpaEvidenceManifestsTask` becomes a pure evidence assembler over those inputs plus filesystem/exec services.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Gradle 9 BuildService + Tooling Events, JUnit 6/TestKit, Jackson 3.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-17-jpa-evidence-gradle-model-decoupling-design.md`
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Preserve readiness-card schema and task names.
|
||||||
|
- Preserve JUnit XML as test evidence.
|
||||||
|
- Preserve task-claim meaning: only a successful producer task covers a task claim.
|
||||||
|
- Preserve evidence grade, blocker, hashing, prerequisite, output, candidate/R2 semantics.
|
||||||
|
- No execution-time `Project`, `Task`, or `TaskState` access in `GenerateJpaEvidenceManifestsTask`.
|
||||||
|
- Do not stage, commit, amend, reset, or push existing worktree changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Task completion evidence service
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidenceExecutionService.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidenceTaskOutcome.java`
|
||||||
|
- Test: `src/build-tools/src/test/java/dev/caskeleton/buildtools/jpa/JpaEvidenceExecutionServiceTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `JpaEvidenceExecutionService.outcome(String taskPath)` and `completedSuccessfully(String taskPath)`.
|
||||||
|
- Consumes: Gradle `TaskFinishEvent` via `OperationCompletionListener`.
|
||||||
|
|
||||||
|
- [ ] Write tests for success, failure, skipped, and unknown task paths.
|
||||||
|
- [ ] Verify tests fail because the service/model do not exist.
|
||||||
|
- [ ] Implement the typed outcome model and thread-safe service.
|
||||||
|
- [ ] Verify focused tests pass.
|
||||||
|
|
||||||
|
### Task 2: Typed JUnit evidence inputs
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidenceTestResultLocator.java`
|
||||||
|
- Modify: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidenceTaskSupport.java`
|
||||||
|
- Test: `src/build-tools/src/test/java/dev/caskeleton/buildtools/jpa/JpaEvidenceTestResultLocatorTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `Map<String, String>` task-path to repository-relative/absolute JUnit XML directory.
|
||||||
|
- Produces: `JpaGeneratedTestResult read(String taskPath)` without `Project` or `Test`.
|
||||||
|
|
||||||
|
- [ ] Write a failing test that creates JUnit XML under a temporary directory and resolves it by task path.
|
||||||
|
- [ ] Implement file-based result lookup using `JUnitEvidenceReader`.
|
||||||
|
- [ ] Remove the `readJUnitResult(Project, Test)` helper once no caller remains.
|
||||||
|
- [ ] Verify focused tests pass.
|
||||||
|
|
||||||
|
### Task 3: Generator typed input surface
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/GenerateJpaEvidenceManifestsTask.java`
|
||||||
|
- Test: extend `src/build-tools/src/test/java/dev/caskeleton/buildtools/jpa/JpaEvidencePluginTypeTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Add typed properties for profile/CI/artifact/topology/provenance/dependency versions/JUnit result directories.
|
||||||
|
- Add an internal/service reference to `JpaEvidenceExecutionService`.
|
||||||
|
|
||||||
|
- [ ] Add reflection/type tests asserting the new task properties exist and no generator source contains `getProject()` or `Task.getState()` usage.
|
||||||
|
- [ ] Verify the test fails against the current generator.
|
||||||
|
- [ ] Add the typed properties and service reference.
|
||||||
|
- [ ] Replace Project/Task/TaskState/configuration/extra-property reads with typed inputs/service lookups.
|
||||||
|
- [ ] Verify focused tests pass.
|
||||||
|
|
||||||
|
### Task 4: Plugin wiring
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidencePlugin.java`
|
||||||
|
- Modify: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidenceTaskSupport.java`
|
||||||
|
- Test: add `src/build-tools/src/test/java/dev/caskeleton/buildtools/jpa/JpaEvidencePluginFunctionalTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Register shared execution service and task-completion listener.
|
||||||
|
- Configure generator typed inputs.
|
||||||
|
- Configure JUnit result-directory mapping for active readiness/support Test tasks.
|
||||||
|
- Preserve existing `dependsOn` producer graph.
|
||||||
|
|
||||||
|
- [ ] Write TestKit fixture asserting typed generator inputs and task wiring.
|
||||||
|
- [ ] Verify RED.
|
||||||
|
- [ ] Register/wire the service and all generator properties.
|
||||||
|
- [ ] Resolve dependency versions and release provenance during configuration/plugin wiring rather than task action.
|
||||||
|
- [ ] Verify TestKit GREEN.
|
||||||
|
|
||||||
|
### Task 5: Regression and Gradle 10-preparation verification
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify only if verification exposes a regression.
|
||||||
|
|
||||||
|
- [ ] Run `src/build-tools` full `check --warning-mode=fail`.
|
||||||
|
- [ ] Run root `verifyJpaReadinessRegistry verifyJpaReleaseGateTasks --warning-mode=fail`.
|
||||||
|
- [ ] Run `:adapter:outbound:persistence-jpa:check --warning-mode=fail`.
|
||||||
|
- [ ] Run candidate evidence generation with `--warning-mode=all`; verify there is no `Task.project`/execution-time project deprecation from JPA evidence tooling.
|
||||||
|
- [ ] Run adapter procedural-Groovy scan and confirm no regression.
|
||||||
|
- [ ] Run `git diff --check`.
|
||||||
|
- [ ] Record any environment-only Docker/Testcontainers limitation separately from code correctness.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# JPA Leaf Verification Java Migration Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Remove the remaining procedural JPA leaf verification logic from Groovy while deleting a redundant verifier-of-verifier task.
|
||||||
|
|
||||||
|
**Architecture:** Keep the security scenario as a declarative `strictTestLanes.requires(...)` contract and remove `verifyJpaSecurityFixtures` from both the leaf and readiness registry. Move the real PostgreSQL `set_config` source-safety rule into a typed task/verifier owned by the already-applied `ca.jpa-evidence` Java plugin.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Gradle 9 binary plugins/tasks, JUnit 6.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md`
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Preserve the `verifyJpaSqlConstructionSafety` task name because readiness registry/evidence tooling references it.
|
||||||
|
- Preserve the PostgreSQL security method selector on `postgresqlSecurityBaselineIntegrationTest`.
|
||||||
|
- Remove `verifyJpaSecurityFixtures` only together with its readiness-card support-task reference.
|
||||||
|
- Do not add production dependencies or change JPA runtime behavior.
|
||||||
|
- Do not stage, commit, amend, or push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Typed SQL construction safety verifier
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaSqlConstructionSafetyVerifier.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaSqlConstructionSafetyResult.java`
|
||||||
|
- Test: `src/build-tools/src/test/java/dev/caskeleton/buildtools/jpa/JpaSqlConstructionSafetyVerifierTest.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1:** Write RED tests for parameterized `set_config`, non-parameterized `set_config`, comments, and nested source paths.
|
||||||
|
- [ ] **Step 2:** Implement the minimal typed verifier preserving the current line-based rule.
|
||||||
|
- [ ] **Step 3:** Run the focused verifier tests to GREEN.
|
||||||
|
|
||||||
|
### Task 2: Java task ownership and redundant task removal
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/VerifyJpaSqlConstructionSafetyTask.java`
|
||||||
|
- Modify: `src/build-tools/src/main/java/dev/caskeleton/buildtools/jpa/JpaEvidencePlugin.java`
|
||||||
|
- Modify: `src/adapter/outbound/persistence-jpa/build.gradle`
|
||||||
|
- Modify: `src/config/jpa/readiness-cards.yaml`
|
||||||
|
|
||||||
|
- [ ] **Step 1:** Register `verifyJpaSqlConstructionSafety` as a typed task from `JpaEvidencePlugin`.
|
||||||
|
- [ ] **Step 2:** Delete the Groovy implementation of `verifyJpaSqlConstructionSafety`.
|
||||||
|
- [ ] **Step 3:** Delete `verifyJpaSecurityFixtures` and remove it from the security card support tasks while keeping the `requires(...)` selector.
|
||||||
|
- [ ] **Step 4:** Verify readiness registry and SQL-safety tasks.
|
||||||
|
- [ ] **Step 5:** Run `:adapter:outbound:persistence-jpa:check`, build-tools tests, and `git diff --check`.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# JPA Test Lanes Java Convention Plan
|
||||||
|
|
||||||
|
**Goal:** Remove the remaining Map-driven JPA lane factories from Groovy and make lane metadata compile-time checked Java records.
|
||||||
|
|
||||||
|
**Architecture:** `ca.jpa-test-lanes` lives in build-logic and configures `ca.strict-test-lane`. Typed record lists own the PostgreSQL readiness and tagged platform lane metadata. The JPA leaf keeps source-set/dependency declarations plus the explicit cross-project task edge.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- Preserve all 14 PostgreSQL readiness task names and selectors.
|
||||||
|
- Preserve the security method selector in addition to its class selector.
|
||||||
|
- Preserve five tagged platform lanes and the pool contract lane.
|
||||||
|
- Preserve UTC JVM args, PostgreSQL evidence image property, and `jpa.matrix.versions` default `16`.
|
||||||
|
- Keep release orchestration outside the leaf.
|
||||||
|
- Do not stage, commit, amend, or push.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
- [ ] RED TestKit contract for registered lanes, typed selector metadata, and property defaults.
|
||||||
|
- [ ] Implement Java record-backed `JpaTestLanesPlugin` and register `ca.jpa-test-lanes`.
|
||||||
|
- [ ] Apply plugin in persistence-jpa and remove both Groovy `Map` factories plus pool-lane Groovy configuration.
|
||||||
|
- [ ] Run focused build-logic tests, JPA check, adapter dynamic-model scan, and broad verification.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Mongo Gradle Verification Java Migration Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Remove Mongo JUnit/XML and release-manifest verification algorithms from the Groovy leaf build script and move them into typed Java build tooling without changing task names or verification semantics.
|
||||||
|
|
||||||
|
**Architecture:** `ca.mongo-verification` lives in `src/build-tools` because the checks are repository certification tooling, not reusable compilation conventions. The plugin registers the two existing verification task names; normal Java verifier/parser classes own XML/JSON parsing and return typed records, while `persistence-mongo/build.gradle` keeps only plugin/lane/dependency declarations and `check` wiring.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Gradle 9 binary plugins/tasks, Jackson 3, JUnit 6, JUnit XML.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md`
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Preserve `verifyMongoTestLaneDisjointness` and `verifyMongoReleaseContractLanes` task names and report paths.
|
||||||
|
- Preserve the existing `test` + `mongoStableContractTest` dependency graph.
|
||||||
|
- Do not add or resolve new production dependencies in the Mongo leaf.
|
||||||
|
- Keep Groovy only as declarative build DSL; no JSON/XML parsing or `doLast` verification algorithm remains in the leaf.
|
||||||
|
- Do not stage, commit, amend, or push; repository policy is human-only commits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Typed Mongo verification core
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoLaneDisjointnessVerifier.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoLaneDisjointnessResult.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoReleaseContract.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoReleaseContractManifestParser.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoReleaseContractLaneVerifier.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoReleaseContractLaneResult.java`
|
||||||
|
- Test: `src/build-tools/src/test/java/dev/caskeleton/buildtools/mongo/MongoLaneDisjointnessVerifierTest.java`
|
||||||
|
- Test: `src/build-tools/src/test/java/dev/caskeleton/buildtools/mongo/MongoReleaseContractLaneVerifierTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Gradle JUnit XML result directories and `config/mongodb/release-contracts.json`.
|
||||||
|
- Produces: typed result records used by Gradle task classes.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write failing verifier tests** covering disjoint lanes, overlap failure data, manifest filtering to hermetic lanes, missing result XML, and minimum-executed checks.
|
||||||
|
- [ ] **Step 2: Run** `cd src/build-tools && ../gradlew test --tests 'dev.caskeleton.buildtools.mongo.*' --console=plain` and confirm RED from missing production types.
|
||||||
|
- [ ] **Step 3: Implement minimal typed records/parsers/verifiers** using fail-closed XML parsing and Jackson 3 JSON tree parsing.
|
||||||
|
- [ ] **Step 4: Run the focused tests again** and confirm PASS.
|
||||||
|
|
||||||
|
### Task 2: Binary plugin/task ownership and leaf cleanup
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/MongoVerificationPlugin.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/VerifyMongoTestLaneDisjointnessTask.java`
|
||||||
|
- Create: `src/build-tools/src/main/java/dev/caskeleton/buildtools/mongo/VerifyMongoReleaseContractLanesTask.java`
|
||||||
|
- Modify: `src/build-tools/build.gradle`
|
||||||
|
- Modify: `src/adapter/outbound/persistence-mongo/build.gradle`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: typed verifiers from Task 1.
|
||||||
|
- Produces: existing task names `verifyMongoTestLaneDisjointness`, `verifyMongoReleaseContractLanes` with unchanged report paths.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Register `ca.mongo-verification`** and the two typed tasks in Java.
|
||||||
|
- [ ] **Step 2: Apply the plugin in the Mongo leaf and delete both Groovy `tasks.register { doLast { ... } }` implementations.**
|
||||||
|
- [ ] **Step 3: Run** `cd src && ./gradlew :adapter:outbound:persistence-mongo:verifyMongoTestLaneDisjointness :adapter:outbound:persistence-mongo:verifyMongoReleaseContractLanes --console=plain`.
|
||||||
|
- [ ] **Step 4: Run** `cd src && ./gradlew :adapter:outbound:persistence-mongo:check --console=plain`.
|
||||||
|
- [ ] **Step 5: Run** `cd src/build-tools && ../gradlew test --console=plain` and `git diff --check`.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Redis Topology Lane Java Migration Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** Remove the last adapter-level procedural Groovy test lifecycle logic by moving Redis topology qualification into a typed Java convention plugin.
|
||||||
|
|
||||||
|
**Architecture:** `ca.redis-topology-lane` belongs in `build-logic`: it configures the default test exclusion, declares the strict `redisTopologyTest` lane, validates mode/properties, forwards topology system properties, and verifies executed required classes/no skips. The Redis leaf keeps only plugin and dependency declarations.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Gradle 9, JUnit Platform test tasks.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md`
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- Preserve supported modes: standalone, sentinel, cluster, tls.
|
||||||
|
- Preserve TLS deployment-mode mapping to standalone.
|
||||||
|
- Preserve required property and required executed-class semantics.
|
||||||
|
- Preserve fail-closed behavior for unknown mode and skipped topology tests.
|
||||||
|
- Keep `redisTopologyTest` opt-in; do not add it to normal `check`.
|
||||||
|
- Do not stage, commit, amend, or push.
|
||||||
|
|
||||||
|
### Task 1: Typed topology contract
|
||||||
|
- [ ] Write failing Java tests for mode validation, required properties, class coverage, and skipped-test rejection.
|
||||||
|
- [ ] Implement typed contract/result records and get focused tests GREEN.
|
||||||
|
|
||||||
|
### Task 2: Java convention plugin
|
||||||
|
- [ ] Implement `RedisTopologyLanePlugin` using `StrictTestLaneExtension` and a Java `TestListener` tracker.
|
||||||
|
- [ ] Register `ca.redis-topology-lane` in build-logic.
|
||||||
|
- [ ] Apply it in cache-redis and remove the Groovy topology lifecycle/configuration block.
|
||||||
|
- [ ] Verify build-logic tests, Redis unit `check`, task configuration, invalid-mode fail-closed behavior, and `git diff --check`.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Messaging Platform Bridge Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Connect the canonical application integration-event publish boundary to the messaging platform without re-encoding bytes or introducing broker-specific ownership in app-bootstrap.
|
||||||
|
|
||||||
|
**Architecture:** Add an application-owned publish port, implement it in `adapter/outbound/messaging/platformbridge`, and publish canonical pre-encoded envelopes through `EncodedMessagePublisher`. The bridge preserves identity/routing/evidence and maps platform publish evidence explicitly into application outcomes.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Gradle 9, Spring Boot auto-configuration, JUnit 5, AssertJ.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-18-messaging-platform-bridge-design.md`
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Preserve existing uncommitted changes; do not reset, stage, commit, amend, or push.
|
||||||
|
- Never invent missing canonical identity, timestamp, trace, tenant, schema, or routing values.
|
||||||
|
- Never bypass `DefaultMessagePublisher` through transport SPI or native Kafka clients.
|
||||||
|
- Preserve exact `ValidatedIntegrationEvent.envelopeBytes()`.
|
||||||
|
- Fail closed before send when event or causation identity is not UUIDv7.
|
||||||
|
- Legacy outbox storage/relay migration is outside this plan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Application-owned canonical publish port
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `application-core/src/main/java/dev/caskeleton/application/messaging/event/IntegrationEventPublishPort.java`
|
||||||
|
- Test: existing bridge test compile contract
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `CompletionStage<OutboxPublishOutcome> publish(ValidatedIntegrationEvent event)`
|
||||||
|
|
||||||
|
- [x] Create the minimal application-owned interface.
|
||||||
|
- [x] Run the focused bridge test and verify remaining failures are platform dependencies/adapter implementation, not the port.
|
||||||
|
|
||||||
|
### Task 2: Outbound messaging platform API dependency
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `adapter/outbound/messaging/build.gradle`
|
||||||
|
- Modify: `adapter/outbound/messaging/gradle.lockfile` through Gradle lock writing
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `:messaging:messaging-core-api`, `:messaging:messaging-schema-api`.
|
||||||
|
|
||||||
|
- [x] Add only the platform API dependencies required by the bridge.
|
||||||
|
- [x] Refresh this module's locks.
|
||||||
|
- [x] Re-run focused bridge test and verify the missing type set is reduced to bridge production code.
|
||||||
|
|
||||||
|
### Task 3: Canonical platform bridge
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/platformbridge/PlatformIntegrationEventPublishAdapter.java`
|
||||||
|
- Test: `adapter/outbound/messaging/src/test/java/dev/caskeleton/adapter/outbound/messaging/platformbridge/PlatformIntegrationEventPublishAdapterTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `IntegrationEventPublishPort`, `EncodedMessagePublisher`.
|
||||||
|
- Produces: canonical application-to-platform anti-corruption bridge.
|
||||||
|
|
||||||
|
- [x] Implement UUIDv7 parsing that rejects incompatible identity before publisher invocation.
|
||||||
|
- [x] Map canonical metadata and exact bytes into `MessageEnvelope<EncodedMessage>`.
|
||||||
|
- [x] Preserve non-first-class evidence in bounded `x-ca-*` headers.
|
||||||
|
- [x] Map `PublishResult` using transmission evidence.
|
||||||
|
- [x] Run all three focused bridge tests to GREEN.
|
||||||
|
|
||||||
|
### Task 4: Spring ownership while preserving the legacy seam
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/MessagingSettings.java`
|
||||||
|
- Modify: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/autoconfigure/MessagingBridgeRootAutoConfiguration.java`
|
||||||
|
- Test: focused auto-configuration ownership test
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Spring-provided `EncodedMessagePublisher`, explicit `app.messaging.producer-id`.
|
||||||
|
- Produces: `IntegrationEventPublishPort` bean for canonical events.
|
||||||
|
|
||||||
|
- [x] Add explicit `producerId` to the existing `app.messaging` adapter settings.
|
||||||
|
- [x] Register the canonical bridge only when `app.messaging.producer-id` is explicitly present.
|
||||||
|
- [x] Keep `KafkaSender` / `KafkaMessageBroker` as a documented transitional dependency of legacy outbox/realtime only.
|
||||||
|
- [x] Add a Spring test proving producer-id present => one canonical bridge bean, absent => no canonical bridge bean.
|
||||||
|
- [x] Run outbound messaging tests.
|
||||||
|
|
||||||
|
### Task 5: Platform and composition regression verification
|
||||||
|
|
||||||
|
**Files:** no new production files unless a test exposes a real defect.
|
||||||
|
|
||||||
|
- [x] Run `:messaging:messaging-runtime-core:test`.
|
||||||
|
- [x] Run `:messaging:messaging-spring-boot-starter:test`.
|
||||||
|
- [x] Run `:adapter:outbound:messaging:check --warning-mode=fail`.
|
||||||
|
- [x] Run `:app-bootstrap:architectureTest :app-bootstrap:systemTest --warning-mode=fail`.
|
||||||
|
- [x] Search for direct app-bootstrap Kafka producer ownership.
|
||||||
|
- [x] Run `git diff --check`.
|
||||||
|
- [x] Report any remaining legacy outbox cutover blocker explicitly rather than inventing a migration.
|
||||||
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Outbox Transport-Only Cutover Implementation Plan
|
||||||
|
|
||||||
|
**Goal:** Preserve canonical integration-event bytes and metadata inside the existing legacy `outbox_event` authority, then route canonical claimed rows through the messaging platform while legacy rows keep the current broker path.
|
||||||
|
|
||||||
|
**Spec:** `docs/superpowers/specs/2026-09-18-outbox-transport-only-cutover-design.md`
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Preserve all existing uncommitted work. No reset/checkout/stage/commit/amend/push.
|
||||||
|
- Do not activate or switch to `POLLING_V2`.
|
||||||
|
- Do not synthesize missing canonical metadata for legacy rows.
|
||||||
|
- Do not re-encode a persisted canonical envelope.
|
||||||
|
- One claimed row goes through exactly one publish branch.
|
||||||
|
- Default configuration remains legacy-compatible and canonical transport is off.
|
||||||
|
|
||||||
|
### Task 1 — Split canonical and legacy append ports
|
||||||
|
|
||||||
|
- [x] Create `LegacyOutboxAppendPort` with the current `NewOutboxEvent` signature.
|
||||||
|
- [x] Change `OutboxAppendPort` to accept `ValidatedIntegrationEvent`.
|
||||||
|
- [x] Move all current raw production consumers and their tests to `LegacyOutboxAppendPort`.
|
||||||
|
- [x] Make `OutboxStoreAdapter` implement `LegacyOutboxAppendPort` only.
|
||||||
|
- [x] Run `:application-core:test` and focused sample/outbox compile tests.
|
||||||
|
|
||||||
|
### Task 2 — Add additive canonical columns to `outbox_event`
|
||||||
|
|
||||||
|
- [x] Add the next PostgreSQL migration after V12.
|
||||||
|
- [x] Widen event/correlation identifiers as required.
|
||||||
|
- [x] Add canonical metadata, exact `BYTEA`, hashes/revisions, and all-or-none check constraints.
|
||||||
|
- [x] Extend `OutboxEventEntity` mappings.
|
||||||
|
- [x] Update migration history expectations.
|
||||||
|
- [x] Add real PostgreSQL integration assertions for legacy rows and canonical shape constraints.
|
||||||
|
|
||||||
|
### Task 3 — Implement canonical append adapter
|
||||||
|
|
||||||
|
- [x] Add `CanonicalOutboxAppendAdapter`.
|
||||||
|
- [x] Strictly validate UTF-8 compatibility projection.
|
||||||
|
- [x] Persist every canonical field and exact `envelopeBytes`.
|
||||||
|
- [x] Preserve old required columns for legacy relay/storage compatibility.
|
||||||
|
- [x] Add unit tests for byte equality, field mapping and invalid UTF-8.
|
||||||
|
- [x] Gate bean exposure on `ca-skeleton.outbox.canonical-transport-enabled=true`.
|
||||||
|
|
||||||
|
### Task 4 — Split the claimed row model
|
||||||
|
|
||||||
|
- [x] Add sealed `ClaimedOutboxEvent`.
|
||||||
|
- [x] Keep `OutboxEvent` as legacy subtype.
|
||||||
|
- [x] Add `CanonicalClaimedOutboxEvent` carrying reconstructed `ValidatedIntegrationEvent`.
|
||||||
|
- [x] Change `OutboxStorePort.claimBatch` to return the sealed type.
|
||||||
|
- [x] Map all-canonical rows to canonical subtype and all-null rows to legacy subtype.
|
||||||
|
- [x] Reject partial canonical rows.
|
||||||
|
- [x] Update legacy relay tests without changing its state-machine semantics.
|
||||||
|
|
||||||
|
### Task 5 — Route canonical claims through the platform
|
||||||
|
|
||||||
|
- [x] Update `OutboxMessagePublishPort` to accept `ClaimedOutboxEvent`.
|
||||||
|
- [x] Extend `OutboxMessagePublishAdapter` with canonical `IntegrationEventPublishPort`.
|
||||||
|
- [x] Legacy subtype uses only `MessageBroker`.
|
||||||
|
- [x] Canonical subtype uses only the application canonical publish port and exact stored bytes.
|
||||||
|
- [x] Add focused branch-isolation and outcome tests.
|
||||||
|
|
||||||
|
### Task 6 — Add explicit activation and composition validation
|
||||||
|
|
||||||
|
- [x] Add `canonicalTransportEnabled` to `OutboxSettings` and `config/outbox.yml`, default false.
|
||||||
|
- [x] Startup fails when canonical transport is enabled but no `IntegrationEventPublishPort` exists.
|
||||||
|
- [x] Relay-enabled compatibility deployment still requires the legacy broker until a later zero-legacy-backlog proof.
|
||||||
|
- [x] Default-off composition keeps the existing legacy path.
|
||||||
|
- [x] Enabled composition exposes the canonical append/publish path without a second scheduler.
|
||||||
|
- [x] Update configuration docs/SSOT.
|
||||||
|
|
||||||
|
### Task 7 — Regression and architecture verification
|
||||||
|
|
||||||
|
- [x] `:application-core:check`
|
||||||
|
- [x] `:adapter:outbound:persistence-jpa:check`
|
||||||
|
- [x] focused PostgreSQL migration/outbox integration lane
|
||||||
|
- [x] `:adapter:outbound:messaging:check`
|
||||||
|
- [ ] `:sample-portfolio:check` — blocked by pre-existing `JpaLiveEventReplayAdapter` missing `Duration` wiring; the same 3 `SampleApplicationContextTest` failures reproduce on clean HEAD.
|
||||||
|
- [x] focused sample outbox regression tests (`PosterEventPublisherTest`, `CreateWorkLogOutboxTest`, `WorkLogUseCasesTest`, `WorkLogAuthorizationContractTest`)
|
||||||
|
- [x] `:app-bootstrap:architectureTest :app-bootstrap:systemTest`
|
||||||
|
- [x] `verifyCleanArchitectureDependencies`
|
||||||
|
- [x] `:app-bootstrap:verifyEnvKeys`
|
||||||
|
- [x] static scans: no platform runtime/Kafka import in canonical bridge
|
||||||
|
- [x] `git diff --check`
|
||||||
|
- [x] no separate LLM Wiki branch-note warranted; spec, plan, module README/CLAUDE and configuration reference carry the implementation decision.
|
||||||
|
|
||||||
|
## Verification result
|
||||||
|
|
||||||
|
Transport-only cutover implementation is complete for this slice.
|
||||||
|
|
||||||
|
Passed:
|
||||||
|
- `:application-core:check`
|
||||||
|
- `:adapter:outbound:persistence-jpa:check`
|
||||||
|
- `:adapter:outbound:persistence-jpa:postgresqlMigrationIntegrationTest`
|
||||||
|
- `:adapter:outbound:messaging:check`
|
||||||
|
- focused sample outbox regression tests
|
||||||
|
- `:app-bootstrap:test`
|
||||||
|
- focused `:app-bootstrap:integrationTest` outbox append + row-lifecycle contracts
|
||||||
|
- `:app-bootstrap:architectureTest`
|
||||||
|
- `:app-bootstrap:systemTest`
|
||||||
|
- `verifyCleanArchitectureDependencies`
|
||||||
|
- `:app-bootstrap:verifyEnvKeys`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
Known unrelated blocker:
|
||||||
|
- full `:sample-portfolio:check` still fails only the 3 previously documented `SampleApplicationContextTest` cases because `JpaLiveEventReplayAdapter` requires an unbound `Duration` bean. This reproduces on clean HEAD and was not introduced by this cutover.
|
||||||
|
|
||||||
|
Publication authority remains `LEGACY_POLLING`; no code path in this slice activates `POLLING_V2`.
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
# JPA Evidence Gradle Model Decoupling Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
`GenerateJpaEvidenceManifestsTask` currently performs evidence generation after its producer tasks run. Its semantic contract is useful, but the task action reaches back into the live Gradle model through `getProject()`, resolves configurations, locates `Task` instances, reads `Test` report locations, inspects `TaskState`, and reads root extra properties.
|
||||||
|
|
||||||
|
Gradle 9 deprecates `Task.project` access at execution time and Gradle 10 will reject it. More importantly, the current task mixes two responsibilities:
|
||||||
|
|
||||||
|
1. Gradle configuration/model discovery.
|
||||||
|
2. Pure evidence assembly from producer results.
|
||||||
|
|
||||||
|
The refactor must separate those concerns without weakening evidence claims.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Preserve the current readiness-card and evidence-manifest semantics.
|
||||||
|
- Remove execution-time `Project`, `Task`, and `TaskState` access from `GenerateJpaEvidenceManifestsTask`.
|
||||||
|
- Preserve JUnit XML as the source of truth for test execution evidence.
|
||||||
|
- Preserve successful non-Test task execution as the source of truth for `task-claims` such as architecture/configuration claims.
|
||||||
|
- Represent generator inputs with typed Gradle properties rather than hidden project lookups.
|
||||||
|
- Keep producer task names and readiness-card schema unchanged.
|
||||||
|
- Remain compatible with `--warning-mode=fail` on Gradle 9 and prepare the evidence lane for Gradle 10.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Do not redesign the readiness-card schema.
|
||||||
|
- Do not change evidence grades, prerequisite semantics, content hashing, R1/R2 rules, or output layout.
|
||||||
|
- Do not introduce marker files into every producer task.
|
||||||
|
- Do not move release orchestration into the persistence-JPA leaf.
|
||||||
|
- Do not add new runtime dependencies to application modules.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### 1. Build service owns task completion outcomes
|
||||||
|
|
||||||
|
Introduce `JpaEvidenceExecutionService`, a Gradle shared build service implementing `OperationCompletionListener`.
|
||||||
|
|
||||||
|
The plugin registers it through `BuildEventsListenerRegistry.onTaskCompletion(...)` so the service receives `TaskFinishEvent` events without the generator querying `TaskState`.
|
||||||
|
|
||||||
|
The service stores a thread-safe typed outcome for each task path:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Task path
|
||||||
|
-> SUCCESS
|
||||||
|
-> FAILED
|
||||||
|
-> SKIPPED
|
||||||
|
```
|
||||||
|
|
||||||
|
Only `SUCCESS` satisfies an evidence `task-claim`. Failed or skipped producers do not cover the claim.
|
||||||
|
|
||||||
|
The service is build-scoped and contains no `Project` reference.
|
||||||
|
|
||||||
|
### 2. Test evidence remains file-based
|
||||||
|
|
||||||
|
JUnit evidence already has a durable output: Gradle's JUnit XML result directory. The plugin resolves every readiness/support `Test` task during configuration and supplies a typed mapping:
|
||||||
|
|
||||||
|
```text
|
||||||
|
absolute task path -> JUnit XML result directory
|
||||||
|
```
|
||||||
|
|
||||||
|
The generator reads those directories directly with `JUnitEvidenceReader`; it never locates a `Test` object.
|
||||||
|
|
||||||
|
Non-Test support tasks continue to participate in the task graph but do not produce JUnit evidence.
|
||||||
|
|
||||||
|
### 3. Configuration-derived values become task inputs
|
||||||
|
|
||||||
|
The plugin supplies these inputs before execution:
|
||||||
|
|
||||||
|
- evidence profile
|
||||||
|
- CI job
|
||||||
|
- artifact location
|
||||||
|
- topology
|
||||||
|
- PostgreSQL image
|
||||||
|
- source revision
|
||||||
|
- traceable version
|
||||||
|
- resolved PostgreSQL JDBC version
|
||||||
|
- resolved Hibernate ORM version
|
||||||
|
- resolved Flyway version
|
||||||
|
- repository-relative evidence output location used by the candidate default
|
||||||
|
- JUnit result-directory mapping
|
||||||
|
|
||||||
|
The generator reads only its properties/files plus the execution service.
|
||||||
|
|
||||||
|
`releaseProvenance` is the preferred source for revision/version. The existing extra-property compatibility bridge is no longer read by the generator.
|
||||||
|
|
||||||
|
### 4. Dependency-version discovery stays in plugin configuration
|
||||||
|
|
||||||
|
The JPA evidence plugin owns the Gradle `Configuration` object. It derives the three relevant resolved module versions and writes them into typed task properties before the generator executes.
|
||||||
|
|
||||||
|
This keeps dependency-graph access out of the task action. The existing coordinates remain unchanged:
|
||||||
|
|
||||||
|
- `org.postgresql:postgresql`
|
||||||
|
- `org.hibernate.orm:hibernate-core`
|
||||||
|
- `org.flywaydb:flyway-core`
|
||||||
|
|
||||||
|
### 5. Generator becomes an evidence assembler
|
||||||
|
|
||||||
|
The generator task action may use:
|
||||||
|
|
||||||
|
- its declared Gradle properties/files
|
||||||
|
- `ExecOperations` for git/docker commands already owned by the task
|
||||||
|
- `FileSystemOperations`
|
||||||
|
- `JpaEvidenceExecutionService`
|
||||||
|
- pure parser/verifier/helper classes
|
||||||
|
|
||||||
|
It must not call:
|
||||||
|
|
||||||
|
```java
|
||||||
|
getProject()
|
||||||
|
Project.findProject(...)
|
||||||
|
Task.getState()
|
||||||
|
TaskContainer.findByName(...)
|
||||||
|
ConfigurationContainer.getByName(...)
|
||||||
|
ExtraPropertiesExtension.get(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Evidence semantics
|
||||||
|
|
||||||
|
For a readiness card:
|
||||||
|
|
||||||
|
- `evidence.scenarios` are covered only by selectors found in JUnit XML.
|
||||||
|
- `evidence.task-claims` are covered only when the build service reports the named task completed successfully in the current build.
|
||||||
|
- `no-skip` remains based on JUnit result counts.
|
||||||
|
- prerequisite manifest ordering and hashing remain unchanged.
|
||||||
|
- candidate/R2 blockers remain unchanged.
|
||||||
|
|
||||||
|
The primary foundation card still obtains architecture/configuration coverage from successful execution of its declared producer tasks; the mechanism changes from `TaskState` lookup to task-finish events, not the meaning.
|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
- A readiness task expected to produce JUnit evidence but missing from the configured result mapping is a hard failure.
|
||||||
|
- A configured JUnit result directory that contains no usable result remains subject to the existing JUnit evidence validation.
|
||||||
|
- A task claim with no successful completion event is simply uncovered and therefore becomes missing required evidence when that claim is required.
|
||||||
|
- Unsupported evidence profile remains a hard failure.
|
||||||
|
- Missing immutable image digest/dependency versions retain the existing blocker behavior.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
1. Unit-test task-event classification in `JpaEvidenceExecutionService`.
|
||||||
|
2. Unit-test pure JUnit result lookup from configured task-path/directory inputs.
|
||||||
|
3. TestKit: apply `ca.jpa-evidence` in a fixture and verify the generator task exposes typed inputs without execution-time project lookup.
|
||||||
|
4. Existing JPA evidence verifier tests must remain green.
|
||||||
|
5. Run `build-tools:check --warning-mode=fail`.
|
||||||
|
6. Run `verifyJpaReadinessRegistry verifyJpaReleaseGateTasks --warning-mode=fail`.
|
||||||
|
7. Run the affected JPA leaf `check`.
|
||||||
|
8. Run a candidate evidence lane far enough to confirm no `Task.project` deprecation is emitted; environment-dependent Docker/Testcontainers failure may be reported separately from Gradle-model warnings.
|
||||||
|
|
||||||
|
## Migration boundary
|
||||||
|
|
||||||
|
This change only decouples evidence generation from the live Gradle model. It does not alter the readiness registry, producer tasks, JUnit test suites, manifest schema, release workflow, or evidence verification policy.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Messaging Platform Bridge Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Replace the application-specific broker seam with one canonical anti-corruption bridge:
|
||||||
|
|
||||||
|
```
|
||||||
|
application-core IntegrationEventPublishPort
|
||||||
|
-> adapter/outbound/messaging/platformbridge
|
||||||
|
-> messaging-schema-api EncodedMessagePublisher
|
||||||
|
-> messaging-runtime-core DefaultMessagePublisher
|
||||||
|
-> messaging transport/runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
The bridge must preserve canonical event identity and exact encoded bytes while reusing the platform's destination resolution, authorization, admission, runtime leasing, transport normalization, and observation pipeline.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This phase introduces and verifies the canonical bridge. It does **not** migrate the legacy outbox storage/relay rows, because `OutboxEvent` does not retain the schema/order/tenant metadata required to reconstruct `ValidatedIntegrationEvent` without invention.
|
||||||
|
|
||||||
|
## Application boundary
|
||||||
|
|
||||||
|
Create `IntegrationEventPublishPort` in `application-core`.
|
||||||
|
|
||||||
|
Signature:
|
||||||
|
|
||||||
|
```java
|
||||||
|
CompletionStage<OutboxPublishOutcome> publish(ValidatedIntegrationEvent event);
|
||||||
|
```
|
||||||
|
|
||||||
|
The application package depends only on its own canonical event model and application outcome vocabulary.
|
||||||
|
|
||||||
|
## Adapter bridge
|
||||||
|
|
||||||
|
`PlatformIntegrationEventPublishAdapter` lives under:
|
||||||
|
|
||||||
|
```
|
||||||
|
adapter/outbound/messaging/platformbridge
|
||||||
|
```
|
||||||
|
|
||||||
|
It depends on `EncodedMessagePublisher`, never on a concrete broker client, runtime-core implementation, or transport SPI.
|
||||||
|
|
||||||
|
Mapping rules:
|
||||||
|
|
||||||
|
- `logicalDestinationId` -> platform `DestinationName`.
|
||||||
|
- `contractId` -> platform `MessageType`.
|
||||||
|
- `payloadVersion` -> `SchemaVersion`.
|
||||||
|
- event and causation identities must parse as UUIDv7; values are preserved exactly. Incompatible identities fail closed before the platform publisher is called.
|
||||||
|
- `occurredAt` is used for both `producedAt` and `occurredAt` until the application canonical model carries a separate production timestamp. The bridge never invents a new timestamp.
|
||||||
|
- producer is an explicit constructor/configuration value.
|
||||||
|
- correlation, partition key, tenant, aggregate order and exact envelope bytes are preserved.
|
||||||
|
- trace context is explicitly absent (`TraceContext.none()`) until the application model owns canonical trace context.
|
||||||
|
- exact `envelopeBytes` become `EncodedMessage` bytes; no re-encoding occurs.
|
||||||
|
- schema/catalog/binding/envelope evidence that has no first-class platform field is preserved as bounded `x-ca-*` headers.
|
||||||
|
- the schema reference subject is the canonical contract id and version is the canonical payload version.
|
||||||
|
|
||||||
|
## Outcome mapping
|
||||||
|
|
||||||
|
Mapping is based on completion **and transmission evidence**, not enum name similarity:
|
||||||
|
|
||||||
|
- CONFIRMED -> `OutboxPublishOutcome.CONFIRMED`.
|
||||||
|
- AMBIGUOUS -> `OutboxPublishOutcome.AMBIGUOUS`.
|
||||||
|
- REJECTED + NOT_TRANSMITTED -> `REJECTED_BEFORE_SEND`.
|
||||||
|
- REJECTED + any evidence that bytes may have left the process -> `REJECTED_AFTER_BROKER`.
|
||||||
|
|
||||||
|
Bridge preparation failures are definite pre-send rejection.
|
||||||
|
|
||||||
|
## Platform boundary
|
||||||
|
|
||||||
|
`EncodedMessagePublisher` is owned by `messaging-schema-api`, because `EncodedMessage` is owned there and the dependency direction remains acyclic.
|
||||||
|
|
||||||
|
`DefaultMessagePublisher` implements both `MessagePublisher` and `EncodedMessagePublisher`. The encoded path skips only codec lookup/encoding; destination resolution, access policy, admission, runtime lease, transport send, deadline handling, result normalization and observation are shared with the normal publish path.
|
||||||
|
|
||||||
|
The starter exposes one `DefaultMessagePublisher` singleton, which therefore satisfies both public interfaces.
|
||||||
|
|
||||||
|
## Spring ownership
|
||||||
|
|
||||||
|
`MessagingBridgeRootAutoConfiguration` owns the bridge bean when an `EncodedMessagePublisher` is present **and** `app.messaging.producer-id` is explicitly configured. Producer identity is never inferred from `spring.application.name` or invented. Application bootstrap must not construct Kafka producer clients or implement broker-specific send behavior.
|
||||||
|
|
||||||
|
The existing `KafkaSender` / `KafkaMessageBroker` path remains temporarily for the legacy `OutboxEvent` and realtime publishers, which do not yet carry enough canonical metadata to enter the new bridge without invention. It is explicitly transitional and is removed only with the legacy outbox/realtime cutover. The new canonical bridge never calls it.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Required checks:
|
||||||
|
|
||||||
|
1. `DefaultMessagePublisherTest`: pre-encoded publish preserves bytes and skips codec while still exercising central pipeline.
|
||||||
|
2. `PlatformIntegrationEventPublishAdapterTest`: golden mapping, outcome mapping, fail-closed identity behavior.
|
||||||
|
3. outbound messaging module tests/check.
|
||||||
|
4. messaging runtime/starter tests.
|
||||||
|
5. app-bootstrap system test and architecture test after adding the canonical bridge while retaining the documented legacy seam.
|
||||||
|
6. search proving app-bootstrap has no direct native Kafka sender configuration.
|
||||||
|
7. dependency/build lock refresh only where dependency ownership changed.
|
||||||
|
8. `git diff --check`.
|
||||||
|
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
# Outbox Transport-Only Cutover Design
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Approved implementation slice for MSG-015 transport-only cutover.
|
||||||
|
|
||||||
|
This design deliberately does **not** activate `POLLING_V2` and does not migrate the publication authority to the v2 delivery tables. The existing `outbox_event` writer/store/claim/status authority remains the only active authority. The change makes that legacy authority capable of carrying a canonical integration event without losing the exact platform envelope.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Support both row generations under one legacy relay authority:
|
||||||
|
|
||||||
|
```text
|
||||||
|
business transaction
|
||||||
|
-> legacy NewOutboxEvent -> legacy row
|
||||||
|
-> canonical ValidatedIntegrationEvent -> canonical-compatible row
|
||||||
|
|
||||||
|
one OutboxStorePort claim authority
|
||||||
|
-> legacy claimed row -> MessageBroker compatibility path
|
||||||
|
-> canonical claimed row -> IntegrationEventPublishPort -> messaging platform
|
||||||
|
```
|
||||||
|
|
||||||
|
A row is published through exactly one branch. There is no dual write and no second relay scheduler.
|
||||||
|
|
||||||
|
## Application boundaries
|
||||||
|
|
||||||
|
### Canonical append
|
||||||
|
|
||||||
|
`OutboxAppendPort` becomes the canonical durable append boundary:
|
||||||
|
|
||||||
|
```java
|
||||||
|
void append(ValidatedIntegrationEvent event);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legacy append
|
||||||
|
|
||||||
|
Raw R0 payload append moves to an explicitly named compatibility port:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LegacyOutboxAppendPort
|
||||||
|
void append(NewOutboxEvent event);
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing sample/durable-operation code that still emits raw `NewOutboxEvent` uses only the legacy port. New canonical code must not call the legacy port.
|
||||||
|
|
||||||
|
### Claimed row model
|
||||||
|
|
||||||
|
The relay-facing row is a sealed application model:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ClaimedOutboxEvent
|
||||||
|
|- OutboxEvent // legacy R0 claim model retained for compatibility
|
||||||
|
`- CanonicalClaimedOutboxEvent // reconstructs one ValidatedIntegrationEvent
|
||||||
|
```
|
||||||
|
|
||||||
|
`OutboxStorePort.claimBatch` returns `List<ClaimedOutboxEvent>`.
|
||||||
|
|
||||||
|
Common relay state is exposed by the sealed interface: event id, event type, aggregate id, occurred-at, status and attempt count. The canonical subtype also exposes the exact `ValidatedIntegrationEvent`.
|
||||||
|
|
||||||
|
A persisted row with a **partial** canonical metadata set is corrupt and fails closed during mapping. It is never downgraded to the legacy path.
|
||||||
|
|
||||||
|
## Storage compatibility projection
|
||||||
|
|
||||||
|
The existing PostgreSQL `outbox_event` remains authoritative. Add a forward migration after current legacy V12 that:
|
||||||
|
|
||||||
|
- widens `event_id` to `varchar(96)`;
|
||||||
|
- widens `correlation_id` to `varchar(128)`;
|
||||||
|
- adds nullable canonical columns to preserve existing rows;
|
||||||
|
- adds an all-or-none canonical-shape check;
|
||||||
|
- stores exact canonical envelope bytes in `bytea`;
|
||||||
|
- keeps the legacy required columns for the rollback window.
|
||||||
|
|
||||||
|
Canonical required columns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
contract_id
|
||||||
|
envelope_version
|
||||||
|
payload_version
|
||||||
|
logical_destination
|
||||||
|
tenant_scope
|
||||||
|
aggregate_type
|
||||||
|
aggregate_sequence
|
||||||
|
event_index
|
||||||
|
partition_key
|
||||||
|
envelope_bytes
|
||||||
|
content_type
|
||||||
|
schema_set_hash
|
||||||
|
envelope_sha256
|
||||||
|
envelope_schema_hash
|
||||||
|
payload_schema_hash
|
||||||
|
contract_catalog_revision
|
||||||
|
destination_binding_revision
|
||||||
|
```
|
||||||
|
|
||||||
|
`causation_id` is optional by the application contract.
|
||||||
|
|
||||||
|
Existing legacy columns remain populated for canonical rows with this compatibility projection:
|
||||||
|
|
||||||
|
```text
|
||||||
|
event_id = canonical event id
|
||||||
|
aggregate_id = canonical aggregate id
|
||||||
|
event_type = contract id
|
||||||
|
payload = exact envelope bytes decoded as strict UTF-8
|
||||||
|
occurred_at = canonical occurred-at
|
||||||
|
status = PENDING
|
||||||
|
attempt_count = 0
|
||||||
|
next_attempt_at = occurred-at
|
||||||
|
correlation_id = canonical correlation id
|
||||||
|
idempotency_key = event id
|
||||||
|
```
|
||||||
|
|
||||||
|
The canonical encoder currently emits a UTF-8 JSON envelope. The append adapter verifies strict UTF-8 round-trip before storing the compatibility text. Invalid UTF-8 fails the business transaction; replacement characters are forbidden.
|
||||||
|
|
||||||
|
`partitionKeyBytes` is not stored separately because the canonical model already requires it to be exactly the US-ASCII bytes of `partitionKeyText`. The claimed model reconstructs those bytes from the stored canonical text.
|
||||||
|
|
||||||
|
## Persistence adapters
|
||||||
|
|
||||||
|
`OutboxStoreAdapter` remains the legacy claim/status store and implements `LegacyOutboxAppendPort`, not `OutboxAppendPort`.
|
||||||
|
|
||||||
|
A separate `CanonicalOutboxAppendAdapter` implements `OutboxAppendPort`. It participates in the caller's existing write transaction exactly like the legacy adapter and never opens a local transaction.
|
||||||
|
|
||||||
|
Both write the same `outbox_event` table; they are alternative semantic inputs, not dual writers for one business fact.
|
||||||
|
|
||||||
|
## Activation
|
||||||
|
|
||||||
|
Introduce:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ca-skeleton.outbox.canonical-transport-enabled=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Default remains false.
|
||||||
|
|
||||||
|
When false:
|
||||||
|
- existing legacy append/relay behavior is unchanged;
|
||||||
|
- canonical append bean is not exposed;
|
||||||
|
- canonical relay routing is not considered an active deployment capability.
|
||||||
|
|
||||||
|
When true:
|
||||||
|
- canonical append bean is exposed;
|
||||||
|
- startup requires an `IntegrationEventPublishPort`;
|
||||||
|
- the relay publisher can route canonical claimed rows to that port;
|
||||||
|
- legacy rows continue through `MessageBroker`;
|
||||||
|
- while mixed legacy rows may still exist, a relay-enabled deployment still requires the legacy broker. Canonical transport is an additional route, not permission to strand legacy backlog.
|
||||||
|
|
||||||
|
The gate is a compatibility/cutover gate only. It does not change DB publication authority and does not activate `POLLING_V2`.
|
||||||
|
|
||||||
|
## Publish routing
|
||||||
|
|
||||||
|
`OutboxMessagePublishPort` remains the one relay publish port and accepts `ClaimedOutboxEvent`.
|
||||||
|
|
||||||
|
Implementation behavior:
|
||||||
|
- `OutboxEvent` -> existing `OutboxEnvelopeJson` + `MessageBroker`.
|
||||||
|
- `CanonicalClaimedOutboxEvent` -> exact stored `ValidatedIntegrationEvent` -> `IntegrationEventPublishPort`.
|
||||||
|
|
||||||
|
The canonical branch blocks on the returned `CompletionStage` only at this legacy compatibility boundary, because the current legacy relay port is synchronous. The platform result is mapped unchanged into `OutboxPublishOutcome`.
|
||||||
|
|
||||||
|
The bridge does not re-encode canonical bytes.
|
||||||
|
|
||||||
|
If canonical transport is disabled or the canonical publisher is absent, canonical publication fails closed before broker/platform transmission. Startup validation prevents the normal configured case from reaching that state.
|
||||||
|
|
||||||
|
## Outcome policy
|
||||||
|
|
||||||
|
The existing legacy relay state machine remains authoritative in this slice:
|
||||||
|
- CONFIRMED -> mark PUBLISHED.
|
||||||
|
- AMBIGUOUS -> retryable legacy FAILED flow.
|
||||||
|
- REJECTED_BEFORE_SEND / REJECTED_AFTER_BROKER -> existing definite-refusal DEAD behavior.
|
||||||
|
|
||||||
|
This is intentionally the existing compatibility semantics. The richer v2 per-attempt state machine is a later storage-authority cutover.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
This slice does not:
|
||||||
|
- switch `OutboxPublicationAuthority` to `POLLING_V2`;
|
||||||
|
- mutate/reconcile `outbox_event_log_v2` or `outbox_delivery_v2`;
|
||||||
|
- implement CDC;
|
||||||
|
- remove `MessageBroker`, `KafkaSender`, `NewOutboxEvent`, `OutboxEvent`, or the legacy scheduler;
|
||||||
|
- migrate old rows into canonical rows;
|
||||||
|
- invent tenant, trace, schema or routing metadata for old rows.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Required:
|
||||||
|
1. application port split compiles and old raw producers use `LegacyOutboxAppendPort`;
|
||||||
|
2. migration integration proves additive columns, exact BYTEA, constraints and legacy compatibility;
|
||||||
|
3. canonical append adapter round-trips every canonical field and exact bytes;
|
||||||
|
4. partial canonical row mapping fails closed;
|
||||||
|
5. legacy row mapping remains unchanged;
|
||||||
|
6. relay unit test proves canonical row invokes only `IntegrationEventPublishPort`;
|
||||||
|
7. legacy row invokes only `MessageBroker`;
|
||||||
|
8. canonical bytes reaching `PlatformIntegrationEventPublishAdapter` are byte-identical;
|
||||||
|
9. startup rejects canonical transport enabled without `IntegrationEventPublishPort`;
|
||||||
|
10. default-off composition preserves current behavior;
|
||||||
|
11. architecture/dependency checks and `git diff --check` pass.
|
||||||
@@ -227,6 +227,7 @@ APP_CACHE_REDIS_L1_INVALIDATION_QUEUE_CAPACITY=1024
|
|||||||
APP_CACHE_DEFAULT_TTL=300s
|
APP_CACHE_DEFAULT_TTL=300s
|
||||||
APP_CACHE_NEGATIVE_TTL=60s
|
APP_CACHE_NEGATIVE_TTL=60s
|
||||||
APP_MESSAGING_BROKER=
|
APP_MESSAGING_BROKER=
|
||||||
|
APP_MESSAGING_PRODUCER_ID=
|
||||||
APP_MESSAGING_KAFKA_BROKERS=
|
APP_MESSAGING_KAFKA_BROKERS=
|
||||||
APP_NOTIFICATION_SLACK_PROVIDER=
|
APP_NOTIFICATION_SLACK_PROVIDER=
|
||||||
APP_NOTIFICATION_EMAIL_PROVIDER=
|
APP_NOTIFICATION_EMAIL_PROVIDER=
|
||||||
@@ -301,6 +302,7 @@ APP_GRAPHQL_ENABLED=false
|
|||||||
APP_GRAPHQL_DEPLOYMENT_MODE=
|
APP_GRAPHQL_DEPLOYMENT_MODE=
|
||||||
APP_OUTBOX_ENABLED=false
|
APP_OUTBOX_ENABLED=false
|
||||||
APP_OUTBOX_RELAY_ENABLED=false
|
APP_OUTBOX_RELAY_ENABLED=false
|
||||||
|
APP_OUTBOX_CANONICAL_TRANSPORT_ENABLED=false
|
||||||
APP_NOTIFICATION_PLATFORM_ENABLED=false
|
APP_NOTIFICATION_PLATFORM_ENABLED=false
|
||||||
APP_NOTIFICATION_PLATFORM_MODE=SERVING
|
APP_NOTIFICATION_PLATFORM_MODE=SERVING
|
||||||
# OpenAPI exposure. application-prod.yml pins both false regardless of these.
|
# OpenAPI exposure. application-prod.yml pins both false regardless of these.
|
||||||
|
|||||||
@@ -87,70 +87,15 @@ dependencies {
|
|||||||
testImplementation 'io.micrometer:micrometer-core'
|
testImplementation 'io.micrometer:micrometer-core'
|
||||||
}
|
}
|
||||||
|
|
||||||
registerGraphQlPlatformTestLanes()
|
|
||||||
|
|
||||||
registerStrictQualificationTest(
|
extensions.getByName('strictQualification').register(
|
||||||
name: 'graphqlTransportQualificationTest',
|
'graphqlTransportQualificationTest',
|
||||||
sourceSet: sourceSets.test,
|
sourceSets.test,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.inbound.graphql.GraphqlHttpBoundaryQualificationTest'
|
'dev.caskeleton.adapter.inbound.graphql.GraphqlHttpBoundaryQualificationTest'
|
||||||
],
|
],
|
||||||
description: 'Runs exact no-skip GraphQL conditional transport wire evidence.')
|
'Runs exact no-skip GraphQL conditional transport wire evidence.'
|
||||||
|
)
|
||||||
// verifyGraphQlProductionJar — the production artifact must carry nothing a test wrote.
|
|
||||||
//
|
|
||||||
// Moving the testkit into test fixtures is a source-tree decision, and source-tree decisions drift.
|
|
||||||
// One `implementation` where a `testFixturesImplementation` belonged, one file created in the wrong
|
|
||||||
// directory, and the contract suites are back inside the jar an adopter deploys — where an
|
|
||||||
// in-memory persisted-operation registry looks like a working bean until a second instance starts,
|
|
||||||
// and where `testContext(String)` hands out an authenticated actor to anyone who calls it.
|
|
||||||
//
|
|
||||||
// So the claim is checked against the jar rather than against the layout that is supposed to
|
|
||||||
// produce it. Entry names and class names only: this reads the archive index, never the bytecode.
|
|
||||||
tasks.register('verifyGraphQlProductionJar') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Fails when the GraphQL production jar contains testkit, fixture or in-memory-only types.'
|
|
||||||
|
|
||||||
dependsOn tasks.named('jar')
|
|
||||||
def jarFile = tasks.named('jar').flatMap { it.archiveFile }
|
|
||||||
inputs.file(jarFile)
|
|
||||||
outputs.upToDateWhen { true }
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
Map<String, String> forbidden = [
|
|
||||||
'/testkit/' : 'contract suites and integration fixtures belong to test fixtures',
|
|
||||||
'InMemory' : 'an in-memory implementation is a development stand-in, not a shipped default',
|
|
||||||
'ForTests' : 'a for-tests factory in the production jar is reachable from production code',
|
|
||||||
'TestContext' : 'a credential-free authenticated context must not ship',
|
|
||||||
'Fixture' : 'fixtures belong to test fixtures',
|
|
||||||
]
|
|
||||||
List<String> violations = []
|
|
||||||
new java.util.zip.ZipFile(jarFile.get().asFile).withCloseable { archive ->
|
|
||||||
archive.entries().each { entry ->
|
|
||||||
if (entry.directory || !entry.name.endsWith('.class')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
forbidden.each { marker, reason ->
|
|
||||||
if (entry.name.contains(marker)) {
|
|
||||||
violations << "${entry.name}: ${reason}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!violations.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
"The GraphQL production jar contains non-production types:\n " +
|
|
||||||
violations.sort().join('\n ') +
|
|
||||||
"\nMove them to src/testFixtures/java, or declare them with " +
|
|
||||||
"testFixturesImplementation."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('check') {
|
|
||||||
dependsOn tasks.named('verifyGraphQlProductionJar')
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyGraphQlApiSurface — every public type this leaf exposes is a committed decision.
|
// verifyGraphQlApiSurface — every public type this leaf exposes is a committed decision.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ dependencies {
|
|||||||
testImplementation "io.grpc:grpc-stub:${grpcVersion}"
|
testImplementation "io.grpc:grpc-stub:${grpcVersion}"
|
||||||
}
|
}
|
||||||
|
|
||||||
registerStrictQualificationTest(
|
extensions.getByName('strictQualification').register(
|
||||||
name: 'grpcTransportQualificationTest',
|
'grpcTransportQualificationTest',
|
||||||
sourceSet: sourceSets.test,
|
sourceSets.test,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.inbound.grpc.GrpcSafeActivationTest',
|
'dev.caskeleton.adapter.inbound.grpc.GrpcSafeActivationTest',
|
||||||
'dev.caskeleton.adapter.inbound.grpc.GrpcP1BoundaryWireTest'
|
'dev.caskeleton.adapter.inbound.grpc.GrpcP1BoundaryWireTest'
|
||||||
],
|
],
|
||||||
description: 'Runs exact no-skip gRPC conditional transport wire evidence.')
|
'Runs exact no-skip gRPC conditional transport wire evidence.'
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
||||||
@@ -70,7 +71,7 @@ tasks.named('test') {
|
|||||||
// The web platform's reusable ArchUnit rules ship in their own source set, consumed by this leaf's
|
// The web platform's reusable ArchUnit rules ship in their own source set, consumed by this leaf's
|
||||||
// tests and by the composition root. A rule pack that only its own fixture tests import is verified
|
// tests and by the composition root. A rule pack that only its own fixture tests import is verified
|
||||||
// as library code and applied to nothing — the shape the JPA testkit had to be corrected out of.
|
// as library code and applied to nothing — the shape the JPA testkit had to be corrected out of.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
// The Jetty compatibility lane is its own source set because it needs a different embedded
|
// The Jetty compatibility lane is its own source set because it needs a different embedded
|
||||||
// server on the classpath. Two servers in one source set means Spring Boot picks one and the
|
// server on the classpath. Two servers in one source set means Spring Boot picks one and the
|
||||||
// "Jetty" lane silently runs on Tomcat — a compatibility matrix that certifies the same
|
// "Jetty" lane silently runs on Tomcat — a compatibility matrix that certifies the same
|
||||||
@@ -183,22 +184,23 @@ strictTestLanes {
|
|||||||
lane('webFluxContractTest') {
|
lane('webFluxContractTest') {
|
||||||
sourceSet = 'webfluxContractTest'
|
sourceSet = 'webfluxContractTest'
|
||||||
description = 'Runs the Stable HTTP contract against a real Reactor Netty.'
|
description = 'Runs the Stable HTTP contract against a real Reactor Netty.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Docker-gated, and it says so rather than skipping. A lane that quietly passes when the
|
// Docker-gated, and it says so rather than skipping. A lane that quietly passes when the
|
||||||
// container runtime is missing is a lane that has been certifying nothing since whenever Docker
|
// container runtime is missing is a lane that has been certifying nothing since whenever Docker
|
||||||
// last broke.
|
// last broke.
|
||||||
lane('webNginxProxyTest') {
|
lane('webNginxProxyTest') {
|
||||||
|
integration()
|
||||||
sourceSet = 'nginxProxyTest'
|
sourceSet = 'nginxProxyTest'
|
||||||
description = 'Runs the proxy, prefix and spoofing contract behind a real Nginx.'
|
description = 'Runs the proxy, prefix and spoofing contract behind a real Nginx.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
lane('webJettyCompatTest') {
|
lane('webJettyCompatTest') {
|
||||||
sourceSet = 'jettyCompatTest'
|
sourceSet = 'jettyCompatTest'
|
||||||
description = 'Runs the Stable HTTP contract against a real Jetty instead of Tomcat.'
|
description = 'Runs the Stable HTTP contract against a real Jetty instead of Tomcat.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The cross-stack gate. It depends on every recording lane rather than tolerating a missing one:
|
// The cross-stack gate. It depends on every recording lane rather than tolerating a missing one:
|
||||||
@@ -207,10 +209,8 @@ strictTestLanes {
|
|||||||
lane('webCrossStackParityTest') {
|
lane('webCrossStackParityTest') {
|
||||||
tag = 'web-parity'
|
tag = 'web-parity'
|
||||||
description = 'Compares the wire contract recorded by Tomcat, Jetty and Reactor Netty.'
|
description = 'Compares the wire contract recorded by Tomcat, Jetty and Reactor Netty.'
|
||||||
customize = { test ->
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
test.jvmArgs '-Duser.timezone=UTC'
|
dependsOn 'test', 'webJettyCompatTest', 'webFluxContractTest'
|
||||||
test.dependsOn 'test', 'webJettyCompatTest', 'webFluxContractTest'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Advanced lane. Every capability is off unless a deployment names it, so none of them is
|
// The Advanced lane. Every capability is off unless a deployment names it, so none of them is
|
||||||
@@ -223,7 +223,7 @@ strictTestLanes {
|
|||||||
lane('webAdvancedTest') {
|
lane('webAdvancedTest') {
|
||||||
tag = 'web-advanced'
|
tag = 'web-advanced'
|
||||||
description = 'Runs every web Advanced capability contract.'
|
description = 'Runs every web Advanced capability contract.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,16 +241,9 @@ strictTestLanes {
|
|||||||
lane('webSecurityBoundaryTest') {
|
lane('webSecurityBoundaryTest') {
|
||||||
tag = 'security-boundary'
|
tag = 'security-boundary'
|
||||||
description = 'Runs hermetic JWT/JWKS and CORS filter-boundary contracts with no skips.'
|
description = 'Runs hermetic JWT/JWKS and CORS filter-boundary contracts with no skips.'
|
||||||
customize = { test ->
|
shouldRunAfter 'test'
|
||||||
test.shouldRunAfter test.project.tasks.named('test')
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
test.jvmArgs '-Duser.timezone=UTC'
|
rejectSkipped = true
|
||||||
test.afterSuite { descriptor, result ->
|
|
||||||
if (descriptor.parent == null && result.skippedTestCount > 0) {
|
|
||||||
throw new GradleException(
|
|
||||||
"webSecurityBoundaryTest forbids skipped tests: ${result.skippedTestCount}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
||||||
@@ -57,7 +58,7 @@ dependencies {
|
|||||||
// The platform's reusable ArchUnit rules ship in their own source set, consumed by this leaf's
|
// The platform's reusable ArchUnit rules ship in their own source set, consumed by this leaf's
|
||||||
// tests and available to the composition root. A rule pack that only its own fixture tests import
|
// tests and available to the composition root. A rule pack that only its own fixture tests import
|
||||||
// is verified as library code and applied to nothing.
|
// is verified as library code and applied to nothing.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
// Jetty replaces Tomcat for this lane only. With both on one classpath Boot starts Tomcat and
|
// Jetty replaces Tomcat for this lane only. With both on one classpath Boot starts Tomcat and
|
||||||
// the lane certifies the same container twice — which for a WebSocket matters more than for
|
// the lane certifies the same container twice — which for a WebSocket matters more than for
|
||||||
// HTTP, because upgrade handling, close-frame timing and idle handling are all container code.
|
// HTTP, because upgrade handling, close-frame timing and idle handling are all container code.
|
||||||
@@ -111,9 +112,10 @@ strictTestLanes {
|
|||||||
// Docker-gated, and it fails rather than skipping. A proxy contract that quietly passes without
|
// Docker-gated, and it fails rather than skipping. A proxy contract that quietly passes without
|
||||||
// a proxy has been certifying nothing since whenever the container runtime last broke.
|
// a proxy has been certifying nothing since whenever the container runtime last broke.
|
||||||
lane('websocketNginxTest') {
|
lane('websocketNginxTest') {
|
||||||
|
integration()
|
||||||
sourceSet = 'nginxWebSocketTest'
|
sourceSet = 'nginxWebSocketTest'
|
||||||
description = 'Runs the upgrade and forwarded-header contract behind a real Nginx.'
|
description = 'Runs the upgrade and forwarded-header contract behind a real Nginx.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The real-container lane. Upgrade negotiation, close-frame handling and idle behaviour are
|
// The real-container lane. Upgrade negotiation, close-frame handling and idle behaviour are
|
||||||
@@ -121,7 +123,7 @@ strictTestLanes {
|
|||||||
lane('websocketJettyTest') {
|
lane('websocketJettyTest') {
|
||||||
sourceSet = 'jettyWebSocketTest'
|
sourceSet = 'jettyWebSocketTest'
|
||||||
description = 'Runs the WebSocket runtime contract against a real Jetty instead of Tomcat.'
|
description = 'Runs the WebSocket runtime contract against a real Jetty instead of Tomcat.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Advanced lane. Every capability is off unless a deployment names it, so nothing a
|
// The Advanced lane. Every capability is off unless a deployment names it, so nothing a
|
||||||
@@ -131,14 +133,15 @@ strictTestLanes {
|
|||||||
lane('websocketAdvancedTest') {
|
lane('websocketAdvancedTest') {
|
||||||
tag = 'websocket-advanced'
|
tag = 'websocket-advanced'
|
||||||
description = 'Runs every WebSocket Advanced capability contract.'
|
description = 'Runs every WebSocket Advanced capability contract.'
|
||||||
customize = { test -> test.jvmArgs '-Duser.timezone=UTC' }
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerStrictQualificationTest(
|
extensions.getByName('strictQualification').register(
|
||||||
name: 'websocketTransportQualificationTest',
|
'websocketTransportQualificationTest',
|
||||||
sourceSet: sourceSets.test,
|
sourceSets.test,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.inbound.websocket.stomp.WebSocketBoundaryQualificationTest'
|
'dev.caskeleton.adapter.inbound.websocket.stomp.WebSocketBoundaryQualificationTest'
|
||||||
],
|
],
|
||||||
description: 'Runs exact no-skip WebSocket conditional transport wire evidence.')
|
'Runs exact no-skip WebSocket conditional transport wire evidence.'
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
|
id 'ca.redis-topology-lane'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redis SDK leaf — see docs/superpowers/specs/2026-08-07-redis-wrapper-typed-api-design.md.
|
// Redis SDK leaf — see docs/superpowers/specs/2026-08-07-redis-wrapper-typed-api-design.md.
|
||||||
@@ -50,148 +51,6 @@ dependencies {
|
|||||||
// Zero imports.
|
// Zero imports.
|
||||||
}
|
}
|
||||||
|
|
||||||
// The topology lane is opt-in and fail-closed. The default unit task excludes it, and selecting it
|
// Redis topology qualification is opt-in and owned by `ca.redis-topology-lane`. The Java
|
||||||
// without an endpoint is an error rather than a skip: a topology test that silently passes because
|
// convention owns mode/property validation, tag selection, TLS deployment mapping, required-class
|
||||||
// it never connected is worse than not having one.
|
// coverage and no-skip enforcement; this leaf keeps only its dependency declaration surface.
|
||||||
tasks.named('test') {
|
|
||||||
useJUnitPlatform {
|
|
||||||
excludeTags 'redis-topology'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lane selection is derived from the declared mode rather than chosen by hand. A promotion test is
|
|
||||||
// meaningless without sentinels and a cross-slot test is meaningless without a cluster, but writing
|
|
||||||
// that as a runtime assumption would turn "the lane was never started" into a green skip. Selecting
|
|
||||||
// by tag keeps the lane fail-closed: what a mode cannot prove is not selected, and what is selected
|
|
||||||
// must pass.
|
|
||||||
//
|
|
||||||
// The mode is an allowlist, not free text. Deriving the tag from an arbitrary property produced the
|
|
||||||
// worst possible result for a qualification lane: `-Predis.topology.mode=TYPO` built the tag
|
|
||||||
// `lane-typo`, matched nothing, ran zero tests and exited 0. A release gate that reports success
|
|
||||||
// for a lane it never ran is worse than no gate, so an unknown mode is an error and a run that
|
|
||||||
// executed no test is a failure.
|
|
||||||
// `tls` is a lane, not a deployment mode. Its shape is standalone; what it qualifies is the
|
|
||||||
// transport, which no other lane carries a single command over. It was reachable only by hand —
|
|
||||||
// point LiveRedisCompositionTest at the TLS compose with an ad-hoc init script — which is another
|
|
||||||
// way of saying the release gate did not cover TLS at all.
|
|
||||||
def REDIS_TOPOLOGY_MODES = ['standalone', 'sentinel', 'cluster', 'tls'] as Set
|
|
||||||
def REDIS_TOPOLOGY_DEPLOYMENT_MODE = ['standalone': 'standalone', 'sentinel': 'sentinel',
|
|
||||||
'cluster': 'cluster', 'tls': 'standalone']
|
|
||||||
// The classes each lane exists to run. A declaration, not an observation: a lane that lost a class
|
|
||||||
// to a rename otherwise still reports success on whatever remains.
|
|
||||||
//
|
|
||||||
// This list stays hand-written and that is a deliberate refusal, not an oversight. The repository's
|
|
||||||
// own mechanism for "a named test must actually have run" is `strictTestLanes { lane { requires(…) } }`,
|
|
||||||
// and ca.strict-test-lane refuses a lane that declares both a tag and required tests — "pick one
|
|
||||||
// selection". This lane's selection is a tag expression computed from the mode, so `requires` is not
|
|
||||||
// available to it. Deriving the list instead would mean reading @Tag off the compiled test classes,
|
|
||||||
// which is a bytecode dependency a leaf build file should not grow.
|
|
||||||
//
|
|
||||||
// What did go: REDIS_TOPOLOGY_MINIMUM_TESTS, a per-mode floor of 20/20/24/4 that had to be edited
|
|
||||||
// whenever a case was added or removed, and whose failure sentence was "coverage shrank" — not a
|
|
||||||
// runtime, deployment, data, security or compile failure. The class list below covers the case that
|
|
||||||
// mattered (a lane class silently leaving the lane); the number did not add a second one.
|
|
||||||
def REDIS_TOPOLOGY_REQUIRED_CLASSES = [
|
|
||||||
'standalone': ['LiveRedisCompositionTest', 'LiveRedisSemanticPortsTest',
|
|
||||||
'RedisTopologyContractTest', 'LiveRedisGuardrailTest'],
|
|
||||||
'sentinel' : ['LiveRedisCompositionTest', 'LiveRedisSentinelPromotionTest',
|
|
||||||
'RedisTopologyContractTest'],
|
|
||||||
'cluster' : ['LiveRedisCompositionTest', 'LiveRedisClusterTest',
|
|
||||||
'LiveRedisClusterTransactionTest', 'LiveRedisSemanticPortsTest'],
|
|
||||||
'tls' : ['LiveRedisTlsTest'],
|
|
||||||
]
|
|
||||||
|
|
||||||
String declaredMode = (project.findProperty('redis.topology.mode') ?: 'unset').toString().toLowerCase()
|
|
||||||
|
|
||||||
// Declared through the convention rather than hand-rolled. `ca.strict-test-lane` owns
|
|
||||||
// testClassesDirs, classpath, the tag filter, failOnNoDiscoveredTests, the refusal to serve an
|
|
||||||
// up-to-date result, and the "executed nothing" check — the same six things this task spelled out.
|
|
||||||
// What stays here is what is true of this lane only: the mode allowlist, the endpoint properties and
|
|
||||||
// the class-coverage check.
|
|
||||||
//
|
|
||||||
// The @Tag source-text scan that used to sit in `doFirst` is gone. It read every .java file in the
|
|
||||||
// test source set looking for the literal strings `@Tag("redis-topology")` and `@Tag("lane-<mode>")`,
|
|
||||||
// which a comment satisfied and a tag held in a constant defeated — and by its own comment it only
|
|
||||||
// improved the message for a failure `failOnNoDiscoveredTests` already produces.
|
|
||||||
strictTestLanes {
|
|
||||||
lane('redisTopologyTest') {
|
|
||||||
tag = "redis-topology & lane-${declaredMode}".toString()
|
|
||||||
description = 'Runs the Redis SDK contracts against a real topology declared in infra/redis-sdk.'
|
|
||||||
customize = { test ->
|
|
||||||
['redis.topology.host', 'redis.topology.port',
|
|
||||||
'redis.topology.master', 'redis.topology.username', 'redis.topology.password',
|
|
||||||
'redis.topology.trust-material']
|
|
||||||
.each { String key ->
|
|
||||||
if (project.hasProperty(key)) {
|
|
||||||
test.systemProperty key, project.property(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The lane name and the deployment mode are different things, and only the TLS lane makes
|
|
||||||
// that visible: its shape is standalone, so the tests must see `standalone` while the tag
|
|
||||||
// filter and the required properties come from the lane. Passing the lane name through as
|
|
||||||
// the mode would fail RedisDeploymentMode.valueOf on a value that is not a topology.
|
|
||||||
test.systemProperty 'redis.topology.mode',
|
|
||||||
REDIS_TOPOLOGY_DEPLOYMENT_MODE.getOrDefault(declaredMode, declaredMode)
|
|
||||||
test.systemProperty 'redis.topology.tls', (declaredMode == 'tls').toString()
|
|
||||||
|
|
||||||
// Executed, not merely reported. `afterTest` fires for a skipped test too, so counting
|
|
||||||
// every callback would let a lane whose tests all skipped satisfy the checks below.
|
|
||||||
def skipped = new java.util.concurrent.atomic.AtomicInteger()
|
|
||||||
def classes = java.util.Collections.synchronizedSet(new java.util.LinkedHashSet<String>())
|
|
||||||
test.afterTest { descriptor, result ->
|
|
||||||
if (result.resultType == org.gradle.api.tasks.testing.TestResult.ResultType.SKIPPED) {
|
|
||||||
skipped.incrementAndGet()
|
|
||||||
} else {
|
|
||||||
classes.add(descriptor.className.tokenize('.').last())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test.doFirst {
|
|
||||||
if (!REDIS_TOPOLOGY_MODES.contains(declaredMode)) {
|
|
||||||
throw new GradleException(
|
|
||||||
"redisTopologyTest was selected with redis.topology.mode='${declaredMode}'; " +
|
|
||||||
'the supported modes are ' + REDIS_TOPOLOGY_MODES.sort().join(', ') +
|
|
||||||
'. An unrecognised mode selects no test and would otherwise report success.')
|
|
||||||
}
|
|
||||||
def required = ['redis.topology.host', 'redis.topology.port']
|
|
||||||
if (declaredMode == 'sentinel') {
|
|
||||||
required += 'redis.topology.master'
|
|
||||||
}
|
|
||||||
if (declaredMode == 'tls') {
|
|
||||||
// Without the trust material the client would have to disable verification to
|
|
||||||
// connect, and a TLS lane that trusts anything qualifies nothing.
|
|
||||||
required += 'redis.topology.trust-material'
|
|
||||||
}
|
|
||||||
def missing = required.findAll { !project.hasProperty(it) }
|
|
||||||
if (!missing.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
'redisTopologyTest was selected without ' + missing.join(', ') +
|
|
||||||
'; start a lane from infra/redis-sdk and pass -P<key>=<value>.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test.doLast {
|
|
||||||
// What a lane must cover, named rather than counted by accident. A tag filter matching
|
|
||||||
// one trivial class satisfied "ran something" while the class the lane exists for had
|
|
||||||
// been renamed out of the filter, and nothing said so.
|
|
||||||
def absent = REDIS_TOPOLOGY_REQUIRED_CLASSES[declaredMode].findAll {
|
|
||||||
!classes.contains(it)
|
|
||||||
}
|
|
||||||
if (!absent.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
"redisTopologyTest ran the ${declaredMode} lane without ${absent.join(', ')}. " +
|
|
||||||
'These classes are what the lane qualifies; a run that skipped them proves ' +
|
|
||||||
'less than the lane claims.')
|
|
||||||
}
|
|
||||||
if (skipped.get() > 0) {
|
|
||||||
throw new GradleException(
|
|
||||||
"redisTopologyTest skipped ${skipped.get()} test(s) on the ${declaredMode} " +
|
|
||||||
'lane. A qualification lane has no conditional coverage: what it cannot prove ' +
|
|
||||||
'must not be selected, and what is selected must run.')
|
|
||||||
}
|
|
||||||
test.logger.lifecycle(
|
|
||||||
"redisTopologyTest: ${declaredMode} lane covered ${classes.size()} class(es).")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
|
id 'ca.jmh-benchmarks'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
||||||
@@ -88,10 +90,9 @@ dependencies {
|
|||||||
// Performance certification and JMH benchmarks are separate source sets for their own reason: they
|
// Performance certification and JMH benchmarks are separate source sets for their own reason: they
|
||||||
// are slow, they assert on resource bounds rather than behaviour, and they must never be part of
|
// are slow, they assert on resource bounds rather than behaviour, and they must never be part of
|
||||||
// the default unit lane.
|
// the default unit lane.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
// The testkit compiles against exactly what a test does: `implementation` inheritance runs
|
// The testkit compiles against exactly what a test does: `implementation` inheritance runs
|
||||||
sourceSet('httpClientPerformanceTest') { compilesAgainst 'main', 'testFixtures' }
|
sourceSet('httpClientPerformanceTest') { compilesAgainst 'main', 'testFixtures' }
|
||||||
sourceSet('jmh') { compilesAgainst 'main', 'testFixtures' }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -119,44 +120,23 @@ dependencies {
|
|||||||
testFixturesImplementation libs.archunit.junit5
|
testFixturesImplementation libs.archunit.junit5
|
||||||
testFixturesImplementation libs.blockhound
|
testFixturesImplementation libs.blockhound
|
||||||
|
|
||||||
jmhImplementation libs.jmh.core
|
|
||||||
jmhAnnotationProcessor libs.jmh.generator.annprocess
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF-8 is pinned for every JavaCompile task in the root build; this leaf no longer repeats it.
|
// UTF-8 is pinned for every JavaCompile task in the root build; this leaf no longer repeats it.
|
||||||
|
|
||||||
// JMH generates its harness classes at compile time. They are not our source, so the
|
// JMH is owned by ca.jmh-benchmarks. This leaf's benchmarks deliberately see test fixtures,
|
||||||
// compile-time checker and -Werror are switched off for that source set only; applying them
|
// not the ordinary test output, so benchmark support remains a reusable main-like surface.
|
||||||
// would fail the build on generated code we cannot edit.
|
jmhBenchmarks {
|
||||||
tasks.named('compileJmhJava', JavaCompile) {
|
compilesAgainst 'main', 'testFixtures'
|
||||||
options.errorprone.enabled = false
|
jsonReport 'reports/jmh/result.json'
|
||||||
options.compilerArgs.removeIf { it == '-Werror' }
|
|
||||||
}
|
|
||||||
|
|
||||||
// The bytecode analyser is disabled for the same generated harness, for the same reason.
|
|
||||||
tasks.named('spotbugsJmh') {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes a Test task. The parameter is left untyped because the IDE's Gradle parser has no Gradle
|
|
||||||
// API on its classpath and reports the annotation as an unresolved type; Groovy dispatches the
|
|
||||||
// calls below dynamically either way.
|
|
||||||
Closure<Void> applyContractSelection = { task ->
|
|
||||||
// Cross-transport contract lane. The same semantic contract runs against every Stable transport;
|
|
||||||
// the transport under test is selected explicitly so a missing transport is an error, not a skip.
|
|
||||||
task.systemProperty 'httpclient.contract.transports',
|
|
||||||
(project.findProperty('httpclient.contract.transports') ?: 'apache,jdk,reactor').toString()
|
|
||||||
// Netty's strictest leak detector is on for every lane. It is only meaningful if it is actually
|
|
||||||
// live, so NettyLeakDetectionExtension asserts the level rather than trusting the flag reached
|
|
||||||
// the forked JVM.
|
|
||||||
task.systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
|
||||||
// HTTP/3 is Experimental: it is never part of the default lane and never silently skipped.
|
|
||||||
task.systemProperty 'httpclient.http3.tests.enabled',
|
|
||||||
(project.findProperty('http3.tests.enabled') ?: 'false').toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test', Test) {
|
tasks.named('test', Test) {
|
||||||
applyContractSelection(it)
|
systemProperty 'httpclient.contract.transports',
|
||||||
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
// Two lanes are excluded from the default run for opposite reasons: the fault lane needs Docker
|
// Two lanes are excluded from the default run for opposite reasons: the fault lane needs Docker
|
||||||
// and fails closed without it, and the BlockHound lane rewrites core JDK bytecode, which must
|
// and fails closed without it, and the BlockHound lane rewrites core JDK bytecode, which must
|
||||||
// not be imposed on every unit run.
|
// not be imposed on every unit run.
|
||||||
@@ -176,16 +156,22 @@ strictTestLanes {
|
|||||||
lane('httpClientBlockHoundTest') {
|
lane('httpClientBlockHoundTest') {
|
||||||
tag = 'httpclient-blockhound'
|
tag = 'httpclient-blockhound'
|
||||||
description = 'Proves no platform code blocks a Reactor event loop (design §18.2, §28.6).'
|
description = 'Proves no platform code blocks a Reactor event loop (design §18.2, §28.6).'
|
||||||
customize = { test ->
|
systemProperty 'httpclient.contract.transports',
|
||||||
applyContractSelection(test)
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
// BlockHound instruments already-loaded JDK classes; Java 13+ needs this to redefine them.
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
test.jvmArgs '-XX:+AllowRedefinitionToAddDeleteMethods'
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
}
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
|
// BlockHound instruments already-loaded JDK classes; Java 13+ needs this to redefine them.
|
||||||
|
jvmArgs '-XX:+AllowRedefinitionToAddDeleteMethods'
|
||||||
}
|
}
|
||||||
lane('httpClientStableContractTest') {
|
lane('httpClientStableContractTest') {
|
||||||
tag = 'httpclient-contract'
|
tag = 'httpclient-contract'
|
||||||
description = 'Runs the cross-transport stable contract suite (design §28.2, §33).'
|
description = 'Runs the cross-transport stable contract suite (design §28.2, §33).'
|
||||||
customize = { test -> applyContractSelection(test) }
|
systemProperty 'httpclient.contract.transports',
|
||||||
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
}
|
}
|
||||||
// The same contract suite, run under the name the release gate and the support matrix quote for
|
// The same contract suite, run under the name the release gate and the support matrix quote for
|
||||||
// the repository's Spring 7 baseline. It used to be a hand-written `tasks.register(..., Test)`
|
// the repository's Spring 7 baseline. It used to be a hand-written `tasks.register(..., Test)`
|
||||||
@@ -196,12 +182,20 @@ strictTestLanes {
|
|||||||
lane('spring70CompatibilityTest') {
|
lane('spring70CompatibilityTest') {
|
||||||
tag = 'httpclient-contract'
|
tag = 'httpclient-contract'
|
||||||
description = 'Runs the contract suite on the repository Spring 7 baseline (design §29).'
|
description = 'Runs the contract suite on the repository Spring 7 baseline (design §29).'
|
||||||
customize = { test -> applyContractSelection(test) }
|
systemProperty 'httpclient.contract.transports',
|
||||||
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
}
|
}
|
||||||
lane('httpClientSecurityTest') {
|
lane('httpClientSecurityTest') {
|
||||||
tag = 'httpclient-security'
|
tag = 'httpclient-security'
|
||||||
description = 'Runs the SSRF, credential-leak, and cardinality suite (design §28.5, §28.7).'
|
description = 'Runs the SSRF, credential-leak, and cardinality suite (design §28.5, §28.7).'
|
||||||
customize = { test -> applyContractSelection(test) }
|
systemProperty 'httpclient.contract.transports',
|
||||||
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
}
|
}
|
||||||
// Spring 6.2 / 7.0 compatibility lanes. This repository's Spring Boot 4.0 baseline pins Spring
|
// Spring 6.2 / 7.0 compatibility lanes. This repository's Spring Boot 4.0 baseline pins Spring
|
||||||
// Framework 7, so the 6.2 lane verifies the *API surface* the common packages compile against
|
// Framework 7, so the 6.2 lane verifies the *API surface* the common packages compile against
|
||||||
@@ -213,42 +207,37 @@ strictTestLanes {
|
|||||||
}
|
}
|
||||||
// Its own source set rather than a tag, so the source set is the selection.
|
// Its own source set rather than a tag, so the source set is the selection.
|
||||||
lane('httpClientPerformanceTest') {
|
lane('httpClientPerformanceTest') {
|
||||||
|
performance()
|
||||||
sourceSet = 'httpClientPerformanceTest'
|
sourceSet = 'httpClientPerformanceTest'
|
||||||
description = 'Certifies pool, streaming, retry, and rotation resource bounds (design §28.8).'
|
description = 'Certifies pool, streaming, retry, and rotation resource bounds (design §28.8).'
|
||||||
customize = { test ->
|
systemProperty 'httpclient.contract.transports',
|
||||||
applyContractSelection(test)
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
test.systemProperty 'performance.assertions.enabled',
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
(project.findProperty('performance.assertions.enabled') ?: 'false').toString()
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
}
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
|
systemProperty 'performance.assertions.enabled',
|
||||||
|
providers.gradleProperty('performance.assertions.enabled').orElse('false').get()
|
||||||
}
|
}
|
||||||
lane('httpClientFailureInjectionTest') {
|
lane('httpClientFailureInjectionTest') {
|
||||||
|
integration()
|
||||||
tag = 'httpclient-fault'
|
tag = 'httpclient-fault'
|
||||||
description = 'Runs the Toxiproxy fault-injection suite; fails closed without Docker ' +
|
description = 'Runs the Toxiproxy fault-injection suite; fails closed without Docker ' +
|
||||||
'(design §28.3).'
|
'(design §28.3).'
|
||||||
customize = { test ->
|
systemProperty 'httpclient.contract.transports',
|
||||||
applyContractSelection(test)
|
providers.gradleProperty('httpclient.contract.transports').orElse('apache,jdk,reactor').get()
|
||||||
// The upstream image is mutable by default. A digest here is what makes a red fault run
|
systemProperty 'io.netty.leakDetection.level', 'paranoid'
|
||||||
// attributable to this repository rather than to someone else's image push — and the
|
systemProperty 'httpclient.http3.tests.enabled',
|
||||||
// default said `:latest`, which is the exact thing this comment forbade. The digest is
|
providers.gradleProperty('http3.tests.enabled').orElse('false').get()
|
||||||
// the registry manifest digest of the image the lane has been running.
|
// The upstream image is mutable by default. A digest here is what makes a red fault run
|
||||||
test.systemProperty 'httpclient.fault.httpbin.image',
|
// attributable to this repository rather than to someone else's image push.
|
||||||
(project.findProperty('httpclient.fault.httpbin.image')
|
systemProperty 'httpclient.fault.httpbin.image',
|
||||||
?: 'kennethreitz/httpbin@sha256:' +
|
providers.gradleProperty('httpclient.fault.httpbin.image').orElse(
|
||||||
'599fe5e5073102dbb0ee3dbb65f049dab44fa9fc251f6835c9990f8fb196a72b')
|
'kennethreitz/httpbin@sha256:' +
|
||||||
.toString()
|
'599fe5e5073102dbb0ee3dbb65f049dab44fa9fc251f6835c9990f8fb196a72b').get()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.register('jmh', JavaExec) {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Runs the JMH benchmarks for the blocking and reactive clients (design §28.8).'
|
|
||||||
mainClass = 'org.openjdk.jmh.Main'
|
|
||||||
classpath = sourceSets.jmh.runtimeClasspath
|
|
||||||
args '-rf', 'json', '-rff', layout.buildDirectory.file('reports/jmh/result.json').get().asFile.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
// Which lanes gate an ordinary build, and which do not.
|
// Which lanes gate an ordinary build, and which do not.
|
||||||
//
|
//
|
||||||
// The specialised lanes existed but hung off nothing: `check` ran only `test`, so the SSRF suite,
|
// The specialised lanes existed but hung off nothing: `check` ran only `test`, so the SSRF suite,
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
// groovy: compiles the UuidCodec Spock specs under src/test/groovy. See README.
|
|
||||||
id 'groovy'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':application-core')
|
implementation project(':application-core')
|
||||||
|
|
||||||
testImplementation libs.spock.core.groovy5
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pin UTF-8 so non-ASCII (Korean) Spock spec names build on any host. See README.
|
|
||||||
tasks.withType(GroovyCompile).configureEach {
|
|
||||||
groovyOptions.encoding = 'UTF-8'
|
|
||||||
options.encoding = 'UTF-8'
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ commons-logging:commons-logging:1.3.6=testCompileClasspath,testRuntimeClasspath
|
|||||||
info.picocli:picocli:4.7.7=checkstyle
|
info.picocli:picocli:4.7.7=checkstyle
|
||||||
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor
|
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor
|
||||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor
|
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor
|
||||||
io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath
|
|
||||||
io.micrometer:micrometer-commons:1.16.7=testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-commons:1.16.7=testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-observation:1.16.7=testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-observation:1.16.7=testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.activation:jakarta.activation-api:2.1.4=testCompileClasspath,testRuntimeClasspath
|
jakarta.activation:jakarta.activation-api:2.1.4=testCompileClasspath,testRuntimeClasspath
|
||||||
@@ -58,8 +57,6 @@ org.apache.bcel:bcel:6.12.0=spotbugs
|
|||||||
org.apache.commons:commons-lang3:3.20.0=checkstyle,spotbugs
|
org.apache.commons:commons-lang3:3.20.0=checkstyle,spotbugs
|
||||||
org.apache.commons:commons-text:1.15.0=spotbugs
|
org.apache.commons:commons-text:1.15.0=spotbugs
|
||||||
org.apache.commons:commons-text:1.3=checkstyle
|
org.apache.commons:commons-text:1.3=checkstyle
|
||||||
org.apache.groovy:groovy-bom:5.0.8=testCompileClasspath,testRuntimeClasspath
|
|
||||||
org.apache.groovy:groovy:5.0.8=testCompileClasspath,testRuntimeClasspath
|
|
||||||
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
||||||
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
||||||
org.apache.logging.log4j:log4j-api:2.25.5=spotbugs,testCompileClasspath,testRuntimeClasspath
|
org.apache.logging.log4j:log4j-api:2.25.5=spotbugs,testCompileClasspath,testRuntimeClasspath
|
||||||
@@ -89,7 +86,7 @@ org.junit.jupiter:junit-jupiter-engine:6.0.3=testRuntimeClasspath
|
|||||||
org.junit.jupiter:junit-jupiter-params:6.0.3=testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-params:6.0.3=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter:6.0.3=testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter:6.0.3=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-commons:6.0.3=testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-commons:6.0.3=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-engine:6.0.3=testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-engine:6.0.3=testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-launcher:6.0.3=testRuntimeClasspath
|
org.junit.platform:junit-platform-launcher:6.0.3=testRuntimeClasspath
|
||||||
org.junit:junit-bom:6.0.3=testCompileClasspath,testRuntimeClasspath
|
org.junit:junit-bom:6.0.3=testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit:junit-bom:6.1.0=spotbugs
|
org.junit:junit-bom:6.1.0=spotbugs
|
||||||
@@ -114,8 +111,6 @@ org.slf4j:jul-to-slf4j:2.0.18=testCompileClasspath,testRuntimeClasspath
|
|||||||
org.slf4j:slf4j-api:2.0.18=spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath
|
org.slf4j:slf4j-api:2.0.18=spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath
|
||||||
org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j
|
org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j
|
||||||
org.slf4j:slf4j-simple:2.0.18=checkstyle
|
org.slf4j:slf4j-simple:2.0.18=checkstyle
|
||||||
org.spockframework:spock-bom:2.4-groovy-5.0=testCompileClasspath,testRuntimeClasspath
|
|
||||||
org.spockframework:spock-core:2.4-groovy-5.0=testCompileClasspath,testRuntimeClasspath
|
|
||||||
org.springframework.boot:spring-boot-autoconfigure:4.0.8=testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-autoconfigure:4.0.8=testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-http-converter:4.0.8=testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-http-converter:4.0.8=testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jackson:4.0.8=testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jackson:4.0.8=testCompileClasspath,testRuntimeClasspath
|
||||||
|
|||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
package dev.caskeleton.adapter.outbound.identifier
|
|
||||||
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
class UuidCodecSpec extends Specification {
|
|
||||||
|
|
||||||
static final String CANONICAL = "0190bd6e-7c3e-7abc-8def-0123456789ab"
|
|
||||||
|
|
||||||
def "normalize 는 #label 을 36자 소문자 canonical 형태로 변환한다"() {
|
|
||||||
expect:
|
|
||||||
UuidCodec.normalize(input) == CANONICAL
|
|
||||||
|
|
||||||
where:
|
|
||||||
label | input
|
|
||||||
"이미 canonical 인 입력" | CANONICAL
|
|
||||||
"대문자 입력" | CANONICAL.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "normalize 는 null 입력에 대해 null 을 반환한다"() {
|
|
||||||
expect:
|
|
||||||
UuidCodec.normalize(null) == null
|
|
||||||
}
|
|
||||||
|
|
||||||
def "normalize 는 형식이 잘못된 UUID 를 거부한다"() {
|
|
||||||
when:
|
|
||||||
UuidCodec.normalize("not-a-uuid")
|
|
||||||
|
|
||||||
then:
|
|
||||||
thrown(IllegalArgumentException)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "UUID -> UUID -> UUID 왕복 변환은 무손실이다"() {
|
|
||||||
given:
|
|
||||||
def uuid = UuidCodec.toUuid(CANONICAL)
|
|
||||||
|
|
||||||
expect:
|
|
||||||
UuidCodec.fromUuid(uuid) == CANONICAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.identifier;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class UuidCodecTest {
|
||||||
|
private static final String CANONICAL = "0190bd6e-7c3e-7abc-8def-0123456789ab";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void normalizeProducesLowercaseCanonicalUuid() {
|
||||||
|
assertEquals(CANONICAL, UuidCodec.normalize(CANONICAL));
|
||||||
|
assertEquals(CANONICAL, UuidCodec.normalize(CANONICAL.toUpperCase(Locale.ROOT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void normalizeReturnsNullForNullInput() {
|
||||||
|
assertNull(UuidCodec.normalize(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void normalizeRejectsMalformedUuid() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> UuidCodec.normalize("not-a-uuid"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uuidRoundTripIsLossless() {
|
||||||
|
var uuid = UuidCodec.toUuid(CANONICAL);
|
||||||
|
assertEquals(CANONICAL, UuidCodec.fromUuid(uuid));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,13 @@ Package root: `dev.caskeleton.adapter.outbound.messaging`.
|
|||||||
- Allowed dependency edges come only from the module's
|
- Allowed dependency edges come only from the module's
|
||||||
`src/config/architecture/modules.json` entry.
|
`src/config/architecture/modules.json` entry.
|
||||||
- No inbound DTO/controller, persistence repository/entity, bootstrap, or sample dependency.
|
- No inbound DTO/controller, persistence repository/entity, bootstrap, or sample dependency.
|
||||||
|
- The canonical platform bridge may depend only on `messaging-core-api` and
|
||||||
|
`messaging-schema-api`; do not import `messaging-runtime-core`, transport SPI implementations, or
|
||||||
|
broker SDK types under `platformbridge/`.
|
||||||
|
- `KafkaSender` / `MessageBroker` are transitional legacy R0 seams for `OutboxEvent` and realtime
|
||||||
|
compatibility only. Do not route new canonical integration-event publication through them.
|
||||||
|
- Canonical producer identity is explicit `app.messaging.producer-id`; never derive it from
|
||||||
|
`spring.application.name`, host, pod, or process identity.
|
||||||
- Contract compilation accepts only explicitly supplied application SPI contributions; do not scan,
|
- Contract compilation accepts only explicitly supplied application SPI contributions; do not scan,
|
||||||
import sample payload classes, use `Class.forName`, or discover contracts from raw JSON/tree data.
|
import sample payload classes, use `Class.forName`, or discover contracts from raw JSON/tree data.
|
||||||
- Snapshot each contribution accessor exactly once. Compiled contracts/publication bindings have
|
- Snapshot each contribution accessor exactly once. Compiled contracts/publication bindings have
|
||||||
@@ -52,7 +59,7 @@ Package root: `dev.caskeleton.adapter.outbound.messaging`.
|
|||||||
mutation/concurrency, and enforce output bytes while the generator writes.
|
mutation/concurrency, and enforce output bytes while the generator writes.
|
||||||
- Keep the nine exact checked-in Draft 2020-12 schema/meta bytes and digests synchronized. Startup
|
- Keep the nine exact checked-in Draft 2020-12 schema/meta bytes and digests synchronized. Startup
|
||||||
compares those bytes, IDs, and NetworkNT runtime schema trees without assuming the validator's
|
compares those bytes, IDs, and NetworkNT runtime schema trees without assuming the validator's
|
||||||
`CodeSource` is a regular JAR. Strict dependency locks and `verifyJsonSchemaRuntimeGraph` own the
|
`CodeSource` is a regular JAR. Strict dependency locks and `verifyDependencyPolicy` own the
|
||||||
NetworkNT 3.0.2 artifact provenance.
|
NetworkNT 3.0.2 artifact provenance.
|
||||||
- Permit only lowercase exact `urn` root `$id` and absolute `$ref` schemes. Reject every
|
- Permit only lowercase exact `urn` root `$id` and absolute `$ref` schemes. Reject every
|
||||||
root-external nested `$id` key regardless of value type, and reject `$dynamicRef`,
|
root-external nested `$id` key regardless of value type, and reject `$dynamicRef`,
|
||||||
|
|||||||
@@ -11,15 +11,32 @@ correlation / fail-open 의존성 로깅을 일반 publisher에서 재사용한
|
|||||||
|
|
||||||
## 모듈 개요
|
## 모듈 개요
|
||||||
|
|
||||||
application-core 포트(`MessagePublisher` / `OutboxMessagePublishPort`) 뒤에 두는 **선택형**
|
application-core의 legacy 포트(`MessagePublisher` / `OutboxMessagePublishPort`)와 canonical
|
||||||
연동 어댑터다. `@ConditionalOnProperty` 로 게이팅되고 기본 비활성이며, 비활성 바인딩은
|
`IntegrationEventPublishPort` 뒤에 두는 **선택형** 연동 어댑터다. `@ConditionalOnProperty` 로
|
||||||
`Disabled*` 구현으로 fail-fast 한다(Layer 3). 무거운 broker SDK 는 의도적으로 classpath 에
|
게이팅되고 기본 비활성이며, legacy 비활성 바인딩은 `Disabled*` 구현으로 fail-fast 한다(Layer 3).
|
||||||
최소화하고, 실제 broker client(`KafkaSender`)는 포킹 프로젝트가 채우는 seam 이다.
|
|
||||||
|
|
||||||
## 두 포트를 하나의 활성 broker 에 조립
|
canonical path는 `platformbridge/PlatformIntegrationEventPublishAdapter`가
|
||||||
|
`EncodedMessagePublisher`에 위임한다. 이 path는 broker SDK나 transport SPI를 직접 보지 않는다.
|
||||||
|
`KafkaSender`/`MessageBroker`는 metadata가 부족한 legacy `OutboxEvent`/realtime 경로의 transitional
|
||||||
|
R0 seam으로만 남아 있고, legacy storage/relay cutover와 함께 제거한다.
|
||||||
|
|
||||||
`MessagingConfig` 는 두 messaging 포트를 단일 활성 `MessageBroker` 위에 조립한다 — broker
|
## Legacy 두 포트와 canonical platform bridge
|
||||||
추가는 새 broker 구현 파일 추가만으로 끝나고 이 config 는 바뀌지 않는다.
|
|
||||||
|
`MessagingConfig`는 legacy 두 messaging 포트를 단일 활성 `MessageBroker` 위에 조립한다. 이 경로는
|
||||||
|
기존 `OutboxEvent`/realtime compatibility를 위해 남아 있다.
|
||||||
|
|
||||||
|
canonical `IntegrationEventPublishPort`는 별도 경로다.
|
||||||
|
|
||||||
|
`IntegrationEventPublishPort → PlatformIntegrationEventPublishAdapter → EncodedMessagePublisher`
|
||||||
|
|
||||||
|
`app.messaging.producer-id`가 명시되고 platform publisher bean이 있을 때만 생성된다. exact
|
||||||
|
`ValidatedIntegrationEvent.envelopeBytes()`를 보존하고 platform의 중앙 publish pipeline을 탄다.
|
||||||
|
|
||||||
|
MSG-015 transport-only 단계에서는 legacy relay authority도 이 bridge를 사용할 수 있다.
|
||||||
|
`OutboxMessagePublishAdapter`가 claim subtype으로 경로를 분리해서 legacy `OutboxEvent`는 기존
|
||||||
|
`MessageBroker`, `CanonicalClaimedOutboxEvent`는 `IntegrationEventPublishPort`만 호출한다. 한 row가 두
|
||||||
|
transport로 fallback/dual-publish되지 않는다. mixed-row 기간에는 old backlog를 보존하기 위해 legacy
|
||||||
|
broker requirement도 유지한다.
|
||||||
|
|
||||||
## broker 선택 검증
|
## broker 선택 검증
|
||||||
|
|
||||||
@@ -136,7 +153,7 @@ Draft 2020-12 authority는 `draft/2020-12/schema` 1개와 `meta/*` 8개의 exact
|
|||||||
digest로 pin한다. registry startup은 9개 digest, 각 `$id`, NetworkNT runtime schema tree를
|
digest로 pin한다. registry startup은 9개 digest, 각 `$id`, NetworkNT runtime schema tree를
|
||||||
대조하고 하나라도 다르면 fail-closed 한다. Spring Boot executable/fat/nested JAR 배치를 깨뜨리는
|
대조하고 하나라도 다르면 fail-closed 한다. Spring Boot executable/fat/nested JAR 배치를 깨뜨리는
|
||||||
`CodeSource` regular-file/JAR 가정은 하지 않는다. NetworkNT 3.0.2 artifact provenance는 strict
|
`CodeSource` regular-file/JAR 가정은 하지 않는다. NetworkNT 3.0.2 artifact provenance는 strict
|
||||||
Gradle dependency lock과 `verifyJsonSchemaRuntimeGraph`가 담당한다. 이 검증은 business schema
|
Gradle dependency lock과 `verifyDependencyPolicy`가 담당한다. 이 검증은 business schema
|
||||||
registry나 runtime remote resolution 경로를 넓히지 않는다.
|
registry나 runtime remote resolution 경로를 넓히지 않는다.
|
||||||
|
|
||||||
공통 build evidence manifest는 수동 구조 검사만으로 PASS하지 않는다. test/build 전용
|
공통 build evidence manifest는 수동 구조 검사만으로 PASS하지 않는다. test/build 전용
|
||||||
@@ -153,7 +170,8 @@ Suite/Bowtie 전체 호환이나 hostile regex 시간 상한 증명이 아니며
|
|||||||
`regex-engine-timeout`, `consumer-compatibility-full-suite` unsupported claim으로 남는다. 테스트
|
`regex-engine-timeout`, `consumer-compatibility-full-suite` unsupported claim으로 남는다. 테스트
|
||||||
runtime은 remote corpus를 내려받지 않는다.
|
runtime은 remote corpus를 내려받지 않는다.
|
||||||
|
|
||||||
이 Task의 acceptance는 **deterministic local wire contract candidate**다. encoder/catalog는 기존
|
deterministic local wire contract는 이제 canonical platform bridge의 입력으로 사용할 수 있다.
|
||||||
`MessagingConfig`, runtime append, `KafkaSender` 또는 legacy broker selection에 연결하지 않았다.
|
다만 production legacy relay가 아직 `OutboxEvent`를 읽기 때문에 R2 durable outbox storage/relay
|
||||||
Kafka ACK, durable outbox R2, external topic attestation은 아직 구현되지 않았고 R0 runtime authority와
|
cutover는 남아 있다. `KafkaSender`/legacy broker selection은 그 R0 compatibility path에서만
|
||||||
동작은 그대로다.
|
authority를 유지한다. canonical path는 platform `PublishResult`의 confirmation/transmission evidence를
|
||||||
|
application `OutboxPublishOutcome`으로 명시적으로 변환한다.
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ dependencies {
|
|||||||
implementation project(':application-core')
|
implementation project(':application-core')
|
||||||
implementation project(':shared-contract')
|
implementation project(':shared-contract')
|
||||||
implementation project(':adapter:outbound:support')
|
implementation project(':adapter:outbound:support')
|
||||||
|
implementation project(':messaging:messaging-core-api')
|
||||||
|
implementation project(':messaging:messaging-schema-api')
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-autoconfigure'
|
implementation 'org.springframework.boot:spring-boot-autoconfigure'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-json'
|
implementation 'org.springframework.boot:spring-boot-starter-json'
|
||||||
|
implementation 'org.apache.kafka:kafka-clients'
|
||||||
implementation(libs.json.schema.validator) {
|
implementation(libs.json.schema.validator) {
|
||||||
exclude group: 'com.fasterxml.jackson.dataformat', module: 'jackson-dataformat-yaml'
|
exclude group: 'com.fasterxml.jackson.dataformat', module: 'jackson-dataformat-yaml'
|
||||||
}
|
}
|
||||||
@@ -28,106 +31,56 @@ configurations.configureEach {
|
|||||||
exclude group: 'org.snakeyaml', module: 'snakeyaml-engine'
|
exclude group: 'org.snakeyaml', module: 'snakeyaml-engine'
|
||||||
}
|
}
|
||||||
|
|
||||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
// The runtime graph policy is declarative; ca.dependency-policy owns resolution and verification.
|
||||||
import org.gradle.api.artifacts.ModuleIdentifier
|
// Module presence is version-agnostic because dependency locking/BOMs own version selection.
|
||||||
|
dependencyPolicy {
|
||||||
|
absentMatching '(?i).*yaml.*',
|
||||||
|
'Messaging JSON runtime is intentionally YAML-free.'
|
||||||
|
absentMatching 'com\\.fasterxml\\.jackson\\.core:jackson-(?:core|databind)',
|
||||||
|
'Jackson 2 core/databind must not enter the Jackson 3 messaging runtime.'
|
||||||
|
required libs.json.schema.validator,
|
||||||
|
'NetworkNT is the runtime JSON Schema engine.'
|
||||||
|
required libs.jackson3.core,
|
||||||
|
'Jackson 3 core is part of the closed JSON runtime graph.'
|
||||||
|
required libs.jackson3.databind,
|
||||||
|
'Jackson 3 databind is part of the closed JSON runtime graph.'
|
||||||
|
}
|
||||||
|
|
||||||
tasks.register('verifyJsonSchemaRuntimeGraph') {
|
def messagingCompiledContractsQualification = extensions.getByName('strictQualification').register(
|
||||||
group = 'verification'
|
'messagingCompiledContractsQualificationTest',
|
||||||
description = 'Verifies the closed Jackson 3 / NetworkNT graph contains no YAML or Jackson 2 runtime.'
|
sourceSets.test,
|
||||||
doLast {
|
|
||||||
Set<String> modules = configurations.runtimeClasspath.incoming.resolutionResult
|
|
||||||
.allComponents
|
|
||||||
.findAll { it.moduleVersion != null }
|
|
||||||
.collect {
|
|
||||||
"${it.moduleVersion.group}:${it.moduleVersion.name}:${it.moduleVersion.version}"
|
|
||||||
.toString()
|
|
||||||
} as Set
|
|
||||||
List<String> forbidden = modules.findAll { String coordinate ->
|
|
||||||
String lowered = coordinate.toLowerCase(Locale.ROOT)
|
|
||||||
lowered.contains('yaml') ||
|
|
||||||
lowered.startsWith('org.yaml:') ||
|
|
||||||
lowered.startsWith('org.snakeyaml:') ||
|
|
||||||
lowered ==~ /com\.fasterxml\.jackson\.core:jackson-(core|databind):.*/
|
|
||||||
}.sort()
|
|
||||||
if (!forbidden.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
"Messaging JSON runtime contains forbidden Jackson 2/YAML modules: ${forbidden}")
|
|
||||||
}
|
|
||||||
// The catalog accessors are Providers of a dependency, not coordinate strings.
|
|
||||||
//
|
|
||||||
// This block read `.each { String required -> ... }` over them, so Groovy tried to call the
|
|
||||||
// closure with a TransformBackedProvider and the task threw
|
|
||||||
// `No signature of method: doCall() ... (TransformBackedProvider)` before comparing
|
|
||||||
// anything. It had never passed: the forbidden-module half above ran first and found
|
|
||||||
// nothing, and then this half failed on its own argument types. `check` reached it, but
|
|
||||||
// only ever after some earlier failure had already stopped the build.
|
|
||||||
[
|
[
|
||||||
libs.json.schema.validator,
|
|
||||||
libs.jackson3.core,
|
|
||||||
libs.jackson3.databind
|
|
||||||
].collect { Provider<MinimalExternalModuleDependency> accessor ->
|
|
||||||
ModuleIdentifier module = accessor.get().module
|
|
||||||
"${module.group}:${module.name}".toString()
|
|
||||||
}.each { String requiredModule ->
|
|
||||||
if (!modules.any { it.startsWith(requiredModule + ':') }) {
|
|
||||||
throw new GradleException(
|
|
||||||
"Messaging JSON runtime is missing required module ${requiredModule}; " +
|
|
||||||
"resolved runtime modules are ${modules.toSorted()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Module, not module-and-version.
|
|
||||||
//
|
|
||||||
// The first working version of this compared the full `group:name:version` string from the
|
|
||||||
// catalog against the resolved graph, and the gate failed on its first real run: the
|
|
||||||
// catalog pins tools.jackson.core:jackson-core 3.0.2 while the Jackson 3 BOM resolves
|
|
||||||
// 3.1.5. That is not drift — it is dependency management doing its job, and this task is
|
|
||||||
// not the place that decides versions (gradle.lockfile is). What this task owns is the
|
|
||||||
// shape of the runtime graph: the Jackson 3 + NetworkNT engine present, no YAML engine, no
|
|
||||||
// Jackson 2 databind. Pinning the version here would have made a BOM patch bump a build
|
|
||||||
// failure in a leaf that never asked for the version.
|
|
||||||
|
|
||||||
// Jackson 3 intentionally retains the 2.x-namespace annotations artifact. It is not a
|
|
||||||
// Jackson 2 databind/runtime engine and is part of the official Jackson 3 BOM graph.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('check') {
|
|
||||||
dependsOn tasks.named('verifyJsonSchemaRuntimeGraph')
|
|
||||||
}
|
|
||||||
|
|
||||||
def messagingCompiledContractsQualification = registerStrictQualificationTest(
|
|
||||||
name: 'messagingCompiledContractsQualificationTest',
|
|
||||||
sourceSet: sourceSets.test,
|
|
||||||
requiredClasses: [
|
|
||||||
'dev.caskeleton.adapter.outbound.messaging.config.MessagingCapabilityCardRegistryTest',
|
'dev.caskeleton.adapter.outbound.messaging.config.MessagingCapabilityCardRegistryTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.contract.ContractCatalogCompilerTest',
|
'dev.caskeleton.adapter.outbound.messaging.contract.ContractCatalogCompilerTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.contract.ContractCatalogDigestTest',
|
'dev.caskeleton.adapter.outbound.messaging.contract.ContractCatalogDigestTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.destination.DestinationBindingCompilerTest',
|
'dev.caskeleton.adapter.outbound.messaging.destination.DestinationBindingCompilerTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.destination.PartitionKeyV1Test'
|
'dev.caskeleton.adapter.outbound.messaging.destination.PartitionKeyV1Test'
|
||||||
],
|
],
|
||||||
junitXmlOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence/compiled'),
|
'test-results/messaging-evidence/compiled'),
|
||||||
binaryResultsOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence-binary/compiled'),
|
'test-results/messaging-evidence-binary/compiled'),
|
||||||
description: 'Runs exact Messaging compiled-contract qualification tests.')
|
'Runs exact Messaging compiled-contract qualification tests.'
|
||||||
|
)
|
||||||
messagingCompiledContractsQualification.configure {
|
messagingCompiledContractsQualification.configure {
|
||||||
dependsOn ':prepareMessagingContractEvidence'
|
dependsOn ':prepareMessagingContractEvidence'
|
||||||
}
|
}
|
||||||
|
|
||||||
def messagingJsonSchemaV1Qualification = registerStrictQualificationTest(
|
def messagingJsonSchemaV1Qualification = extensions.getByName('strictQualification').register(
|
||||||
name: 'messagingJsonSchemaV1QualificationTest',
|
'messagingJsonSchemaV1QualificationTest',
|
||||||
sourceSet: sourceSets.test,
|
sourceSets.test,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.outbound.messaging.envelope.LocalJsonSchemaRegistryTest',
|
'dev.caskeleton.adapter.outbound.messaging.envelope.LocalJsonSchemaRegistryTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.envelope.JsonSchemaIntegrationEventEncoderTest',
|
'dev.caskeleton.adapter.outbound.messaging.envelope.JsonSchemaIntegrationEventEncoderTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.envelope.EnvelopeAdversarialCorpusTest',
|
'dev.caskeleton.adapter.outbound.messaging.envelope.EnvelopeAdversarialCorpusTest',
|
||||||
'dev.caskeleton.adapter.outbound.messaging.qualification.MessagingEvidenceManifestSchemaValidatorTest'
|
'dev.caskeleton.adapter.outbound.messaging.qualification.MessagingEvidenceManifestSchemaValidatorTest'
|
||||||
],
|
],
|
||||||
junitXmlOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence/json-schema'),
|
'test-results/messaging-evidence/json-schema'),
|
||||||
binaryResultsOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence-binary/json-schema'),
|
'test-results/messaging-evidence-binary/json-schema'),
|
||||||
description: 'Runs exact Messaging JSON Schema v1 qualification tests.')
|
'Runs exact Messaging JSON Schema v1 qualification tests.'
|
||||||
|
)
|
||||||
messagingJsonSchemaV1Qualification.configure {
|
messagingJsonSchemaV1Qualification.configure {
|
||||||
dependsOn ':prepareMessagingContractEvidence'
|
dependsOn ':prepareMessagingContractEvidence'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# This is a Gradle generated file for dependency locking.
|
# This is a Gradle generated file for dependency locking.
|
||||||
# Manual edits can break the build and are not advised.
|
# Manual edits can break the build and are not advised.
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
|
at.yawk.lz4:lz4-java:1.10.1=testRuntimeClasspath
|
||||||
biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=compileClasspath,testCompileClasspath
|
biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=compileClasspath,testCompileClasspath
|
||||||
ch.qos.logback:logback-classic:1.5.38=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
ch.qos.logback:logback-classic:1.5.38=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
ch.qos.logback:logback-core:1.5.38=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
ch.qos.logback:logback-core:1.5.38=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
@@ -8,6 +9,7 @@ com.ethlo.time:itu:1.14.0=compileClasspath,runtimeClasspath,testCompileClasspath
|
|||||||
com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,testAnnotationProcessor
|
com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,testAnnotationProcessor
|
||||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,testAnnotationProcessor
|
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,testAnnotationProcessor
|
||||||
|
com.github.luben:zstd-jni:1.5.6-10=testRuntimeClasspath
|
||||||
com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs
|
com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs
|
||||||
com.github.spotbugs:spotbugs:4.10.2=spotbugs
|
com.github.spotbugs:spotbugs:4.10.2=spotbugs
|
||||||
com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs
|
com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs
|
||||||
@@ -61,6 +63,7 @@ org.apache.commons:commons-text:1.15.0=spotbugs
|
|||||||
org.apache.commons:commons-text:1.3=checkstyle
|
org.apache.commons:commons-text:1.3=checkstyle
|
||||||
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
||||||
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
||||||
|
org.apache.kafka:kafka-clients:4.1.2=compileClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.logging.log4j:log4j-api:2.25.5=compileClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
|
org.apache.logging.log4j:log4j-api:2.25.5=compileClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.logging.log4j:log4j-core:2.25.5=spotbugs
|
org.apache.logging.log4j:log4j-core:2.25.5=spotbugs
|
||||||
org.apache.logging.log4j:log4j-to-slf4j:2.25.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.logging.log4j:log4j-to-slf4j:2.25.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
@@ -144,6 +147,7 @@ org.springframework:spring-expression:7.0.9=compileClasspath,runtimeClasspath,te
|
|||||||
org.springframework:spring-test:7.0.9=testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-test:7.0.9=testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-web:7.0.9=testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-web:7.0.9=testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-webmvc:7.0.9=testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-webmvc:7.0.9=testCompileClasspath,testRuntimeClasspath
|
||||||
|
org.xerial.snappy:snappy-java:1.1.10.7=testRuntimeClasspath
|
||||||
org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs
|
org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs
|
||||||
org.xmlunit:xmlunit-core:2.10.4=testCompileClasspath,testRuntimeClasspath
|
org.xmlunit:xmlunit-core:2.10.4=testCompileClasspath,testRuntimeClasspath
|
||||||
tools.jackson.core:jackson-core:3.1.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
tools.jackson.core:jackson-core:3.1.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
|
|||||||
+15
-4
@@ -8,9 +8,11 @@ import dev.caskeleton.adapter.outbound.messaging.outbox.DisabledOutboxMessagePub
|
|||||||
import dev.caskeleton.adapter.outbound.messaging.outbox.OutboxMessagePublishAdapter;
|
import dev.caskeleton.adapter.outbound.messaging.outbox.OutboxMessagePublishAdapter;
|
||||||
import dev.caskeleton.adapter.outbound.messaging.outbox.Slf4jOutboxRelayFailureReportAdapter;
|
import dev.caskeleton.adapter.outbound.messaging.outbox.Slf4jOutboxRelayFailureReportAdapter;
|
||||||
import dev.caskeleton.adapter.outbound.support.FailOpenDependencyLogger;
|
import dev.caskeleton.adapter.outbound.support.FailOpenDependencyLogger;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
||||||
import dev.caskeleton.application.outbox.OutboxRelayFailureReportPort;
|
import dev.caskeleton.application.outbox.OutboxRelayFailureReportPort;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -39,11 +41,20 @@ public class MessagingConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public OutboxMessagePublishPort outboxMessagePublishPort(
|
public OutboxMessagePublishPort outboxMessagePublishPort(
|
||||||
ObjectProvider<MessageBroker> brokerProvider, MessagingSettings settings) {
|
ObjectProvider<MessageBroker> brokerProvider,
|
||||||
|
ObjectProvider<IntegrationEventPublishPort> canonicalPublisherProvider,
|
||||||
|
MessagingSettings settings,
|
||||||
|
@Value("${ca-skeleton.outbox.canonical-transport-enabled:false}")
|
||||||
|
boolean canonicalTransportEnabled) {
|
||||||
MessageBroker active = resolveBroker(brokerProvider, settings);
|
MessageBroker active = resolveBroker(brokerProvider, settings);
|
||||||
return (active == null)
|
IntegrationEventPublishPort canonicalPublisher = canonicalPublisherProvider.getIfAvailable();
|
||||||
? new DisabledOutboxMessagePublisher()
|
if (active == null && canonicalPublisher == null) {
|
||||||
: new OutboxMessagePublishAdapter(active);
|
return new DisabledOutboxMessagePublisher();
|
||||||
|
}
|
||||||
|
return new OutboxMessagePublishAdapter(
|
||||||
|
java.util.Optional.ofNullable(active),
|
||||||
|
java.util.Optional.ofNullable(canonicalPublisher),
|
||||||
|
canonicalTransportEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Always available, including when broker publication is disabled. */
|
/** Always available, including when broker publication is disabled. */
|
||||||
|
|||||||
+6
-3
@@ -8,13 +8,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
* {@link MessageBroker} (e.g. {@code kafka}); unset/blank = no broker = fail-fast on use (the
|
* {@link MessageBroker} (e.g. {@code kafka}); unset/blank = no broker = fail-fast on use (the
|
||||||
* disabled sentinels {@code DisabledMessagePublisher} / {@code DisabledOutboxMessagePublisher}).
|
* disabled sentinels {@code DisabledMessagePublisher} / {@code DisabledOutboxMessagePublisher}).
|
||||||
*
|
*
|
||||||
* @param broker the active broker id, matched against {@link MessageBroker#brokerId()}; blank means
|
* @param broker the active legacy broker id, matched against {@link MessageBroker#brokerId()};
|
||||||
* the messaging template is disabled (the default)
|
* blank means the legacy broker template is disabled
|
||||||
|
* @param producerId the explicit logical producing-service identity used by the canonical platform
|
||||||
|
* bridge; blank means that bridge is not exposed
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "app.messaging")
|
@ConfigurationProperties(prefix = "app.messaging")
|
||||||
public record MessagingSettings(String broker) {
|
public record MessagingSettings(String broker, String producerId) {
|
||||||
|
|
||||||
public MessagingSettings {
|
public MessagingSettings {
|
||||||
broker = (broker == null) ? "" : broker.trim();
|
broker = (broker == null) ? "" : broker.trim();
|
||||||
|
producerId = (producerId == null) ? "" : producerId.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-2
@@ -3,9 +3,16 @@ package dev.caskeleton.adapter.outbound.messaging.autoconfigure;
|
|||||||
import dev.caskeleton.adapter.outbound.messaging.MessagingConfig;
|
import dev.caskeleton.adapter.outbound.messaging.MessagingConfig;
|
||||||
import dev.caskeleton.adapter.outbound.messaging.MessagingSettings;
|
import dev.caskeleton.adapter.outbound.messaging.MessagingSettings;
|
||||||
import dev.caskeleton.adapter.outbound.messaging.kafka.KafkaAdapterConfig;
|
import dev.caskeleton.adapter.outbound.messaging.kafka.KafkaAdapterConfig;
|
||||||
|
import dev.caskeleton.adapter.outbound.messaging.kafka.KafkaSenderAutoConfiguration;
|
||||||
|
import dev.caskeleton.adapter.outbound.messaging.platformbridge.PlatformIntegrationEventPublishAdapter;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessagePublisher;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,5 +27,22 @@ import org.springframework.context.annotation.Import;
|
|||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ConditionalOnProperty(prefix = "app.messaging", name = "enabled", havingValue = "true")
|
@ConditionalOnProperty(prefix = "app.messaging", name = "enabled", havingValue = "true")
|
||||||
@EnableConfigurationProperties(MessagingSettings.class)
|
@EnableConfigurationProperties(MessagingSettings.class)
|
||||||
@Import({MessagingConfig.class, KafkaAdapterConfig.class})
|
@Import({MessagingConfig.class, KafkaSenderAutoConfiguration.class, KafkaAdapterConfig.class})
|
||||||
public class MessagingBridgeRootAutoConfiguration {}
|
public class MessagingBridgeRootAutoConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects canonical application integration events to the central messaging platform.
|
||||||
|
*
|
||||||
|
* <p>Producer identity is explicit configuration. The bridge is not created when that identity is
|
||||||
|
* absent, because guessing from a process name or deployment name would make the wire producer
|
||||||
|
* identity environment-dependent.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(EncodedMessagePublisher.class)
|
||||||
|
@ConditionalOnMissingBean(IntegrationEventPublishPort.class)
|
||||||
|
@ConditionalOnProperty(prefix = "app.messaging", name = "producer-id")
|
||||||
|
public IntegrationEventPublishPort integrationEventPublishPort(
|
||||||
|
EncodedMessagePublisher publisher, MessagingSettings settings) {
|
||||||
|
return new PlatformIntegrationEventPublishAdapter(publisher, settings.producerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+7
-8
@@ -1,7 +1,5 @@
|
|||||||
package dev.caskeleton.bootstrap.messaging;
|
package dev.caskeleton.adapter.outbound.messaging.kafka;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.messaging.kafka.KafkaAdapterSettings;
|
|
||||||
import dev.caskeleton.adapter.outbound.messaging.kafka.KafkaSender;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -18,7 +16,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The real broker bridge behind {@link KafkaSender} (MSG-INT-003).
|
* The adapter-owned broker bridge behind {@link KafkaSender} (MSG-INT-003).
|
||||||
*
|
*
|
||||||
* <p>{@code KafkaSender} is documented as an integration seam for a forking project, and the
|
* <p>{@code KafkaSender} is documented as an integration seam for a forking project, and the
|
||||||
* skeleton supplied no implementation of it — only a test fake. So selecting {@code
|
* skeleton supplied no implementation of it — only a test fake. So selecting {@code
|
||||||
@@ -26,9 +24,10 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
* messaging capability had no way to reach a broker in any deployment. A seam with no production
|
* messaging capability had no way to reach a broker in any deployment. A seam with no production
|
||||||
* implementation anywhere is indistinguishable from an unimplemented feature.
|
* implementation anywhere is indistinguishable from an unimplemented feature.
|
||||||
*
|
*
|
||||||
* <p>It stays a seam. The bean backs off entirely to anything a forking project defines, and it is
|
* <p>It stays a seam and is owned by the outbound messaging adapter. The bean backs off entirely to
|
||||||
* registered only when Kafka is the selected broker — so a deployment with messaging off, or on a
|
* anything a forking project defines, and it is registered only when Kafka is the selected broker —
|
||||||
* different broker, carries no producer, no connection and no background sender thread.
|
* so a deployment with messaging off, or on a different broker, carries no producer, no connection
|
||||||
|
* and no background sender thread.
|
||||||
*
|
*
|
||||||
* <p>{@code kafka-clients} rather than {@code spring-kafka}: the seam takes one already-serialized
|
* <p>{@code kafka-clients} rather than {@code spring-kafka}: the seam takes one already-serialized
|
||||||
* message and returns nothing, so a listener container, a converter stack and a template would all
|
* message and returns nothing, so a listener container, a converter stack and a template would all
|
||||||
@@ -36,7 +35,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnProperty(name = "app.messaging.broker", havingValue = "kafka")
|
@ConditionalOnProperty(name = "app.messaging.broker", havingValue = "kafka")
|
||||||
public class KafkaSenderConfig {
|
public class KafkaSenderAutoConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The producer, configured so a send that returns has actually been accepted.
|
* The producer, configured so a send that returns has actually been accepted.
|
||||||
+3
-9
@@ -1,20 +1,14 @@
|
|||||||
package dev.caskeleton.adapter.outbound.messaging.outbox;
|
package dev.caskeleton.adapter.outbound.messaging.outbox;
|
||||||
|
|
||||||
import dev.caskeleton.application.outbox.OutboxEvent;
|
import dev.caskeleton.application.outbox.ClaimedOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
||||||
import dev.caskeleton.shared.error.AdapterDisabledException;
|
import dev.caskeleton.shared.error.AdapterDisabledException;
|
||||||
|
|
||||||
/**
|
/** Fail-fast outbox binding when neither legacy broker nor canonical platform transport exists. */
|
||||||
* Fail-fast {@link OutboxMessagePublishPort} binding when no broker is active ({@code
|
|
||||||
* app.messaging.broker} unset — the default). Any publish throws {@link AdapterDisabledException} —
|
|
||||||
* never a silent no-op. Kept separate from {@code DisabledMessagePublisher} so each disabled bean
|
|
||||||
* implements exactly one port (a single class implementing both makes {@code
|
|
||||||
* getBean(MessagePublisher.class)} ambiguous).
|
|
||||||
*/
|
|
||||||
public class DisabledOutboxMessagePublisher implements OutboxMessagePublishPort {
|
public class DisabledOutboxMessagePublisher implements OutboxMessagePublishPort {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(OutboxEvent event) {
|
public void publish(ClaimedOutboxEvent event) {
|
||||||
throw new AdapterDisabledException("messaging");
|
throw new AdapterDisabledException("messaging");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+77
-17
@@ -2,40 +2,100 @@ package dev.caskeleton.adapter.outbound.messaging.outbox;
|
|||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.messaging.core.MessageBroker;
|
import dev.caskeleton.adapter.outbound.messaging.core.MessageBroker;
|
||||||
import dev.caskeleton.adapter.outbound.messaging.core.OutboundMessage;
|
import dev.caskeleton.adapter.outbound.messaging.core.OutboundMessage;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.outbox.CanonicalClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.ClaimedOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxEvent;
|
import dev.caskeleton.application.outbox.OutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishAmbiguousException;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishRefusedException;
|
||||||
|
import dev.caskeleton.shared.error.AdapterDisabledException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outbox {@link OutboxMessagePublishPort} binding (fail-closed). Maps the claimed {@link
|
* Transport-only cutover router for the legacy outbox relay authority.
|
||||||
* OutboxEvent} to an {@link OutboundMessage} and delegates to the active {@link MessageBroker}.
|
|
||||||
* Runtime failures propagate unchanged and checked failures are wrapped with their cause so the
|
|
||||||
* relay can drive the FAILED/DEAD transition. This adapter emits no dependency log; the confirmed
|
|
||||||
* transition has one canonical ERROR reporter.
|
|
||||||
*
|
*
|
||||||
* <p>Broker-agnostic: the same decorator serves any {@link MessageBroker}, so adding a broker never
|
* <p>Legacy rows are rendered through the existing {@link MessageBroker} path. Canonical rows carry
|
||||||
* touches this class.
|
* an already validated event and are delegated unchanged to {@link IntegrationEventPublishPort}. A
|
||||||
|
* row never falls back across branches when its required transport is absent.
|
||||||
*/
|
*/
|
||||||
public class OutboxMessagePublishAdapter implements OutboxMessagePublishPort {
|
public final class OutboxMessagePublishAdapter implements OutboxMessagePublishPort {
|
||||||
|
|
||||||
private final MessageBroker broker;
|
private final Optional<MessageBroker> broker;
|
||||||
|
private final Optional<IntegrationEventPublishPort> canonicalPublisher;
|
||||||
|
private final boolean canonicalTransportEnabled;
|
||||||
|
|
||||||
public OutboxMessagePublishAdapter(MessageBroker broker) {
|
public OutboxMessagePublishAdapter(MessageBroker broker) {
|
||||||
this.broker = Objects.requireNonNull(broker, "broker must not be null");
|
this(
|
||||||
|
Optional.of(Objects.requireNonNull(broker, "broker must not be null")),
|
||||||
|
Optional.empty(),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutboxMessagePublishAdapter(
|
||||||
|
Optional<MessageBroker> broker, Optional<IntegrationEventPublishPort> canonicalPublisher) {
|
||||||
|
this(broker, canonicalPublisher, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutboxMessagePublishAdapter(
|
||||||
|
Optional<MessageBroker> broker,
|
||||||
|
Optional<IntegrationEventPublishPort> canonicalPublisher,
|
||||||
|
boolean canonicalTransportEnabled) {
|
||||||
|
this.broker = Objects.requireNonNull(broker, "broker Optional must not be null");
|
||||||
|
this.canonicalPublisher =
|
||||||
|
Objects.requireNonNull(canonicalPublisher, "canonicalPublisher Optional must not be null");
|
||||||
|
this.canonicalTransportEnabled = canonicalTransportEnabled;
|
||||||
|
if (broker.isEmpty() && canonicalPublisher.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"at least one outbox publication transport must be present");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(OutboxEvent event) {
|
public void publish(ClaimedOutboxEvent event) {
|
||||||
|
OutboxPublishOutcome outcome = publishForOutcome(event);
|
||||||
|
if (outcome == OutboxPublishOutcome.CONFIRMED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (outcome == OutboxPublishOutcome.AMBIGUOUS) {
|
||||||
|
throw new OutboxPublishAmbiguousException();
|
||||||
|
}
|
||||||
|
throw new OutboxPublishRefusedException(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutboxPublishOutcome publishForOutcome(ClaimedOutboxEvent event) {
|
||||||
|
Objects.requireNonNull(event, "event must not be null");
|
||||||
|
if (event instanceof CanonicalClaimedOutboxEvent canonical) {
|
||||||
|
if (!canonicalTransportEnabled) {
|
||||||
|
throw new AdapterDisabledException("messaging canonical platform transport");
|
||||||
|
}
|
||||||
|
IntegrationEventPublishPort publisher =
|
||||||
|
canonicalPublisher.orElseThrow(
|
||||||
|
() -> new AdapterDisabledException("messaging canonical platform"));
|
||||||
|
return publisher.publish(canonical.event()).toCompletableFuture().join();
|
||||||
|
}
|
||||||
|
if (event instanceof OutboxEvent legacy) {
|
||||||
|
publishLegacy(legacy);
|
||||||
|
return OutboxPublishOutcome.CONFIRMED;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("unsupported claimed outbox event type: " + event.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishLegacy(OutboxEvent event) {
|
||||||
|
MessageBroker activeBroker =
|
||||||
|
broker.orElseThrow(() -> new AdapterDisabledException("messaging legacy broker"));
|
||||||
String envelope = OutboxEnvelopeJson.toJson(event);
|
String envelope = OutboxEnvelopeJson.toJson(event);
|
||||||
OutboundMessage message = new OutboundMessage(event.eventType(), event.aggregateId(), envelope);
|
OutboundMessage message = new OutboundMessage(event.eventType(), event.aggregateId(), envelope);
|
||||||
try {
|
try {
|
||||||
broker.send(message);
|
activeBroker.send(message);
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException exception) {
|
||||||
throw ex;
|
throw exception;
|
||||||
} catch (Exception ex) {
|
} catch (Exception exception) {
|
||||||
// Wrap checked exceptions; preserve cause so the relay can inspect it.
|
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"outbox publish failed for broker '" + broker.brokerId() + "'", ex);
|
"outbox publish failed for broker '" + activeBroker.brokerId() + "'", exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.messaging.platformbridge;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import dev.caskeleton.messaging.api.CausationId;
|
||||||
|
import dev.caskeleton.messaging.api.ContentType;
|
||||||
|
import dev.caskeleton.messaging.api.CorrelationId;
|
||||||
|
import dev.caskeleton.messaging.api.MessageEnvelope;
|
||||||
|
import dev.caskeleton.messaging.api.MessageId;
|
||||||
|
import dev.caskeleton.messaging.api.MessageType;
|
||||||
|
import dev.caskeleton.messaging.api.ProducerId;
|
||||||
|
import dev.caskeleton.messaging.api.SchemaVersion;
|
||||||
|
import dev.caskeleton.messaging.api.TenantContext;
|
||||||
|
import dev.caskeleton.messaging.api.TraceContext;
|
||||||
|
import dev.caskeleton.messaging.api.destination.DestinationName;
|
||||||
|
import dev.caskeleton.messaging.api.destination.MessageDestination;
|
||||||
|
import dev.caskeleton.messaging.api.header.HeaderName;
|
||||||
|
import dev.caskeleton.messaging.api.header.HeaderValue;
|
||||||
|
import dev.caskeleton.messaging.api.header.MessageHeaders;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishCompletion;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishOptions;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishResult;
|
||||||
|
import dev.caskeleton.messaging.api.publish.TransmissionEvidence;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessage;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessagePublisher;
|
||||||
|
import dev.caskeleton.messaging.schema.SchemaReference;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anti-corruption bridge from the application's canonical integration event to the messaging
|
||||||
|
* platform's central pre-encoded publish path.
|
||||||
|
*
|
||||||
|
* <p>The bridge does not reconstruct or re-encode the event. The exact canonical envelope bytes
|
||||||
|
* produced by the application contract boundary are carried as the platform payload. Facts the
|
||||||
|
* platform models directly are mapped to first-class fields; provider-neutral evidence without a
|
||||||
|
* first-class field is retained in bounded {@code x-ca-*} headers.
|
||||||
|
*/
|
||||||
|
public final class PlatformIntegrationEventPublishAdapter implements IntegrationEventPublishPort {
|
||||||
|
|
||||||
|
private final EncodedMessagePublisher publisher;
|
||||||
|
private final ProducerId producer;
|
||||||
|
|
||||||
|
public PlatformIntegrationEventPublishAdapter(
|
||||||
|
EncodedMessagePublisher publisher, String producerId) {
|
||||||
|
this.publisher = Objects.requireNonNull(publisher, "publisher must not be null");
|
||||||
|
this.producer = new ProducerId(producerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletionStage<OutboxPublishOutcome> publish(ValidatedIntegrationEvent event) {
|
||||||
|
final PreparedPublish prepared;
|
||||||
|
try {
|
||||||
|
prepared = prepare(Objects.requireNonNull(event, "event must not be null"));
|
||||||
|
} catch (RuntimeException incompatibleCanonicalEvent) {
|
||||||
|
return CompletableFuture.completedFuture(OutboxPublishOutcome.REJECTED_BEFORE_SEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return publisher
|
||||||
|
.publishEncoded(prepared.destination(), prepared.envelope(), PublishOptions.defaults())
|
||||||
|
.thenApply(PlatformIntegrationEventPublishAdapter::mapOutcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PreparedPublish prepare(ValidatedIntegrationEvent event) {
|
||||||
|
MessageId messageId = canonicalMessageId(event.eventId().value());
|
||||||
|
Optional<CausationId> causationId =
|
||||||
|
event.causationId().map(PlatformIntegrationEventPublishAdapter::canonicalCausationId);
|
||||||
|
MessageType messageType = new MessageType(event.contractId().value());
|
||||||
|
SchemaVersion schemaVersion = new SchemaVersion(event.payloadVersion());
|
||||||
|
DestinationName destinationName = new DestinationName(event.logicalDestinationId().value());
|
||||||
|
ContentType contentType = new ContentType(event.contentType());
|
||||||
|
|
||||||
|
EncodedMessage encoded =
|
||||||
|
new EncodedMessage(
|
||||||
|
event.envelopeBytes(),
|
||||||
|
contentType,
|
||||||
|
Optional.of(SchemaReference.of(event.contractId().value(), schemaVersion)));
|
||||||
|
|
||||||
|
MessageEnvelope<EncodedMessage> envelope =
|
||||||
|
new MessageEnvelope<>(
|
||||||
|
messageId,
|
||||||
|
messageType,
|
||||||
|
schemaVersion,
|
||||||
|
event.occurredAt(),
|
||||||
|
Optional.of(event.occurredAt()),
|
||||||
|
producer,
|
||||||
|
Optional.of(new CorrelationId(event.correlationId())),
|
||||||
|
causationId,
|
||||||
|
contentType,
|
||||||
|
Optional.of(event.partitionKeyText()),
|
||||||
|
Optional.of(event.order().sequence() + ":" + event.order().eventIndex()),
|
||||||
|
Optional.of(new TenantContext(event.aggregate().tenantScope())),
|
||||||
|
TraceContext.none(),
|
||||||
|
evidenceHeaders(event),
|
||||||
|
encoded);
|
||||||
|
|
||||||
|
MessageDestination<EncodedMessage> destination =
|
||||||
|
new MessageDestination<>(destinationName, messageType, EncodedMessage.class);
|
||||||
|
return new PreparedPublish(destination, envelope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MessageHeaders evidenceHeaders(ValidatedIntegrationEvent event) {
|
||||||
|
Map<HeaderName, HeaderValue> values = new LinkedHashMap<>();
|
||||||
|
put(values, "x-ca-envelope-version", Integer.toString(event.envelopeVersion()));
|
||||||
|
put(values, "x-ca-envelope-sha256", event.envelopeSha256().toString());
|
||||||
|
put(values, "x-ca-schema-set-sha256", event.schemaSetHash().toString());
|
||||||
|
put(values, "x-ca-envelope-schema-sha256", event.envelopeSchemaHash().toString());
|
||||||
|
put(values, "x-ca-payload-schema-sha256", event.payloadSchemaHash().toString());
|
||||||
|
put(values, "x-ca-contract-catalog-revision", event.contractCatalogRevision());
|
||||||
|
put(values, "x-ca-destination-binding-revision", event.destinationBindingRevision());
|
||||||
|
put(values, "x-ca-aggregate-type", event.aggregate().aggregateType());
|
||||||
|
put(values, "x-ca-aggregate-id", event.aggregate().aggregateId());
|
||||||
|
put(values, "x-ca-aggregate-sequence", Long.toString(event.order().sequence()));
|
||||||
|
put(values, "x-ca-event-index", Integer.toString(event.order().eventIndex()));
|
||||||
|
return MessageHeaders.platform(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void put(Map<HeaderName, HeaderValue> target, String name, String value) {
|
||||||
|
target.put(new HeaderName(name), new HeaderValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MessageId canonicalMessageId(String value) {
|
||||||
|
return new MessageId(UUID.fromString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CausationId canonicalCausationId(String value) {
|
||||||
|
return new CausationId(canonicalMessageId(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutboxPublishOutcome mapOutcome(PublishResult result) {
|
||||||
|
Objects.requireNonNull(result, "publish result must not be null");
|
||||||
|
if (result.completion() == PublishCompletion.CONFIRMED) {
|
||||||
|
return OutboxPublishOutcome.CONFIRMED;
|
||||||
|
}
|
||||||
|
if (result.completion() == PublishCompletion.AMBIGUOUS) {
|
||||||
|
return OutboxPublishOutcome.AMBIGUOUS;
|
||||||
|
}
|
||||||
|
return result.evidence().transmission() == TransmissionEvidence.NOT_TRANSMITTED
|
||||||
|
? OutboxPublishOutcome.REJECTED_BEFORE_SEND
|
||||||
|
: OutboxPublishOutcome.REJECTED_AFTER_BROKER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PreparedPublish(
|
||||||
|
MessageDestination<EncodedMessage> destination, MessageEnvelope<EncodedMessage> envelope) {}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.messaging.autoconfigure;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import dev.caskeleton.adapter.outbound.support.FailOpenDependencyLogger;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessagePublisher;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
|
||||||
|
class MessagingPlatformBridgeAutoConfigurationTest {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner runner =
|
||||||
|
new ApplicationContextRunner()
|
||||||
|
.withConfiguration(AutoConfigurations.of(MessagingBridgeRootAutoConfiguration.class))
|
||||||
|
.withBean(FailOpenDependencyLogger.class, FailOpenDependencyLogger::new)
|
||||||
|
.withBean(
|
||||||
|
EncodedMessagePublisher.class,
|
||||||
|
() ->
|
||||||
|
(destination, message, options) -> {
|
||||||
|
throw new AssertionError("publisher is not invoked while wiring the bridge");
|
||||||
|
})
|
||||||
|
.withPropertyValues("app.messaging.enabled=true");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void explicitProducerIdCreatesOneCanonicalPublishBridge() {
|
||||||
|
runner
|
||||||
|
.withPropertyValues("app.messaging.producer-id=portfolio-service")
|
||||||
|
.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).hasSingleBean(IntegrationEventPublishPort.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingProducerIdDoesNotInventCanonicalPublisherIdentity() {
|
||||||
|
runner.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).doesNotHaveBean(IntegrationEventPublishPort.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.messaging.kafka;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import dev.caskeleton.adapter.outbound.messaging.autoconfigure.MessagingBridgeRootAutoConfiguration;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
class KafkaSenderAutoConfigurationOwnershipTest {
|
||||||
|
@Test
|
||||||
|
void messagingBridgeRootOwnsTheProductionKafkaSenderInsideTheAdapter() {
|
||||||
|
Import imports = MessagingBridgeRootAutoConfiguration.class.getAnnotation(Import.class);
|
||||||
|
|
||||||
|
assertThat(imports).isNotNull();
|
||||||
|
assertThat(Arrays.asList(imports.value()))
|
||||||
|
.contains(KafkaSenderAutoConfiguration.class, KafkaAdapterConfig.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.messaging.outbox;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import dev.caskeleton.adapter.outbound.messaging.core.MessageBroker;
|
||||||
|
import dev.caskeleton.adapter.outbound.messaging.core.OutboundMessage;
|
||||||
|
import dev.caskeleton.application.messaging.contract.ContractId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.LogicalDestinationId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.Sha256;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateIdentity;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateOrder;
|
||||||
|
import dev.caskeleton.application.messaging.event.EventId;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import dev.caskeleton.application.outbox.CanonicalClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import dev.caskeleton.shared.error.AdapterDisabledException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class OutboxCanonicalPublishRoutingTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canonicalClaimUsesOnlyCanonicalPublisherAndPreservesExactEvent() {
|
||||||
|
RecordingBroker broker = new RecordingBroker();
|
||||||
|
RecordingCanonicalPublisher canonical = new RecordingCanonicalPublisher();
|
||||||
|
canonical.result = OutboxPublishOutcome.AMBIGUOUS;
|
||||||
|
OutboxMessagePublishAdapter adapter =
|
||||||
|
new OutboxMessagePublishAdapter(Optional.of(broker), Optional.of(canonical));
|
||||||
|
CanonicalClaimedOutboxEvent claim =
|
||||||
|
new CanonicalClaimedOutboxEvent(canonicalEvent(), OutboxEventStatus.IN_FLIGHT, 1);
|
||||||
|
|
||||||
|
OutboxPublishOutcome outcome = adapter.publishForOutcome(claim);
|
||||||
|
|
||||||
|
assertThat(outcome).isEqualTo(OutboxPublishOutcome.AMBIGUOUS);
|
||||||
|
assertThat(canonical.events).containsExactly(claim.event());
|
||||||
|
assertThat(broker.sent).isEmpty();
|
||||||
|
assertThat(canonical.events.getFirst().envelopeBytes())
|
||||||
|
.containsExactly(claim.event().envelopeBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void legacyClaimUsesOnlyLegacyBroker() {
|
||||||
|
RecordingBroker broker = new RecordingBroker();
|
||||||
|
RecordingCanonicalPublisher canonical = new RecordingCanonicalPublisher();
|
||||||
|
OutboxMessagePublishAdapter adapter =
|
||||||
|
new OutboxMessagePublishAdapter(Optional.of(broker), Optional.of(canonical));
|
||||||
|
|
||||||
|
OutboxPublishOutcome outcome = adapter.publishForOutcome(legacyEvent());
|
||||||
|
|
||||||
|
assertThat(outcome).isEqualTo(OutboxPublishOutcome.CONFIRMED);
|
||||||
|
assertThat(broker.sent).hasSize(1);
|
||||||
|
assertThat(canonical.events).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disabledCanonicalTransportRejectsCanonicalClaimEvenWhenPublisherExists() {
|
||||||
|
RecordingBroker broker = new RecordingBroker();
|
||||||
|
RecordingCanonicalPublisher canonical = new RecordingCanonicalPublisher();
|
||||||
|
OutboxMessagePublishAdapter adapter =
|
||||||
|
new OutboxMessagePublishAdapter(Optional.of(broker), Optional.of(canonical), false);
|
||||||
|
|
||||||
|
assertThatThrownBy(
|
||||||
|
() ->
|
||||||
|
adapter.publishForOutcome(
|
||||||
|
new CanonicalClaimedOutboxEvent(
|
||||||
|
canonicalEvent(), OutboxEventStatus.IN_FLIGHT, 1)))
|
||||||
|
.isInstanceOf(AdapterDisabledException.class)
|
||||||
|
.hasMessageContaining("canonical");
|
||||||
|
|
||||||
|
assertThat(canonical.events).isEmpty();
|
||||||
|
assertThat(broker.sent).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingBranchDependencyFailsClosedWithoutFallingAcrossTransports() {
|
||||||
|
OutboxMessagePublishAdapter canonicalOnly =
|
||||||
|
new OutboxMessagePublishAdapter(
|
||||||
|
Optional.empty(), Optional.of(new RecordingCanonicalPublisher()));
|
||||||
|
OutboxMessagePublishAdapter legacyOnly =
|
||||||
|
new OutboxMessagePublishAdapter(Optional.of(new RecordingBroker()), Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> canonicalOnly.publishForOutcome(legacyEvent()))
|
||||||
|
.isInstanceOf(AdapterDisabledException.class);
|
||||||
|
assertThatThrownBy(
|
||||||
|
() ->
|
||||||
|
legacyOnly.publishForOutcome(
|
||||||
|
new CanonicalClaimedOutboxEvent(
|
||||||
|
canonicalEvent(), OutboxEventStatus.IN_FLIGHT, 1)))
|
||||||
|
.isInstanceOf(AdapterDisabledException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutboxEvent legacyEvent() {
|
||||||
|
return new OutboxEvent(
|
||||||
|
"legacy-1",
|
||||||
|
"LegacyEvent",
|
||||||
|
"aggregate-1",
|
||||||
|
"{}",
|
||||||
|
Instant.parse("2026-09-18T03:00:00Z"),
|
||||||
|
"corr-1",
|
||||||
|
"legacy-1",
|
||||||
|
OutboxEventStatus.IN_FLIGHT,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValidatedIntegrationEvent canonicalEvent() {
|
||||||
|
String partition = "a".repeat(64);
|
||||||
|
Sha256 zero = new Sha256(new byte[32]);
|
||||||
|
return new ValidatedIntegrationEvent(
|
||||||
|
new EventId("01994e11-4d88-7000-8000-000000000001"),
|
||||||
|
new ContractId("portfolio.worklog.reserved"),
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
new LogicalDestinationId("portfolio-domain-events"),
|
||||||
|
new AggregateIdentity("tenant-a", "worklog", "worklog-42"),
|
||||||
|
new AggregateOrder(17, 2),
|
||||||
|
Instant.parse("2026-09-18T03:00:00Z"),
|
||||||
|
"corr-1",
|
||||||
|
Optional.empty(),
|
||||||
|
partition,
|
||||||
|
partition.getBytes(StandardCharsets.US_ASCII),
|
||||||
|
"{\"wire\":\"exact\"}".getBytes(StandardCharsets.UTF_8),
|
||||||
|
"application/json",
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
"catalog-r1",
|
||||||
|
"binding-r1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecordingBroker implements MessageBroker {
|
||||||
|
private final List<OutboundMessage> sent = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String brokerId() {
|
||||||
|
return "kafka";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(OutboundMessage message) {
|
||||||
|
sent.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecordingCanonicalPublisher implements IntegrationEventPublishPort {
|
||||||
|
private final List<ValidatedIntegrationEvent> events = new ArrayList<>();
|
||||||
|
private OutboxPublishOutcome result = OutboxPublishOutcome.CONFIRMED;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.concurrent.CompletionStage<OutboxPublishOutcome> publish(
|
||||||
|
ValidatedIntegrationEvent event) {
|
||||||
|
events.add(event);
|
||||||
|
return CompletableFuture.completedFuture(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -171,11 +171,11 @@ class OutboxMessagePublishAdapterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void adapterStateContainsOnlyTheBroker() {
|
void adapterStateContainsOnlyTheTwoTransportReferencesAndCanonicalGate() {
|
||||||
assertThat(
|
assertThat(
|
||||||
Arrays.stream(OutboxMessagePublishAdapter.class.getDeclaredFields())
|
Arrays.stream(OutboxMessagePublishAdapter.class.getDeclaredFields())
|
||||||
.map(field -> field.getName()))
|
.map(field -> field.getName()))
|
||||||
.containsExactly("broker");
|
.containsExactlyInAnyOrder("broker", "canonicalPublisher", "canonicalTransportEnabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.messaging.platformbridge;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.contract.ContractId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.LogicalDestinationId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.Sha256;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateIdentity;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateOrder;
|
||||||
|
import dev.caskeleton.application.messaging.event.EventId;
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import dev.caskeleton.messaging.api.ContentType;
|
||||||
|
import dev.caskeleton.messaging.api.MessageEnvelope;
|
||||||
|
import dev.caskeleton.messaging.api.destination.MessageDestination;
|
||||||
|
import dev.caskeleton.messaging.api.error.FailureCategory;
|
||||||
|
import dev.caskeleton.messaging.api.error.FailureDescriptor;
|
||||||
|
import dev.caskeleton.messaging.api.publish.ConfirmationLevel;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishCompletion;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishEvidence;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishOptions;
|
||||||
|
import dev.caskeleton.messaging.api.publish.PublishResult;
|
||||||
|
import dev.caskeleton.messaging.api.publish.RoutingOutcome;
|
||||||
|
import dev.caskeleton.messaging.api.publish.TransmissionEvidence;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessage;
|
||||||
|
import dev.caskeleton.messaging.schema.EncodedMessagePublisher;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class PlatformIntegrationEventPublishAdapterTest {
|
||||||
|
private static final String EVENT_ID = "0190f4aa-0000-7000-8000-000000000001";
|
||||||
|
private static final String CAUSATION_ID = "0190f4aa-0000-7000-8000-000000000002";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void goldenMappingPreservesCanonicalIdentityRoutingAndExactEnvelopeBytes() {
|
||||||
|
RecordingPublisher publisher = new RecordingPublisher(confirmed());
|
||||||
|
IntegrationEventPublishPort bridge =
|
||||||
|
new PlatformIntegrationEventPublishAdapter(publisher, "portfolio-service");
|
||||||
|
ValidatedIntegrationEvent event = event(EVENT_ID, Optional.of(CAUSATION_ID));
|
||||||
|
|
||||||
|
assertThat(bridge.publish(event).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.CONFIRMED);
|
||||||
|
|
||||||
|
MessageDestination<EncodedMessage> destination = publisher.destination;
|
||||||
|
MessageEnvelope<EncodedMessage> envelope = publisher.envelope;
|
||||||
|
assertThat(destination.name().value()).isEqualTo("portfolio-domain-events");
|
||||||
|
assertThat(destination.messageType().value()).isEqualTo("portfolio.worklog.reserved");
|
||||||
|
assertThat(envelope.messageId().value()).isEqualTo(UUID.fromString(EVENT_ID));
|
||||||
|
assertThat(envelope.messageType().value()).isEqualTo("portfolio.worklog.reserved");
|
||||||
|
assertThat(envelope.schemaVersion().value()).isEqualTo(3);
|
||||||
|
assertThat(envelope.producedAt()).isEqualTo(event.occurredAt());
|
||||||
|
assertThat(envelope.occurredAt()).contains(event.occurredAt());
|
||||||
|
assertThat(envelope.producer().value()).isEqualTo("portfolio-service");
|
||||||
|
assertThat(envelope.correlationId().orElseThrow().value()).isEqualTo("corr-1");
|
||||||
|
assertThat(envelope.causationId().orElseThrow().value().value())
|
||||||
|
.isEqualTo(UUID.fromString(CAUSATION_ID));
|
||||||
|
assertThat(envelope.partitionKey()).contains(event.partitionKeyText());
|
||||||
|
assertThat(envelope.orderingKey()).contains("17:2");
|
||||||
|
assertThat(envelope.tenantContext().orElseThrow().tenantId()).isEqualTo("tenant-a");
|
||||||
|
assertThat(envelope.traceContext().traceparent()).isEmpty();
|
||||||
|
assertThat(envelope.contentType()).isEqualTo(ContentType.JSON);
|
||||||
|
assertThat(envelope.payload().bytes()).containsExactly(event.envelopeBytes());
|
||||||
|
assertThat(envelope.payload().schemaReference().orElseThrow().subject())
|
||||||
|
.isEqualTo(event.contractId().value());
|
||||||
|
assertThat(envelope.headers().find("x-ca-envelope-version").orElseThrow().value())
|
||||||
|
.isEqualTo("1");
|
||||||
|
assertThat(envelope.headers().find("x-ca-envelope-sha256").orElseThrow().value())
|
||||||
|
.isEqualTo(event.envelopeSha256().toString());
|
||||||
|
assertThat(envelope.headers().find("x-ca-destination-binding-revision").orElseThrow().value())
|
||||||
|
.isEqualTo("binding-r1");
|
||||||
|
assertThat(publisher.calls).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void outcomeMappingUsesTransmissionEvidenceRatherThanEnumNameGuessing() {
|
||||||
|
RecordingPublisher publisher = new RecordingPublisher(confirmed());
|
||||||
|
PlatformIntegrationEventPublishAdapter bridge =
|
||||||
|
new PlatformIntegrationEventPublishAdapter(publisher, "portfolio-service");
|
||||||
|
ValidatedIntegrationEvent event = event(EVENT_ID, Optional.empty());
|
||||||
|
|
||||||
|
assertThat(bridge.publish(event).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.CONFIRMED);
|
||||||
|
publisher.result = ambiguous();
|
||||||
|
assertThat(bridge.publish(event).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.AMBIGUOUS);
|
||||||
|
publisher.result = rejected(PublishEvidence.notTransmitted());
|
||||||
|
assertThat(bridge.publish(event).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.REJECTED_BEFORE_SEND);
|
||||||
|
publisher.result =
|
||||||
|
rejected(
|
||||||
|
new PublishEvidence(
|
||||||
|
true, TransmissionEvidence.TRANSMITTED, false, ConfirmationLevel.NONE));
|
||||||
|
assertThat(bridge.publish(event).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.REJECTED_AFTER_BROKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void incompatibleApplicationIdentityFailsClosedBeforePublisherInvocation() {
|
||||||
|
RecordingPublisher publisher = new RecordingPublisher(confirmed());
|
||||||
|
PlatformIntegrationEventPublishAdapter bridge =
|
||||||
|
new PlatformIntegrationEventPublishAdapter(publisher, "portfolio-service");
|
||||||
|
|
||||||
|
assertThat(bridge.publish(event("event-1", Optional.empty())).toCompletableFuture().join())
|
||||||
|
.isEqualTo(OutboxPublishOutcome.REJECTED_BEFORE_SEND);
|
||||||
|
assertThat(publisher.calls).isZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValidatedIntegrationEvent event(String eventId, Optional<String> causationId) {
|
||||||
|
byte[] envelope = "{\"wire\":\"exact\"}".getBytes(StandardCharsets.UTF_8);
|
||||||
|
Sha256 zero = new Sha256(new byte[32]);
|
||||||
|
String partition = "a".repeat(64);
|
||||||
|
return new ValidatedIntegrationEvent(
|
||||||
|
new EventId(eventId),
|
||||||
|
new ContractId("portfolio.worklog.reserved"),
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
new LogicalDestinationId("portfolio-domain-events"),
|
||||||
|
new AggregateIdentity("tenant-a", "worklog", "worklog-42"),
|
||||||
|
new AggregateOrder(17, 2),
|
||||||
|
Instant.parse("2026-07-28T05:10:30.123Z"),
|
||||||
|
"corr-1",
|
||||||
|
causationId,
|
||||||
|
partition,
|
||||||
|
partition.getBytes(StandardCharsets.US_ASCII),
|
||||||
|
envelope,
|
||||||
|
"application/json",
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
zero,
|
||||||
|
"catalog-r1",
|
||||||
|
"binding-r1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublishResult confirmed() {
|
||||||
|
return new PublishResult(
|
||||||
|
PublishCompletion.CONFIRMED,
|
||||||
|
PublishEvidence.confirmed(ConfirmationLevel.BROKER_ACK),
|
||||||
|
RoutingOutcome.ROUTED,
|
||||||
|
Optional.empty(),
|
||||||
|
1,
|
||||||
|
Duration.ZERO,
|
||||||
|
Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublishResult ambiguous() {
|
||||||
|
return new PublishResult(
|
||||||
|
PublishCompletion.AMBIGUOUS,
|
||||||
|
PublishEvidence.ambiguous(),
|
||||||
|
RoutingOutcome.UNKNOWN,
|
||||||
|
Optional.empty(),
|
||||||
|
1,
|
||||||
|
Duration.ZERO,
|
||||||
|
Optional.of(
|
||||||
|
FailureDescriptor.of(FailureCategory.AMBIGUOUS, "AMBIGUOUS_TEST", "ambiguous")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublishResult rejected(PublishEvidence evidence) {
|
||||||
|
return new PublishResult(
|
||||||
|
PublishCompletion.REJECTED,
|
||||||
|
evidence,
|
||||||
|
RoutingOutcome.UNKNOWN,
|
||||||
|
Optional.empty(),
|
||||||
|
1,
|
||||||
|
Duration.ZERO,
|
||||||
|
Optional.of(
|
||||||
|
FailureDescriptor.of(FailureCategory.CONFIGURATION, "REJECTED_TEST", "rejected")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecordingPublisher implements EncodedMessagePublisher {
|
||||||
|
private PublishResult result;
|
||||||
|
private int calls;
|
||||||
|
private MessageDestination<EncodedMessage> destination;
|
||||||
|
private MessageEnvelope<EncodedMessage> envelope;
|
||||||
|
|
||||||
|
private RecordingPublisher(PublishResult result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletionStage<PublishResult> publishEncoded(
|
||||||
|
MessageDestination<EncodedMessage> destination,
|
||||||
|
MessageEnvelope<EncodedMessage> message,
|
||||||
|
PublishOptions options) {
|
||||||
|
calls++;
|
||||||
|
this.destination = destination;
|
||||||
|
this.envelope = message;
|
||||||
|
return CompletableFuture.completedFuture(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,10 +57,10 @@ dependencies {
|
|||||||
// asserts. verifyDependencyPolicy resolves runtimeClasspath and fails if the coordinate is present.
|
// asserts. verifyDependencyPolicy resolves runtimeClasspath and fails if the coordinate is present.
|
||||||
dependencyPolicy {
|
dependencyPolicy {
|
||||||
absent 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml',
|
absent 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml',
|
||||||
because: 'schemas arrive as JSON strings; a second YAML parser is surface for a format ' +
|
'schemas arrive as JSON strings; a second YAML parser is surface for a format ' +
|
||||||
'this leaf never reads'
|
'this leaf never reads'
|
||||||
absent 'tools.jackson.dataformat:jackson-dataformat-yaml',
|
absent 'tools.jackson.dataformat:jackson-dataformat-yaml',
|
||||||
because: 'the Jackson 3 coordinate of the same parser, excluded for the same reason'
|
'the Jackson 3 coordinate of the same parser, excluded for the same reason'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The three notification gates run with the leaf they are about.
|
// The three notification gates run with the leaf they are about.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driven adapter: provider-neutral semantic object-storage ports plus a bounded local-development
|
// Driven adapter: provider-neutral semantic object-storage ports plus a bounded local-development
|
||||||
@@ -27,7 +28,7 @@ description = 'Outbound adapter: object storage (S3/MinIO + local filesystem)'
|
|||||||
// source sets import no type from `dev.caskeleton` at all (they drive MinIO and S3 through the AWS
|
// source sets import no type from `dev.caskeleton` at all (they drive MinIO and S3 through the AWS
|
||||||
// SDK), so the `test` edge was carrying nothing and a `testFixtures` variant would have been an
|
// SDK), so the `test` edge was carrying nothing and a `testFixtures` variant would have been an
|
||||||
// empty one. Verified by compiling all three with the edge removed.
|
// empty one. Verified by compiling all three with the edge removed.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
sourceSet('objectStorageMinioContractTest') { compilesAgainst 'main' }
|
sourceSet('objectStorageMinioContractTest') { compilesAgainst 'main' }
|
||||||
sourceSet('objectStorageMinioFaultTest') { compilesAgainst 'main' }
|
sourceSet('objectStorageMinioFaultTest') { compilesAgainst 'main' }
|
||||||
sourceSet('objectStorageAwsQualificationTest') { compilesAgainst 'main' }
|
sourceSet('objectStorageAwsQualificationTest') { compilesAgainst 'main' }
|
||||||
@@ -67,35 +68,38 @@ tasks.named('test') {
|
|||||||
systemProperty 'objectstorage.readiness.registry', objectStorageReadinessRegistry.absolutePath
|
systemProperty 'objectstorage.readiness.registry', objectStorageReadinessRegistry.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
def objectStorageMinioContractQualification = registerStrictQualificationTest(
|
def objectStorageMinioContractQualification = extensions.getByName('strictQualification').register(
|
||||||
name: 'objectStorageMinioContractTest',
|
'objectStorageMinioContractTest',
|
||||||
sourceSet: sourceSets.objectStorageMinioContractTest,
|
sourceSets.objectStorageMinioContractTest,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioDirectTransferContractTest',
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioDirectTransferContractTest',
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioManagedObjectContractTest'
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioManagedObjectContractTest'
|
||||||
],
|
],
|
||||||
description: 'Runs the non-skipping exact-release MinIO managed object contract.')
|
'Runs the non-skipping exact-release MinIO managed object contract.'
|
||||||
|
)
|
||||||
objectStorageMinioContractQualification.configure {
|
objectStorageMinioContractQualification.configure {
|
||||||
shouldRunAfter tasks.named('test')
|
shouldRunAfter tasks.named('test')
|
||||||
}
|
}
|
||||||
|
|
||||||
def objectStorageMinioFaultQualification = registerStrictQualificationTest(
|
def objectStorageMinioFaultQualification = extensions.getByName('strictQualification').register(
|
||||||
name: 'objectStorageMinioFaultTest',
|
'objectStorageMinioFaultTest',
|
||||||
sourceSet: sourceSets.objectStorageMinioFaultTest,
|
sourceSets.objectStorageMinioFaultTest,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioDirectTransferFaultTest',
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioDirectTransferFaultTest',
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioManagedObjectFaultTest'
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.MinioManagedObjectFaultTest'
|
||||||
],
|
],
|
||||||
description: 'Runs the non-skipping digest-pinned MinIO/Toxiproxy fault contract.')
|
'Runs the non-skipping digest-pinned MinIO/Toxiproxy fault contract.'
|
||||||
|
)
|
||||||
objectStorageMinioFaultQualification.configure {
|
objectStorageMinioFaultQualification.configure {
|
||||||
shouldRunAfter objectStorageMinioContractQualification
|
shouldRunAfter objectStorageMinioContractQualification
|
||||||
}
|
}
|
||||||
|
|
||||||
registerStrictQualificationTest(
|
extensions.getByName('strictQualification').register(
|
||||||
name: 'objectStorageAwsQualificationTest',
|
'objectStorageAwsQualificationTest',
|
||||||
sourceSet: sourceSets.objectStorageAwsQualificationTest,
|
sourceSets.objectStorageAwsQualificationTest,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.AwsS3DirectTransferQualificationTest',
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.AwsS3DirectTransferQualificationTest',
|
||||||
'dev.caskeleton.adapter.outbound.objectstorage.qualification.AwsS3ManagedCommonSubsetQualificationTest'
|
'dev.caskeleton.adapter.outbound.objectstorage.qualification.AwsS3ManagedCommonSubsetQualificationTest'
|
||||||
],
|
],
|
||||||
description: 'Runs only with explicit protected AWS sandbox authority and exact inputs.')
|
'Runs only with explicit protected AWS sandbox authority and exact inputs.'
|
||||||
|
)
|
||||||
|
|||||||
@@ -59,7 +59,13 @@ cd src
|
|||||||
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest # EXPLAIN structure
|
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest # EXPLAIN structure
|
||||||
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformSecurityTest # runtime role privileges
|
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformSecurityTest # runtime role privileges
|
||||||
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformPoolContractTest # pool behaviour
|
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformPoolContractTest # pool behaviour
|
||||||
./gradlew jpaReleaseGate # every gate, from the root
|
# Release blocking composition is CI-owned; locally run the exact database qualification set:
|
||||||
|
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest \
|
||||||
|
:adapter:outbound:persistence-jpa:jpaPlatformMigrationTest \
|
||||||
|
:adapter:outbound:persistence-jpa:jpaPlatformFailureTest \
|
||||||
|
:adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest \
|
||||||
|
:adapter:outbound:persistence-jpa:jpaPlatformSecurityTest \
|
||||||
|
:adapter:outbound:persistence-jpa:jpaPlatformPoolContractTest
|
||||||
```
|
```
|
||||||
|
|
||||||
Every Docker-backed lane fails closed. A selected lane that discovers nothing, or a container that
|
Every Docker-backed lane fails closed. A selected lane that discovers nothing, or a container that
|
||||||
@@ -72,6 +78,7 @@ nobody tested.
|
|||||||
- Spring Data repositories.
|
- Spring Data repositories.
|
||||||
- Persistence mappers.
|
- Persistence mappers.
|
||||||
- Repository adapter implementations.
|
- Repository adapter implementations.
|
||||||
|
- Outbox transport-only compatibility: raw `NewOutboxEvent` append belongs only to `LegacyOutboxAppendPort`; canonical `OutboxAppendPort` persists `ValidatedIntegrationEvent` exact bytes. Partial canonical metadata must fail closed and this path must not activate `POLLING_V2`.
|
||||||
- `TransactionPort` implementation (`SpringTransactionPort`) — the bridge between
|
- `TransactionPort` implementation (`SpringTransactionPort`) — the bridge between
|
||||||
application transactional intent and Spring's `PlatformTransactionManager`.
|
application transactional intent and Spring's `PlatformTransactionManager`.
|
||||||
- Audit-metadata base + actor seam (`audit/AuditableEntity`, `audit/AuditContextPort`,
|
- Audit-metadata base + actor seam (`audit/AuditableEntity`, `audit/AuditContextPort`,
|
||||||
|
|||||||
@@ -146,7 +146,18 @@ DB 저장으로 fallback. 실제 구현(S3 / GCS / MinIO)을 와이어링하면
|
|||||||
|
|
||||||
## outbox — `OutboxStoreAdapter` 외
|
## outbox — `OutboxStoreAdapter` 외
|
||||||
|
|
||||||
스키마는 Flyway(`V3__outbox_event.sql`)가 소유한다.
|
스키마는 Flyway(`V3__outbox_event.sql`)가 소유하고, MSG-015 transport-only cutover를 위한
|
||||||
|
`V13__outbox_canonical_transport_compatibility.sql`이 기존 row를 깨지 않으면서 canonical metadata와
|
||||||
|
exact `BYTEA envelope_bytes`를 additive하게 확장한다. 이 V13은 `POLLING_V2` authority 전환이 아니다.
|
||||||
|
|
||||||
|
append 경계도 명시적으로 분리된다. `LegacyOutboxAppendPort`는 기존 `NewOutboxEvent` compatibility
|
||||||
|
producer 전용이고, `OutboxAppendPort`는 `ValidatedIntegrationEvent`만 받는다.
|
||||||
|
`CanonicalOutboxAppendAdapter`는 `ca-skeleton.outbox.enabled=true`와
|
||||||
|
`canonical-transport-enabled=true`가 모두 만족될 때만 존재하며 exact bytes를 저장한다. legacy 필수
|
||||||
|
컬럼에는 rollback window용 UTF-8 compatibility projection을 같이 기록한다.
|
||||||
|
|
||||||
|
claim 시 canonical column이 전부 null이면 legacy `OutboxEvent`, 전부 존재하면
|
||||||
|
`CanonicalClaimedOutboxEvent`로 매핑한다. 일부만 존재하는 row는 legacy로 강등하지 않고 fail-closed한다.
|
||||||
|
|
||||||
### 트랜잭션 경계 계약
|
### 트랜잭션 경계 계약
|
||||||
- **append**: 호출자의 `TransactionPort.inWrite()` 경계 안에서 호출되어야 하며, 내부에서 새
|
- **append**: 호출자의 `TransactionPort.inWrite()` 경계 안에서 호출되어야 하며, 내부에서 새
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ plugins {
|
|||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
id 'ca.jpa-evidence'
|
id 'ca.jpa-evidence'
|
||||||
|
id 'ca.jpa-test-lanes'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001.
|
||||||
@@ -20,7 +22,7 @@ plugins {
|
|||||||
// The testkit is its own source set rather than part of `test` because more than one lane consumes
|
// The testkit is its own source set rather than part of `test` because more than one lane consumes
|
||||||
// it and because a source set whose dependencies are declared only on the test configurations gives
|
// it and because a source set whose dependencies are declared only on the test configurations gives
|
||||||
// the design's "no production module depends on the testkit" guarantee without a new Gradle project.
|
// the design's "no production module depends on the testkit" guarantee without a new Gradle project.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
sourceSet('postgresqlIntegrationTest') {
|
sourceSet('postgresqlIntegrationTest') {
|
||||||
// 'testFixtures' as well as 'main': the integration lane consumed the testkit through the
|
// 'testFixtures' as well as 'main': the integration lane consumed the testkit through the
|
||||||
// convention's `consumedBy 'test', 'postgresqlIntegrationTest'`, and java-test-fixtures only
|
// convention's `consumedBy 'test', 'postgresqlIntegrationTest'`, and java-test-fixtures only
|
||||||
@@ -101,143 +103,10 @@ dependencies {
|
|||||||
jpaPlatformPerformanceTestRuntimeOnly 'org.postgresql:postgresql'
|
jpaPlatformPerformanceTestRuntimeOnly 'org.postgresql:postgresql'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fourteen no-skip PostgreSQL readiness lanes, declared rather than assembled.
|
// PostgreSQL readiness, tagged platform, and pool-contract lanes are owned by
|
||||||
//
|
// `ca.jpa-test-lanes`. Their metadata is represented as Java records, so task names, selectors,
|
||||||
// They were fourteen calls to a local `tasks.register(..., Test)` factory that re-spelled the five
|
// tags, descriptions and runtime property wiring are compiled rather than assembled from Groovy Maps.
|
||||||
// lines `ca.strict-test-lane` owns. The convention adds what the factory could not: naming the test
|
// The security lane includes the runtime-role namespace-denial method selector in that typed model.
|
||||||
// through `requires(...)` turns on `failOnNoMatchingTests` AND the post-run check that the named
|
|
||||||
// selector actually executed, so a renamed readiness class fails its lane instead of leaving it
|
|
||||||
// with nothing to run.
|
|
||||||
String jpaPostgreSqlEvidenceImage = providers.gradleProperty('jpaPostgreSqlEvidenceImage')
|
|
||||||
.getOrElse('postgres:16-alpine')
|
|
||||||
String readinessPackage = 'dev.caskeleton.adapter.outbound.persistence.readiness'
|
|
||||||
Map<String, String> postgresqlReadinessLanes = [
|
|
||||||
postgresqlLifecycleIntegrationTest : 'PostgreSqlLifecycleIntegrationTest',
|
|
||||||
postgresqlSecurityBaselineIntegrationTest : 'PostgreSqlSecurityBaselineIntegrationTest',
|
|
||||||
postgresqlMigrationIntegrationTest : 'PostgreSqlMigrationIntegrationTest',
|
|
||||||
postgresqlTransactionIntegrationTest : 'PostgreSqlTransactionIntegrationTest',
|
|
||||||
postgresqlAggregateIntegrationTest : 'PostgreSqlAggregateIntegrationTest',
|
|
||||||
postgresqlQueryIntegrationTest : 'PostgreSqlQueryIntegrationTest',
|
|
||||||
postgresqlIdempotencyIntegrationTest : 'PostgreSqlIdempotencyIntegrationTest',
|
|
||||||
postgresqlOutboxStorageIntegrationTest : 'PostgreSqlOutboxStorageIntegrationTest',
|
|
||||||
postgresqlOutboxPollingIntegrationTest : 'PostgreSqlOutboxPollingIntegrationTest',
|
|
||||||
postgresqlInboxIntegrationTest : 'PostgreSqlInboxIntegrationTest',
|
|
||||||
postgresqlFileserverMigrationIntegrationTest : 'PostgreSqlFileserverMigrationIntegrationTest',
|
|
||||||
postgresqlFileserverMetadataIntegrationTest : 'PostgreSqlFileserverMetadataStoreIntegrationTest',
|
|
||||||
postgresqlFileserverReclamationIntegrationTest : 'PostgreSqlFileserverReclamationIntegrationTest',
|
|
||||||
// The notification stream is opt-in and lives outside the default Flyway location, so "is it
|
|
||||||
// applied and promoted" is a real deployment question with a real wrong answer. This lane
|
|
||||||
// asks it against a real server; the entity-scan half is a unit test.
|
|
||||||
postgresqlNotificationSchemaActivationIntegrationTest :
|
|
||||||
'PostgreSqlNotificationSchemaActivationIntegrationTest'
|
|
||||||
]
|
|
||||||
postgresqlReadinessLanes.each { String taskName, String simpleName ->
|
|
||||||
String testClass = "${readinessPackage}.${simpleName}"
|
|
||||||
strictTestLanes.lane(taskName) {
|
|
||||||
sourceSet = 'postgresqlIntegrationTest'
|
|
||||||
description = "Runs the no-skip real PostgreSQL readiness scenario ${testClass}."
|
|
||||||
requires(testClass)
|
|
||||||
customize = { test ->
|
|
||||||
test.jvmArgs(
|
|
||||||
'-Duser.timezone=UTC',
|
|
||||||
"-Djpa.evidence.postgresql.image=${jpaPostgreSqlEvidenceImage}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rule verifyJpaSecurityFixtures was reaching for is now a selector.
|
|
||||||
//
|
|
||||||
// It read PostgreSqlSecurityBaselineIntegrationTest.java as text and failed when three strings were
|
|
||||||
// absent from it — which three strings sitting in a comment would have satisfied, and which said
|
|
||||||
// nothing about whether the scenario ran. Naming the method on the lane means the convention fails
|
|
||||||
// when the runtime-role denial scenario is renamed or stops executing.
|
|
||||||
strictTestLanes.lanes.named('postgresqlSecurityBaselineIntegrationTest').configure { lane ->
|
|
||||||
lane.requires("${readinessPackage}.PostgreSqlSecurityBaselineIntegrationTest" +
|
|
||||||
'.runtimeRoleCannotCreateInApplicationSchema')
|
|
||||||
}
|
|
||||||
|
|
||||||
// The task itself stays, and its name is not negotiable: config/jpa/readiness-cards.yaml lists it as
|
|
||||||
// a support task of the `jpa-security-baseline` card, and the ca.jpa-evidence qualification plugin resolves every
|
|
||||||
// listed path through `tasks.findByName` and fails the build when one is missing. What changed is
|
|
||||||
// what it checks. Grepping the fixture's source text for 'runtimeRoleCannotCreateInApplicationSchema',
|
|
||||||
// 'assertDockerAvailable' and '42501' passed on three strings in a comment and proved nothing about
|
|
||||||
// execution; the lane's `requires(...)` above is the thing that now enforces the scenario, so this
|
|
||||||
// task verifies that the enforcement is declared rather than re-deriving it from source text.
|
|
||||||
tasks.register('verifyJpaSecurityFixtures') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Verifies the no-skip PostgreSQL security lane still names the runtime-role namespace denial scenario.'
|
|
||||||
String laneName = 'postgresqlSecurityBaselineIntegrationTest'
|
|
||||||
String requiredSelector = "${readinessPackage}.PostgreSqlSecurityBaselineIntegrationTest" +
|
|
||||||
'.runtimeRoleCannotCreateInApplicationSchema'
|
|
||||||
// A live reference to the lane spec's own list, captured at configuration time. Reading it in
|
|
||||||
// `doLast` therefore sees the final declaration without touching `Task.project` at execution
|
|
||||||
// time, which Gradle 9 deprecates and the `--warning-mode=fail` gates reject.
|
|
||||||
List<String> declaredSelectors = strictTestLanes.lanes.getByName(laneName).requiredTests
|
|
||||||
doLast {
|
|
||||||
if (!declaredSelectors.contains(requiredSelector)) {
|
|
||||||
throw new GradleException(
|
|
||||||
"verifyJpaSecurityFixtures: strict test lane '${laneName}' no longer requires " +
|
|
||||||
"'${requiredSelector}'. Without that selector the lane can run the " +
|
|
||||||
'security baseline class with the runtime-role namespace denial scenario ' +
|
|
||||||
"renamed or deleted and still report success. It requires ${declaredSelectors}.")
|
|
||||||
}
|
|
||||||
logger.lifecycle(
|
|
||||||
"verifyJpaSecurityFixtures: OK — '${laneName}' requires the runtime-role namespace denial scenario.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyJpaSqlConstructionSafety keeps its name for the same registry reason, and gives up the half
|
|
||||||
// of its job that a real tool already does.
|
|
||||||
//
|
|
||||||
// It used to also match `(createNativeQuery|queryForObject|update)\s*\([^;]*"\s*\+` against Java
|
|
||||||
// source text: a regex that matches any method named `update`, and that stops at the first `;`
|
|
||||||
// inside a string literal, so it over- and under-reported at once. Concatenated SQL is covered
|
|
||||||
// repo-wide and inter-procedurally by FindSecBugs, which the root build puts on every leaf
|
|
||||||
// (`spotbugsPlugins libs.findsecbugs.plugin`) with SpotBugs' `ignoreFailures` left at its blocking
|
|
||||||
// default and no SQL_INJECTION / SQL_NONCONSTANT exclusion in config/spotbugs/exclude.xml. A
|
|
||||||
// bytecode dataflow check with no package restriction is strictly better than that regex, so the
|
|
||||||
// regex is gone rather than duplicated.
|
|
||||||
//
|
|
||||||
// What no tool covers is the PostgreSQL-specific rule: a `set_config` value must be bound, never
|
|
||||||
// interpolated, because that value carries the tenant id and the search_path. That check stays, and
|
|
||||||
// two things about it changed. It no longer parses Java — it matches the SQL token `set_config('`
|
|
||||||
// and asks whether the same line binds a parameter. And it scans the whole main source root: it was
|
|
||||||
// pinned to `.../persistence/postgresql`, which is why it never saw the two real call sites in
|
|
||||||
// experimental/rls/RlsTenantSessionBinder.java and
|
|
||||||
// experimental/schema/SchemaMultiTenantConnectionProvider.java.
|
|
||||||
tasks.register('verifyJpaSqlConstructionSafety') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Rejects non-parameterized PostgreSQL set_config values anywhere in this leaf.'
|
|
||||||
File mainSource = file('src/main/java')
|
|
||||||
inputs.dir(mainSource)
|
|
||||||
doLast {
|
|
||||||
List<String> violations = []
|
|
||||||
mainSource.eachFileRecurse { File source ->
|
|
||||||
if (!source.name.endsWith('.java')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
source.readLines().eachWithIndex { String line, int index ->
|
|
||||||
String trimmed = line.trim()
|
|
||||||
// Javadoc and line comments mention set_config to explain why it is used; a comment
|
|
||||||
// is not a call site, and treating one as a violation is how a correct build turns
|
|
||||||
// red for a sentence.
|
|
||||||
if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (line.contains("set_config('") && !line.contains('?')) {
|
|
||||||
violations << "${source}:${index + 1}: set_config value is not parameterized"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!violations.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
"verifyJpaSqlConstructionSafety: ${violations.size()} violation(s):\n " +
|
|
||||||
violations.join('\n '))
|
|
||||||
}
|
|
||||||
logger.lifecycle(
|
|
||||||
"verifyJpaSqlConstructionSafety: OK — every set_config value in ${mainSource} binds a parameter.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('postgresqlSecurityBaselineIntegrationTest') {
|
tasks.named('postgresqlSecurityBaselineIntegrationTest') {
|
||||||
// Cross-leaf task edge: see the handoff. verifyCleanArchitectureDependencies inspects
|
// Cross-leaf task edge: see the handoff. verifyCleanArchitectureDependencies inspects
|
||||||
@@ -245,73 +114,8 @@ tasks.named('postgresqlSecurityBaselineIntegrationTest') {
|
|||||||
dependsOn project(':adapter:inbound:web').tasks.named('jpaPersistenceRedactionContractTest')
|
dependsOn project(':adapter:inbound:web').tasks.named('jpaPersistenceRedactionContractTest')
|
||||||
}
|
}
|
||||||
|
|
||||||
// JPA platform lanes (design §40-§41). Each maps one of the plan's JVM test suites onto this
|
// Which JPA lanes block a release is declared by .github/workflows/jpa-release.yml.
|
||||||
// leaf's existing Docker-backed source set; the mapping is recorded in
|
// This leaf declares the lanes and how each runs; it does not own release orchestration.
|
||||||
// docs/jpa/repository-adaptation.md §3.
|
|
||||||
//
|
|
||||||
// Every lane fails closed. `failOnNoDiscoveredTests` matters more here than usual: a selected lane
|
|
||||||
// that discovers nothing reports success, and a contract suite that silently stopped running is
|
|
||||||
// indistinguishable from one that passes.
|
|
||||||
Map<String, List<String>> jpaPlatformLanes = [
|
|
||||||
jpaPlatformContractTest : ['jpa-contract',
|
|
||||||
'Runs the JPA platform contract suite against real PostgreSQL (design §40).'],
|
|
||||||
jpaPlatformMigrationTest : ['jpa-migration',
|
|
||||||
'Runs the Flyway upgrade snapshot scenarios (design §31).'],
|
|
||||||
jpaPlatformFailureTest : ['jpa-failure',
|
|
||||||
'Reproduces deadlock, serialization, and commit-ambiguity failures (design §39).'],
|
|
||||||
jpaPlatformQueryPlanTest : ['jpa-queryplan',
|
|
||||||
'Asserts query plan structure and planner estimate error (design §33).'],
|
|
||||||
jpaPlatformSecurityTest : ['jpa-security',
|
|
||||||
'Verifies runtime role privileges and search_path safety (design §36).']
|
|
||||||
]
|
|
||||||
jpaPlatformLanes.each { String taskName, List<String> spec ->
|
|
||||||
strictTestLanes.lane(taskName) {
|
|
||||||
sourceSet = 'postgresqlIntegrationTest'
|
|
||||||
tag = spec[0]
|
|
||||||
description = spec[1]
|
|
||||||
customize = { test ->
|
|
||||||
test.jvmArgs('-Duser.timezone=UTC')
|
|
||||||
// The Stable matrix selection. An unknown or empty value is an error in
|
|
||||||
// PostgreSqlVersion.parseSelection rather than an empty run.
|
|
||||||
test.systemProperty 'jpa.matrix.versions',
|
|
||||||
(project.findProperty('jpa.matrix.versions') ?: '16').toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The pool behaviour contract. Named for what it does.
|
|
||||||
//
|
|
||||||
// It was `jpaPlatformPerformanceTest`, described as certifying pool and REQUIRES_NEW pressure, and
|
|
||||||
// gated behind a boolean that defaulted to off everywhere it appeared — in this file, and in the
|
|
||||||
// nightly workflow that set it explicitly to off. So the release gate depended on a lane whose only
|
|
||||||
// threshold assertion was that thresholds were not being asserted, and "certified" described a run
|
|
||||||
// in which no latency or throughput bound was ever compared to anything. The property is gone; its
|
|
||||||
// name is deliberately not repeated here, because a name in a comment is the next thing somebody
|
|
||||||
// tries to set.
|
|
||||||
//
|
|
||||||
// What the lane genuinely verifies is a behaviour contract: a REQUIRES_NEW depth of one needs two
|
|
||||||
// connections per concurrent thread, a saturated pool reports its pending count, and a caller waits
|
|
||||||
// rather than proceeding without a connection. Those are true on any machine, so they need no flag
|
|
||||||
// — and this name does not promise a number nobody measured. A real performance gate needs a
|
|
||||||
// dedicated runner, warmup and sample counts, and recorded thresholds; when that exists it belongs
|
|
||||||
// in a lane of its own rather than behind a boolean on this one.
|
|
||||||
strictTestLanes {
|
|
||||||
lane('jpaPlatformPoolContractTest') {
|
|
||||||
sourceSet = 'jpaPlatformPerformanceTest'
|
|
||||||
description = 'Verifies Hikari pool and REQUIRES_NEW connection behaviour (design §38).'
|
|
||||||
customize = { test -> test.jvmArgs('-Duser.timezone=UTC') }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The JPA release gate (design §41). Aggregates every lane whose absence would let one of the
|
|
||||||
// documented gates in docs/jpa/support-matrix.md pass unverified.
|
|
||||||
tasks.register('jpaPlatformReleaseGate') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Runs every JPA platform lane required for a release (design §41).'
|
|
||||||
dependsOn tasks.named('test')
|
|
||||||
jpaPlatformLanes.keySet().each { String laneName -> dependsOn tasks.named(laneName) }
|
|
||||||
dependsOn tasks.named('jpaPlatformPoolContractTest')
|
|
||||||
}
|
|
||||||
|
|
||||||
// The unit lane reads three files that are not Java sources: the release registry and its two
|
// The unit lane reads three files that are not Java sources: the release registry and its two
|
||||||
// renderings. Without declaring them, Gradle calls the lane up-to-date after a registry demotion or
|
// renderings. Without declaring them, Gradle calls the lane up-to-date after a registry demotion or
|
||||||
@@ -361,7 +165,7 @@ apiSurface {
|
|||||||
|
|
||||||
// The JPA readiness registry describes this platform's lanes and resolves their task paths, so it
|
// The JPA readiness registry describes this platform's lanes and resolves their task paths, so it
|
||||||
// runs with this leaf's `check` rather than with all 62. The task itself is registered by
|
// runs with this leaf's `check` rather than with all 62. The task itself is registered by
|
||||||
// the ca.jpa-qualification plugin from build-qualification, which the root applies.
|
// the ca.jpa-qualification plugin from build-tools, which the root applies.
|
||||||
tasks.named('check') {
|
tasks.named('check') {
|
||||||
dependsOn rootProject.tasks.named('verifyJpaReadinessRegistry')
|
dependsOn rootProject.tasks.named('verifyJpaReadinessRegistry')
|
||||||
}
|
}
|
||||||
|
|||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.persistence.outbox;
|
||||||
|
|
||||||
|
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.CharacterCodingException;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canonical append path for the transport-only outbox cutover.
|
||||||
|
*
|
||||||
|
* <p>The existing {@code outbox_event} row remains the durable authority. Canonical metadata and
|
||||||
|
* exact envelope bytes are stored additively while legacy required columns receive a reversible
|
||||||
|
* compatibility projection. This adapter never opens its own transaction.
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
prefix = "ca-skeleton.outbox",
|
||||||
|
name = {"enabled", "canonical-transport-enabled"},
|
||||||
|
havingValue = "true")
|
||||||
|
final class CanonicalOutboxAppendAdapter implements OutboxAppendPort {
|
||||||
|
|
||||||
|
private final OutboxEventJpaRepository repository;
|
||||||
|
|
||||||
|
CanonicalOutboxAppendAdapter(OutboxEventJpaRepository repository) {
|
||||||
|
this.repository = Objects.requireNonNull(repository, "repository must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void append(ValidatedIntegrationEvent event) {
|
||||||
|
Objects.requireNonNull(event, "event must not be null");
|
||||||
|
byte[] envelopeBytes = event.envelopeBytes();
|
||||||
|
String compatibilityPayload = strictUtf8(envelopeBytes);
|
||||||
|
|
||||||
|
OutboxEventEntity entity = new OutboxEventEntity();
|
||||||
|
|
||||||
|
// Legacy compatibility projection retained while LEGACY_POLLING owns the row lifecycle.
|
||||||
|
entity.setEventId(event.eventId().value());
|
||||||
|
entity.setAggregateId(event.aggregate().aggregateId());
|
||||||
|
entity.setEventType(event.contractId().value());
|
||||||
|
entity.setPayload(compatibilityPayload);
|
||||||
|
entity.setOccurredAt(event.occurredAt());
|
||||||
|
entity.setStatus(OutboxEventStatus.PENDING.name());
|
||||||
|
entity.setAttemptCount(0);
|
||||||
|
entity.setNextAttemptAt(event.occurredAt());
|
||||||
|
entity.setCorrelationId(event.correlationId());
|
||||||
|
entity.setIdempotencyKey(event.eventId().value());
|
||||||
|
|
||||||
|
// Canonical authority for the new platform transport branch.
|
||||||
|
entity.setContractId(event.contractId().value());
|
||||||
|
entity.setEnvelopeVersion(event.envelopeVersion());
|
||||||
|
entity.setPayloadVersion(event.payloadVersion());
|
||||||
|
entity.setLogicalDestination(event.logicalDestinationId().value());
|
||||||
|
entity.setTenantScope(event.aggregate().tenantScope());
|
||||||
|
entity.setAggregateType(event.aggregate().aggregateType());
|
||||||
|
entity.setAggregateSequence(event.order().sequence());
|
||||||
|
entity.setEventIndex(event.order().eventIndex());
|
||||||
|
entity.setPartitionKey(event.partitionKeyText());
|
||||||
|
entity.setEnvelopeBytes(envelopeBytes);
|
||||||
|
entity.setContentType(event.contentType());
|
||||||
|
entity.setSchemaSetHash(event.schemaSetHash().toString());
|
||||||
|
entity.setEnvelopeSha256(event.envelopeSha256().toString());
|
||||||
|
entity.setEnvelopeSchemaHash(event.envelopeSchemaHash().toString());
|
||||||
|
entity.setPayloadSchemaHash(event.payloadSchemaHash().toString());
|
||||||
|
entity.setContractCatalogRevision(event.contractCatalogRevision());
|
||||||
|
entity.setDestinationBindingRevision(event.destinationBindingRevision());
|
||||||
|
entity.setCausationId(event.causationId().orElse(null));
|
||||||
|
|
||||||
|
repository.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String strictUtf8(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
String decoded =
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
.newDecoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPORT)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPORT)
|
||||||
|
.decode(ByteBuffer.wrap(bytes))
|
||||||
|
.toString();
|
||||||
|
if (!Arrays.equals(decoded.getBytes(StandardCharsets.UTF_8), bytes)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"canonical envelope must round-trip as exact UTF-8 for the legacy compatibility projection");
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
} catch (CharacterCodingException exception) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"canonical envelope must be valid UTF-8 for the legacy compatibility projection",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+151
-18
@@ -1,27 +1,43 @@
|
|||||||
package dev.caskeleton.adapter.outbound.persistence.outbox;
|
package dev.caskeleton.adapter.outbound.persistence.outbox;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
||||||
|
import dev.caskeleton.application.messaging.contract.ContractId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.LogicalDestinationId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.Sha256;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateIdentity;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateOrder;
|
||||||
|
import dev.caskeleton.application.messaging.event.EventId;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import dev.caskeleton.application.outbox.CanonicalClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.ClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.LegacyOutboxAppendPort;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
|
||||||
import dev.caskeleton.application.outbox.OutboxEvent;
|
import dev.caskeleton.application.outbox.OutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
||||||
import dev.caskeleton.application.outbox.OutboxStorePort;
|
import dev.caskeleton.application.outbox.OutboxStorePort;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.CharacterCodingException;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA-backed {@link OutboxAppendPort} + {@link OutboxStorePort}. {@link #append} and the claim/mark
|
* JPA-backed {@link LegacyOutboxAppendPort} + {@link OutboxStorePort}. {@link #append} and the
|
||||||
* operations run inside the caller's {@code TransactionPort.inWrite()} boundary and declare no
|
* claim/mark operations run inside the caller's {@code TransactionPort.inWrite()} boundary and
|
||||||
* {@code @Transactional} of their own; {@link #claimBatch} delegates the vendor claim to {@link
|
* declare no {@code @Transactional} of their own; {@link #claimBatch} delegates the vendor claim to
|
||||||
* OutboxClaimRepository}. See README "outbox" for the append/claim/no-@Transactional contracts.
|
* {@link OutboxClaimRepository}. See README "outbox" for the append/claim/no-@Transactional
|
||||||
|
* contracts.
|
||||||
*/
|
*/
|
||||||
@Repository
|
@Repository
|
||||||
public class OutboxStoreAdapter implements OutboxAppendPort, OutboxStorePort {
|
public class OutboxStoreAdapter implements LegacyOutboxAppendPort, OutboxStorePort {
|
||||||
|
|
||||||
private final OutboxEventJpaRepository repository;
|
private final OutboxEventJpaRepository repository;
|
||||||
private final OutboxClaimRepository claimRepository;
|
private final OutboxClaimRepository claimRepository;
|
||||||
@@ -50,7 +66,7 @@ public class OutboxStoreAdapter implements OutboxAppendPort, OutboxStorePort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<OutboxEvent> claimBatch(int batchSize, Instant now, Duration inFlightTimeout) {
|
public List<ClaimedOutboxEvent> claimBatch(int batchSize, Instant now, Duration inFlightTimeout) {
|
||||||
List<OutboxEventEntity> eligible = claimRepository.claimEligible(now, batchSize);
|
List<OutboxEventEntity> eligible = claimRepository.claimEligible(now, batchSize);
|
||||||
return eligible.stream()
|
return eligible.stream()
|
||||||
.map(
|
.map(
|
||||||
@@ -58,7 +74,7 @@ public class OutboxStoreAdapter implements OutboxAppendPort, OutboxStorePort {
|
|||||||
entity.setStatus(OutboxEventStatus.IN_FLIGHT.name());
|
entity.setStatus(OutboxEventStatus.IN_FLIGHT.name());
|
||||||
entity.setAttemptCount(entity.getAttemptCount() + 1);
|
entity.setAttemptCount(entity.getAttemptCount() + 1);
|
||||||
entity.setNextAttemptAt(now.plus(inFlightTimeout));
|
entity.setNextAttemptAt(now.plus(inFlightTimeout));
|
||||||
return toOutboxEvent(entity);
|
return toClaimedOutboxEvent(entity);
|
||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
@@ -127,16 +143,133 @@ public class OutboxStoreAdapter implements OutboxAppendPort, OutboxStorePort {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OutboxEvent toOutboxEvent(OutboxEventEntity e) {
|
private static ClaimedOutboxEvent toClaimedOutboxEvent(OutboxEventEntity entity) {
|
||||||
|
if (!hasAnyCanonicalMetadata(entity)) {
|
||||||
|
return toLegacyOutboxEvent(entity);
|
||||||
|
}
|
||||||
|
if (!hasCompleteCanonicalMetadata(entity)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"outbox row contains partial canonical metadata for eventId=" + entity.getEventId());
|
||||||
|
}
|
||||||
|
return toCanonicalOutboxEvent(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutboxEvent toLegacyOutboxEvent(OutboxEventEntity entity) {
|
||||||
return new OutboxEvent(
|
return new OutboxEvent(
|
||||||
e.getEventId(),
|
entity.getEventId(),
|
||||||
e.getEventType(),
|
entity.getEventType(),
|
||||||
e.getAggregateId(),
|
entity.getAggregateId(),
|
||||||
e.getPayload(),
|
entity.getPayload(),
|
||||||
e.getOccurredAt(),
|
entity.getOccurredAt(),
|
||||||
e.getCorrelationId(),
|
entity.getCorrelationId(),
|
||||||
e.getIdempotencyKey(),
|
entity.getIdempotencyKey(),
|
||||||
OutboxEventStatus.valueOf(e.getStatus()),
|
OutboxEventStatus.valueOf(entity.getStatus()),
|
||||||
e.getAttemptCount());
|
entity.getAttemptCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CanonicalClaimedOutboxEvent toCanonicalOutboxEvent(OutboxEventEntity entity) {
|
||||||
|
byte[] envelopeBytes = entity.getEnvelopeBytes();
|
||||||
|
String compatibilityPayload = strictUtf8(envelopeBytes, entity.getEventId());
|
||||||
|
if (!entity.getEventType().equals(entity.getContractId())
|
||||||
|
|| !entity.getIdempotencyKey().equals(entity.getEventId())
|
||||||
|
|| !entity.getPayload().equals(compatibilityPayload)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"canonical outbox compatibility projection mismatch for eventId=" + entity.getEventId());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ValidatedIntegrationEvent event =
|
||||||
|
new ValidatedIntegrationEvent(
|
||||||
|
new EventId(entity.getEventId()),
|
||||||
|
new ContractId(entity.getContractId()),
|
||||||
|
entity.getEnvelopeVersion(),
|
||||||
|
entity.getPayloadVersion(),
|
||||||
|
new LogicalDestinationId(entity.getLogicalDestination()),
|
||||||
|
new AggregateIdentity(
|
||||||
|
entity.getTenantScope(), entity.getAggregateType(), entity.getAggregateId()),
|
||||||
|
new AggregateOrder(entity.getAggregateSequence(), entity.getEventIndex()),
|
||||||
|
entity.getOccurredAt(),
|
||||||
|
entity.getCorrelationId(),
|
||||||
|
Optional.ofNullable(entity.getCausationId()),
|
||||||
|
entity.getPartitionKey(),
|
||||||
|
entity.getPartitionKey().getBytes(StandardCharsets.US_ASCII),
|
||||||
|
envelopeBytes,
|
||||||
|
entity.getContentType(),
|
||||||
|
digest(entity.getSchemaSetHash(), "schema_set_hash", entity.getEventId()),
|
||||||
|
digest(entity.getEnvelopeSha256(), "envelope_sha256", entity.getEventId()),
|
||||||
|
digest(entity.getEnvelopeSchemaHash(), "envelope_schema_hash", entity.getEventId()),
|
||||||
|
digest(entity.getPayloadSchemaHash(), "payload_schema_hash", entity.getEventId()),
|
||||||
|
entity.getContractCatalogRevision(),
|
||||||
|
entity.getDestinationBindingRevision());
|
||||||
|
return new CanonicalClaimedOutboxEvent(
|
||||||
|
event, OutboxEventStatus.valueOf(entity.getStatus()), entity.getAttemptCount());
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"invalid canonical outbox metadata for eventId=" + entity.getEventId(), exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasAnyCanonicalMetadata(OutboxEventEntity entity) {
|
||||||
|
return entity.getContractId() != null
|
||||||
|
|| entity.getEnvelopeVersion() != null
|
||||||
|
|| entity.getPayloadVersion() != null
|
||||||
|
|| entity.getLogicalDestination() != null
|
||||||
|
|| entity.getTenantScope() != null
|
||||||
|
|| entity.getAggregateType() != null
|
||||||
|
|| entity.getAggregateSequence() != null
|
||||||
|
|| entity.getEventIndex() != null
|
||||||
|
|| entity.getPartitionKey() != null
|
||||||
|
|| entity.getEnvelopeBytes() != null
|
||||||
|
|| entity.getContentType() != null
|
||||||
|
|| entity.getSchemaSetHash() != null
|
||||||
|
|| entity.getEnvelopeSha256() != null
|
||||||
|
|| entity.getEnvelopeSchemaHash() != null
|
||||||
|
|| entity.getPayloadSchemaHash() != null
|
||||||
|
|| entity.getContractCatalogRevision() != null
|
||||||
|
|| entity.getDestinationBindingRevision() != null
|
||||||
|
|| entity.getCausationId() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasCompleteCanonicalMetadata(OutboxEventEntity entity) {
|
||||||
|
return entity.getContractId() != null
|
||||||
|
&& entity.getEnvelopeVersion() != null
|
||||||
|
&& entity.getPayloadVersion() != null
|
||||||
|
&& entity.getLogicalDestination() != null
|
||||||
|
&& entity.getTenantScope() != null
|
||||||
|
&& entity.getAggregateType() != null
|
||||||
|
&& entity.getAggregateSequence() != null
|
||||||
|
&& entity.getEventIndex() != null
|
||||||
|
&& entity.getPartitionKey() != null
|
||||||
|
&& entity.getEnvelopeBytes() != null
|
||||||
|
&& entity.getContentType() != null
|
||||||
|
&& entity.getSchemaSetHash() != null
|
||||||
|
&& entity.getEnvelopeSha256() != null
|
||||||
|
&& entity.getEnvelopeSchemaHash() != null
|
||||||
|
&& entity.getPayloadSchemaHash() != null
|
||||||
|
&& entity.getContractCatalogRevision() != null
|
||||||
|
&& entity.getDestinationBindingRevision() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Sha256 digest(String hex, String field, String eventId) {
|
||||||
|
try {
|
||||||
|
return new Sha256(HexFormat.of().parseHex(hex));
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"invalid " + field + " for canonical outbox eventId=" + eventId, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String strictUtf8(byte[] bytes, String eventId) {
|
||||||
|
try {
|
||||||
|
return StandardCharsets.UTF_8
|
||||||
|
.newDecoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPORT)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPORT)
|
||||||
|
.decode(ByteBuffer.wrap(bytes))
|
||||||
|
.toString();
|
||||||
|
} catch (CharacterCodingException exception) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"canonical outbox envelope is not valid UTF-8 for eventId=" + eventId, exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+205
-6
@@ -9,17 +9,17 @@ import org.hibernate.annotations.JdbcTypeCode;
|
|||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPA row for the {@code outbox_event} table; schema owned by Flyway ({@code
|
* JPA row for the {@code outbox_event} table; schema owned by Flyway ({@code V3__outbox_event.sql}
|
||||||
* V3__outbox_event.sql}). Does not extend {@code AuditableEntity} (infra record, not a domain
|
* + additive canonical metadata in {@code V13__outbox_canonical_transport_compatibility.sql}). Does
|
||||||
* aggregate) and exposes mutating setters on purpose (relay transitions state in-place). See README
|
* not extend {@code AuditableEntity} (infra record, not a domain aggregate) and exposes mutating
|
||||||
* "outbox" for both.
|
* setters on purpose (relay transitions state in-place).
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "outbox_event")
|
@Table(name = "outbox_event")
|
||||||
public class OutboxEventEntity {
|
public class OutboxEventEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "event_id", nullable = false, length = 64, updatable = false)
|
@Column(name = "event_id", nullable = false, length = 96, updatable = false)
|
||||||
private String eventId;
|
private String eventId;
|
||||||
|
|
||||||
@Column(name = "aggregate_id", nullable = false, length = 256, updatable = false)
|
@Column(name = "aggregate_id", nullable = false, length = 256, updatable = false)
|
||||||
@@ -46,12 +46,67 @@ public class OutboxEventEntity {
|
|||||||
@Column(name = "next_attempt_at", nullable = false)
|
@Column(name = "next_attempt_at", nullable = false)
|
||||||
private Instant nextAttemptAt;
|
private Instant nextAttemptAt;
|
||||||
|
|
||||||
@Column(name = "correlation_id", nullable = false, length = 64, updatable = false)
|
@Column(name = "correlation_id", nullable = false, length = 128, updatable = false)
|
||||||
private String correlationId;
|
private String correlationId;
|
||||||
|
|
||||||
@Column(name = "idempotency_key", nullable = false, length = 256, updatable = false)
|
@Column(name = "idempotency_key", nullable = false, length = 256, updatable = false)
|
||||||
private String idempotencyKey;
|
private String idempotencyKey;
|
||||||
|
|
||||||
|
// V13 canonical transport-only compatibility columns. Null as a set means an R0 legacy row.
|
||||||
|
@Column(name = "contract_id", length = 160, updatable = false)
|
||||||
|
private String contractId;
|
||||||
|
|
||||||
|
@Column(name = "envelope_version", updatable = false)
|
||||||
|
private Integer envelopeVersion;
|
||||||
|
|
||||||
|
@Column(name = "payload_version", updatable = false)
|
||||||
|
private Integer payloadVersion;
|
||||||
|
|
||||||
|
@Column(name = "logical_destination", length = 96, updatable = false)
|
||||||
|
private String logicalDestination;
|
||||||
|
|
||||||
|
@Column(name = "tenant_scope", length = 96, updatable = false)
|
||||||
|
private String tenantScope;
|
||||||
|
|
||||||
|
@Column(name = "aggregate_type", length = 64, updatable = false)
|
||||||
|
private String aggregateType;
|
||||||
|
|
||||||
|
@Column(name = "aggregate_sequence", updatable = false)
|
||||||
|
private Long aggregateSequence;
|
||||||
|
|
||||||
|
@Column(name = "event_index", updatable = false)
|
||||||
|
private Integer eventIndex;
|
||||||
|
|
||||||
|
@Column(name = "partition_key", length = 64, updatable = false)
|
||||||
|
private String partitionKey;
|
||||||
|
|
||||||
|
@Column(name = "envelope_bytes", updatable = false)
|
||||||
|
private byte[] envelopeBytes;
|
||||||
|
|
||||||
|
@Column(name = "content_type", length = 96, updatable = false)
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
|
@Column(name = "schema_set_hash", length = 64, updatable = false)
|
||||||
|
private String schemaSetHash;
|
||||||
|
|
||||||
|
@Column(name = "envelope_sha256", length = 64, updatable = false)
|
||||||
|
private String envelopeSha256;
|
||||||
|
|
||||||
|
@Column(name = "envelope_schema_hash", length = 64, updatable = false)
|
||||||
|
private String envelopeSchemaHash;
|
||||||
|
|
||||||
|
@Column(name = "payload_schema_hash", length = 64, updatable = false)
|
||||||
|
private String payloadSchemaHash;
|
||||||
|
|
||||||
|
@Column(name = "contract_catalog_revision", length = 96, updatable = false)
|
||||||
|
private String contractCatalogRevision;
|
||||||
|
|
||||||
|
@Column(name = "destination_binding_revision", length = 96, updatable = false)
|
||||||
|
private String destinationBindingRevision;
|
||||||
|
|
||||||
|
@Column(name = "causation_id", length = 128, updatable = false)
|
||||||
|
private String causationId;
|
||||||
|
|
||||||
/** JPA no-arg constructor. */
|
/** JPA no-arg constructor. */
|
||||||
public OutboxEventEntity() {}
|
public OutboxEventEntity() {}
|
||||||
|
|
||||||
@@ -134,4 +189,148 @@ public class OutboxEventEntity {
|
|||||||
public void setIdempotencyKey(String idempotencyKey) {
|
public void setIdempotencyKey(String idempotencyKey) {
|
||||||
this.idempotencyKey = idempotencyKey;
|
this.idempotencyKey = idempotencyKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContractId() {
|
||||||
|
return contractId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContractId(String contractId) {
|
||||||
|
this.contractId = contractId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getEnvelopeVersion() {
|
||||||
|
return envelopeVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvelopeVersion(Integer envelopeVersion) {
|
||||||
|
this.envelopeVersion = envelopeVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPayloadVersion() {
|
||||||
|
return payloadVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadVersion(Integer payloadVersion) {
|
||||||
|
this.payloadVersion = payloadVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogicalDestination() {
|
||||||
|
return logicalDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogicalDestination(String logicalDestination) {
|
||||||
|
this.logicalDestination = logicalDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTenantScope() {
|
||||||
|
return tenantScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTenantScope(String tenantScope) {
|
||||||
|
this.tenantScope = tenantScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAggregateType() {
|
||||||
|
return aggregateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAggregateType(String aggregateType) {
|
||||||
|
this.aggregateType = aggregateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getAggregateSequence() {
|
||||||
|
return aggregateSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAggregateSequence(Long aggregateSequence) {
|
||||||
|
this.aggregateSequence = aggregateSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getEventIndex() {
|
||||||
|
return eventIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventIndex(Integer eventIndex) {
|
||||||
|
this.eventIndex = eventIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPartitionKey() {
|
||||||
|
return partitionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartitionKey(String partitionKey) {
|
||||||
|
this.partitionKey = partitionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEnvelopeBytes() {
|
||||||
|
return envelopeBytes == null ? null : envelopeBytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvelopeBytes(byte[] envelopeBytes) {
|
||||||
|
this.envelopeBytes = envelopeBytes == null ? null : envelopeBytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSchemaSetHash() {
|
||||||
|
return schemaSetHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchemaSetHash(String schemaSetHash) {
|
||||||
|
this.schemaSetHash = schemaSetHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnvelopeSha256() {
|
||||||
|
return envelopeSha256;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvelopeSha256(String envelopeSha256) {
|
||||||
|
this.envelopeSha256 = envelopeSha256;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnvelopeSchemaHash() {
|
||||||
|
return envelopeSchemaHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnvelopeSchemaHash(String envelopeSchemaHash) {
|
||||||
|
this.envelopeSchemaHash = envelopeSchemaHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayloadSchemaHash() {
|
||||||
|
return payloadSchemaHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadSchemaHash(String payloadSchemaHash) {
|
||||||
|
this.payloadSchemaHash = payloadSchemaHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContractCatalogRevision() {
|
||||||
|
return contractCatalogRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContractCatalogRevision(String contractCatalogRevision) {
|
||||||
|
this.contractCatalogRevision = contractCatalogRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationBindingRevision() {
|
||||||
|
return destinationBindingRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestinationBindingRevision(String destinationBindingRevision) {
|
||||||
|
this.destinationBindingRevision = destinationBindingRevision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCausationId() {
|
||||||
|
return causationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCausationId(String causationId) {
|
||||||
|
this.causationId = causationId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
-- MSG-015 transport-only cutover: preserve canonical messaging metadata in the existing
|
||||||
|
-- legacy outbox_event authority. This migration is additive for old rows. POLLING_V2 is not
|
||||||
|
-- activated here and the legacy status/claim columns remain authoritative.
|
||||||
|
|
||||||
|
ALTER TABLE outbox_event
|
||||||
|
ALTER COLUMN event_id TYPE varchar(96),
|
||||||
|
ALTER COLUMN correlation_id TYPE varchar(128);
|
||||||
|
|
||||||
|
ALTER TABLE outbox_event
|
||||||
|
ADD COLUMN contract_id varchar(160),
|
||||||
|
ADD COLUMN envelope_version integer,
|
||||||
|
ADD COLUMN payload_version integer,
|
||||||
|
ADD COLUMN logical_destination varchar(96),
|
||||||
|
ADD COLUMN tenant_scope varchar(96),
|
||||||
|
ADD COLUMN aggregate_type varchar(64),
|
||||||
|
ADD COLUMN aggregate_sequence bigint,
|
||||||
|
ADD COLUMN event_index integer,
|
||||||
|
ADD COLUMN partition_key char(64),
|
||||||
|
ADD COLUMN envelope_bytes bytea,
|
||||||
|
ADD COLUMN content_type varchar(96),
|
||||||
|
ADD COLUMN schema_set_hash char(64),
|
||||||
|
ADD COLUMN envelope_sha256 char(64),
|
||||||
|
ADD COLUMN envelope_schema_hash char(64),
|
||||||
|
ADD COLUMN payload_schema_hash char(64),
|
||||||
|
ADD COLUMN contract_catalog_revision varchar(96),
|
||||||
|
ADD COLUMN destination_binding_revision varchar(96),
|
||||||
|
ADD COLUMN causation_id varchar(128);
|
||||||
|
|
||||||
|
ALTER TABLE outbox_event
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_shape CHECK (
|
||||||
|
(
|
||||||
|
envelope_version IS NULL
|
||||||
|
AND contract_id IS NULL
|
||||||
|
AND payload_version IS NULL
|
||||||
|
AND logical_destination IS NULL
|
||||||
|
AND tenant_scope IS NULL
|
||||||
|
AND aggregate_type IS NULL
|
||||||
|
AND aggregate_sequence IS NULL
|
||||||
|
AND event_index IS NULL
|
||||||
|
AND partition_key IS NULL
|
||||||
|
AND envelope_bytes IS NULL
|
||||||
|
AND content_type IS NULL
|
||||||
|
AND schema_set_hash IS NULL
|
||||||
|
AND envelope_sha256 IS NULL
|
||||||
|
AND envelope_schema_hash IS NULL
|
||||||
|
AND payload_schema_hash IS NULL
|
||||||
|
AND contract_catalog_revision IS NULL
|
||||||
|
AND destination_binding_revision IS NULL
|
||||||
|
AND causation_id IS NULL
|
||||||
|
)
|
||||||
|
OR
|
||||||
|
(
|
||||||
|
envelope_version IS NOT NULL
|
||||||
|
AND contract_id IS NOT NULL
|
||||||
|
AND payload_version IS NOT NULL
|
||||||
|
AND logical_destination IS NOT NULL
|
||||||
|
AND tenant_scope IS NOT NULL
|
||||||
|
AND aggregate_type IS NOT NULL
|
||||||
|
AND aggregate_sequence IS NOT NULL
|
||||||
|
AND event_index IS NOT NULL
|
||||||
|
AND partition_key IS NOT NULL
|
||||||
|
AND envelope_bytes IS NOT NULL
|
||||||
|
AND content_type IS NOT NULL
|
||||||
|
AND schema_set_hash IS NOT NULL
|
||||||
|
AND envelope_sha256 IS NOT NULL
|
||||||
|
AND envelope_schema_hash IS NOT NULL
|
||||||
|
AND payload_schema_hash IS NOT NULL
|
||||||
|
AND contract_catalog_revision IS NOT NULL
|
||||||
|
AND destination_binding_revision IS NOT NULL
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_versions CHECK (
|
||||||
|
envelope_version IS NULL
|
||||||
|
OR (
|
||||||
|
envelope_version > 0
|
||||||
|
AND payload_version > 0
|
||||||
|
AND aggregate_sequence > 0
|
||||||
|
AND event_index >= 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_partition_key CHECK (
|
||||||
|
partition_key IS NULL OR partition_key ~ '^[0-9a-f]{64}$'
|
||||||
|
),
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_hashes CHECK (
|
||||||
|
schema_set_hash IS NULL
|
||||||
|
OR (
|
||||||
|
schema_set_hash ~ '^[0-9a-f]{64}$'
|
||||||
|
AND envelope_sha256 ~ '^[0-9a-f]{64}$'
|
||||||
|
AND envelope_schema_hash ~ '^[0-9a-f]{64}$'
|
||||||
|
AND payload_schema_hash ~ '^[0-9a-f]{64}$'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_envelope_bytes CHECK (
|
||||||
|
envelope_bytes IS NULL OR octet_length(envelope_bytes) > 0
|
||||||
|
),
|
||||||
|
ADD CONSTRAINT ck_outbox_event_canonical_required_text CHECK (
|
||||||
|
contract_id IS NULL
|
||||||
|
OR (
|
||||||
|
btrim(contract_id) <> ''
|
||||||
|
AND btrim(logical_destination) <> ''
|
||||||
|
AND btrim(tenant_scope) <> ''
|
||||||
|
AND btrim(aggregate_type) <> ''
|
||||||
|
AND btrim(content_type) <> ''
|
||||||
|
AND btrim(contract_catalog_revision) <> ''
|
||||||
|
AND btrim(destination_binding_revision) <> ''
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN outbox_event.envelope_bytes IS
|
||||||
|
'Exact canonical ValidatedIntegrationEvent envelope bytes. Never reconstructed from payload.';
|
||||||
|
COMMENT ON COLUMN outbox_event.payload IS
|
||||||
|
'Legacy compatibility text projection. Canonical authority, when present, is envelope_bytes.';
|
||||||
+155
-3
@@ -3,10 +3,14 @@ package dev.caskeleton.adapter.outbound.persistence.readiness;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
@@ -44,7 +48,7 @@ class PostgreSqlMigrationIntegrationTest {
|
|||||||
.migrate();
|
.migrate();
|
||||||
|
|
||||||
assertThat(appliedVersions(postgres, "flyway_schema_history"))
|
assertThat(appliedVersions(postgres, "flyway_schema_history"))
|
||||||
.containsExactly("1", "3", "4", "5", "6");
|
.containsExactly("1", "3", "4", "5", "6", "9", "10", "11", "12", "13");
|
||||||
|
|
||||||
Flyway coreStream =
|
Flyway coreStream =
|
||||||
Flyway.configure()
|
Flyway.configure()
|
||||||
@@ -59,7 +63,7 @@ class PostgreSqlMigrationIntegrationTest {
|
|||||||
coreStream.baseline();
|
coreStream.baseline();
|
||||||
coreStream.migrate();
|
coreStream.migrate();
|
||||||
|
|
||||||
assertThat(appliedVersions(postgres, "flyway_jpa_core_history")).containsExactly("0", "1");
|
assertThat(appliedVersions(postgres, "flyway_jpa_core_history")).containsExactly("0", "1", "2");
|
||||||
try (Connection connection = postgres.connection();
|
try (Connection connection = postgres.connection();
|
||||||
Statement statement = connection.createStatement();
|
Statement statement = connection.createStatement();
|
||||||
ResultSet result =
|
ResultSet result =
|
||||||
@@ -86,7 +90,7 @@ class PostgreSqlMigrationIntegrationTest {
|
|||||||
.load()
|
.load()
|
||||||
.migrate();
|
.migrate();
|
||||||
|
|
||||||
assertThat(appliedVersions(fresh, "flyway_jpa_core_history")).containsExactly("1");
|
assertThat(appliedVersions(fresh, "flyway_jpa_core_history")).containsExactly("1", "2");
|
||||||
assertThat(
|
assertThat(
|
||||||
singleValue(
|
singleValue(
|
||||||
fresh,
|
fresh,
|
||||||
@@ -197,6 +201,154 @@ class PostgreSqlMigrationIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void outboxCanonicalCompatibilityMigrationPreservesLegacyAndExactEnvelopeBytes()
|
||||||
|
throws Exception {
|
||||||
|
try (PostgreSqlReadinessSupport database = PostgreSqlReadinessSupport.start()) {
|
||||||
|
Flyway.configure()
|
||||||
|
.dataSource(database.dataSource())
|
||||||
|
.locations("classpath:db/migration/postgresql")
|
||||||
|
.table("flyway_schema_history")
|
||||||
|
.load()
|
||||||
|
.migrate();
|
||||||
|
|
||||||
|
assertColumn(database, "event_id", "character varying", 96);
|
||||||
|
assertColumn(database, "correlation_id", "character varying", 128);
|
||||||
|
assertColumn(database, "envelope_bytes", "bytea", null);
|
||||||
|
assertColumn(database, "tenant_scope", "character varying", 96);
|
||||||
|
|
||||||
|
database.execute(
|
||||||
|
"""
|
||||||
|
insert into outbox_event (
|
||||||
|
event_id, aggregate_id, event_type, payload, occurred_at,
|
||||||
|
status, attempt_count, next_attempt_at, correlation_id, idempotency_key
|
||||||
|
) values (
|
||||||
|
'legacy-1', 'aggregate-1', 'LegacyEvent', '{}', clock_timestamp(),
|
||||||
|
'PENDING', 0, clock_timestamp(), 'correlation-1', 'legacy-1'
|
||||||
|
)
|
||||||
|
""");
|
||||||
|
|
||||||
|
assertThatThrownBy(
|
||||||
|
() ->
|
||||||
|
database.execute(
|
||||||
|
"""
|
||||||
|
insert into outbox_event (
|
||||||
|
event_id, aggregate_id, event_type, payload, occurred_at,
|
||||||
|
status, attempt_count, next_attempt_at, correlation_id, idempotency_key,
|
||||||
|
envelope_version
|
||||||
|
) values (
|
||||||
|
'partial-1', 'aggregate-1', 'PartialEvent', '{}', clock_timestamp(),
|
||||||
|
'PENDING', 0, clock_timestamp(), 'correlation-1', 'partial-1',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
"""))
|
||||||
|
.isInstanceOf(java.sql.SQLException.class)
|
||||||
|
.hasMessageContaining("ck_outbox_event_canonical_shape");
|
||||||
|
|
||||||
|
byte[] exactEnvelope =
|
||||||
|
"{\"event\":\"canonical\",\"value\":\"한글\"}".getBytes(StandardCharsets.UTF_8);
|
||||||
|
insertCanonical(database, exactEnvelope);
|
||||||
|
|
||||||
|
try (Connection connection = database.connection();
|
||||||
|
PreparedStatement statement =
|
||||||
|
connection.prepareStatement(
|
||||||
|
"""
|
||||||
|
select envelope_bytes, tenant_scope, contract_id, aggregate_sequence, event_index
|
||||||
|
from outbox_event
|
||||||
|
where event_id = 'canonical-1'
|
||||||
|
""");
|
||||||
|
ResultSet result = statement.executeQuery()) {
|
||||||
|
assertThat(result.next()).isTrue();
|
||||||
|
assertThat(result.getBytes("envelope_bytes")).containsExactly(exactEnvelope);
|
||||||
|
assertThat(result.getString("tenant_scope")).isEqualTo("tenant-a");
|
||||||
|
assertThat(result.getString("contract_id")).isEqualTo("portfolio.work-log-changed");
|
||||||
|
assertThat(result.getLong("aggregate_sequence")).isEqualTo(7L);
|
||||||
|
assertThat(result.getInt("event_index")).isEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void insertCanonical(PostgreSqlReadinessSupport database, byte[] exactEnvelope)
|
||||||
|
throws Exception {
|
||||||
|
try (Connection connection = database.connection();
|
||||||
|
PreparedStatement statement =
|
||||||
|
connection.prepareStatement(
|
||||||
|
"""
|
||||||
|
insert into outbox_event (
|
||||||
|
event_id, aggregate_id, event_type, payload, occurred_at,
|
||||||
|
status, attempt_count, next_attempt_at, correlation_id, idempotency_key,
|
||||||
|
contract_id, envelope_version, payload_version, logical_destination,
|
||||||
|
tenant_scope, aggregate_type, aggregate_sequence, event_index,
|
||||||
|
partition_key, envelope_bytes, content_type, schema_set_hash,
|
||||||
|
envelope_sha256, envelope_schema_hash, payload_schema_hash,
|
||||||
|
contract_catalog_revision, destination_binding_revision, causation_id
|
||||||
|
) values (
|
||||||
|
?, ?, ?, ?, ?, 'PENDING', 0, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||||
|
)
|
||||||
|
""")) {
|
||||||
|
String hexA = "a".repeat(64);
|
||||||
|
String hexB = "b".repeat(64);
|
||||||
|
String hexC = "c".repeat(64);
|
||||||
|
String hexD = "d".repeat(64);
|
||||||
|
String partitionKey = "1".repeat(64);
|
||||||
|
OffsetDateTime occurredAt = OffsetDateTime.of(2026, 9, 18, 3, 0, 0, 0, ZoneOffset.UTC);
|
||||||
|
int index = 1;
|
||||||
|
statement.setString(index++, "canonical-1");
|
||||||
|
statement.setString(index++, "work-log-42");
|
||||||
|
statement.setString(index++, "portfolio.work-log-changed");
|
||||||
|
statement.setString(index++, new String(exactEnvelope, StandardCharsets.UTF_8));
|
||||||
|
statement.setObject(index++, occurredAt);
|
||||||
|
statement.setObject(index++, occurredAt);
|
||||||
|
statement.setString(index++, "correlation-1");
|
||||||
|
statement.setString(index++, "canonical-1");
|
||||||
|
statement.setString(index++, "portfolio.work-log-changed");
|
||||||
|
statement.setInt(index++, 1);
|
||||||
|
statement.setInt(index++, 3);
|
||||||
|
statement.setString(index++, "portfolio-events");
|
||||||
|
statement.setString(index++, "tenant-a");
|
||||||
|
statement.setString(index++, "work-log");
|
||||||
|
statement.setLong(index++, 7L);
|
||||||
|
statement.setInt(index++, 1);
|
||||||
|
statement.setString(index++, partitionKey);
|
||||||
|
statement.setBytes(index++, exactEnvelope);
|
||||||
|
statement.setString(index++, "application/json");
|
||||||
|
statement.setString(index++, hexA);
|
||||||
|
statement.setString(index++, hexB);
|
||||||
|
statement.setString(index++, hexC);
|
||||||
|
statement.setString(index++, hexD);
|
||||||
|
statement.setString(index++, "catalog-2026-09");
|
||||||
|
statement.setString(index++, "binding-2026-09");
|
||||||
|
statement.setString(index, "01994e11-4d88-7000-8000-000000000001");
|
||||||
|
assertThat(statement.executeUpdate()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertColumn(
|
||||||
|
PostgreSqlReadinessSupport database, String columnName, String dataType, Integer length)
|
||||||
|
throws Exception {
|
||||||
|
try (Connection connection = database.connection();
|
||||||
|
PreparedStatement statement =
|
||||||
|
connection.prepareStatement(
|
||||||
|
"""
|
||||||
|
select data_type, character_maximum_length
|
||||||
|
from information_schema.columns
|
||||||
|
where table_schema = 'public'
|
||||||
|
and table_name = 'outbox_event'
|
||||||
|
and column_name = ?
|
||||||
|
""")) {
|
||||||
|
statement.setString(1, columnName);
|
||||||
|
try (ResultSet result = statement.executeQuery()) {
|
||||||
|
assertThat(result.next()).as("column %s exists", columnName).isTrue();
|
||||||
|
assertThat(result.getString("data_type")).isEqualTo(dataType);
|
||||||
|
if (length != null) {
|
||||||
|
assertThat(result.getInt("character_maximum_length")).isEqualTo(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static List<String> appliedVersions(
|
private static List<String> appliedVersions(
|
||||||
PostgreSqlReadinessSupport database, String historyTable) throws Exception {
|
PostgreSqlReadinessSupport database, String historyTable) throws Exception {
|
||||||
List<String> versions = new ArrayList<>();
|
List<String> versions = new ArrayList<>();
|
||||||
|
|||||||
+5
@@ -116,6 +116,11 @@ final class PostgreSqlReadinessSupport implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String dockerHost() {
|
||||||
|
assertDockerAvailable();
|
||||||
|
return DockerClientFactory.instance().dockerHostIpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
HikariDataSource dataSource() {
|
HikariDataSource dataSource() {
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-11
@@ -23,7 +23,7 @@ class PostgreSqlSecurityBaselineIntegrationTest {
|
|||||||
static void startPostgreSql() throws Exception {
|
static void startPostgreSql() throws Exception {
|
||||||
PostgreSqlReadinessSupport.assertDockerAvailable();
|
PostgreSqlReadinessSupport.assertDockerAvailable();
|
||||||
postgres = PostgreSqlReadinessSupport.start();
|
postgres = PostgreSqlReadinessSupport.start();
|
||||||
trustedTls = PostgreSqlTlsMaterial.generate(false);
|
trustedTls = PostgreSqlTlsMaterial.generate(false, PostgreSqlReadinessSupport.dockerHost());
|
||||||
tlsPostgres = PostgreSqlReadinessSupport.startTls(trustedTls);
|
tlsPostgres = PostgreSqlReadinessSupport.startTls(trustedTls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,31 +120,37 @@ class PostgreSqlSecurityBaselineIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void verifyFullAcceptsTrustedHostAndRejectsHostnameMismatchAndUntrustedCertificate()
|
void verifyFullAcceptsTrustedHostAndRejectsHostnameMismatchAndUntrustedCertificate()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try (Connection connection =
|
String dockerHost = PostgreSqlReadinessSupport.dockerHost();
|
||||||
tlsPostgres.tlsConnection("localhost", trustedTls.caCertificate());
|
try (Connection connection = tlsPostgres.tlsConnection(dockerHost, trustedTls.caCertificate());
|
||||||
Statement statement = connection.createStatement()) {
|
Statement statement = connection.createStatement()) {
|
||||||
assertThat(singleValue(statement, "select ssl from pg_stat_ssl where pid = pg_backend_pid()"))
|
assertThat(singleValue(statement, "select ssl from pg_stat_ssl where pid = pg_backend_pid()"))
|
||||||
.isEqualTo("t");
|
.isEqualTo("t");
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThatThrownBy(
|
try (PostgreSqlTlsMaterial wrongHostTls =
|
||||||
() -> tlsPostgres.tlsConnection("127.0.0.1", trustedTls.caCertificate()).close())
|
PostgreSqlTlsMaterial.generate(false, "jpa-readiness.invalid");
|
||||||
.isInstanceOf(SQLException.class);
|
PostgreSqlReadinessSupport wrongHostServer =
|
||||||
|
PostgreSqlReadinessSupport.startTls(wrongHostTls)) {
|
||||||
try (PostgreSqlTlsMaterial untrustedTls = PostgreSqlTlsMaterial.generate(false)) {
|
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() -> tlsPostgres.tlsConnection("localhost", untrustedTls.caCertificate()).close())
|
() -> wrongHostServer.tlsConnection(dockerHost, wrongHostTls.caCertificate()).close())
|
||||||
|
.isInstanceOf(SQLException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (PostgreSqlTlsMaterial untrustedTls = PostgreSqlTlsMaterial.generate(false, dockerHost)) {
|
||||||
|
assertThatThrownBy(
|
||||||
|
() -> tlsPostgres.tlsConnection(dockerHost, untrustedTls.caCertificate()).close())
|
||||||
.isInstanceOf(SQLException.class);
|
.isInstanceOf(SQLException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyFullRejectsAnExpiredServerCertificate() throws Exception {
|
void verifyFullRejectsAnExpiredServerCertificate() throws Exception {
|
||||||
try (PostgreSqlTlsMaterial expiredTls = PostgreSqlTlsMaterial.generate(true);
|
String dockerHost = PostgreSqlReadinessSupport.dockerHost();
|
||||||
|
try (PostgreSqlTlsMaterial expiredTls = PostgreSqlTlsMaterial.generate(true, dockerHost);
|
||||||
PostgreSqlReadinessSupport expiredServer =
|
PostgreSqlReadinessSupport expiredServer =
|
||||||
PostgreSqlReadinessSupport.startTls(expiredTls)) {
|
PostgreSqlReadinessSupport.startTls(expiredTls)) {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() -> expiredServer.tlsConnection("localhost", expiredTls.caCertificate()).close())
|
() -> expiredServer.tlsConnection(dockerHost, expiredTls.caCertificate()).close())
|
||||||
.isInstanceOf(SQLException.class);
|
.isInstanceOf(SQLException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -24,6 +24,11 @@ final class PostgreSqlTlsMaterial implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PostgreSqlTlsMaterial generate(boolean expired) throws IOException, InterruptedException {
|
static PostgreSqlTlsMaterial generate(boolean expired) throws IOException, InterruptedException {
|
||||||
|
return generate(expired, "localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PostgreSqlTlsMaterial generate(boolean expired, String certificateHost)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
Path directory = Files.createTempDirectory("jpa-postgresql-tls-");
|
Path directory = Files.createTempDirectory("jpa-postgresql-tls-");
|
||||||
Path caKey = directory.resolve("ca.key");
|
Path caKey = directory.resolve("ca.key");
|
||||||
Path caCertificate = directory.resolve("ca.crt");
|
Path caCertificate = directory.resolve("ca.crt");
|
||||||
@@ -59,9 +64,9 @@ final class PostgreSqlTlsMaterial implements AutoCloseable {
|
|||||||
"-out",
|
"-out",
|
||||||
serverRequest.toString(),
|
serverRequest.toString(),
|
||||||
"-subj",
|
"-subj",
|
||||||
"/CN=localhost",
|
"/CN=" + certificateHost,
|
||||||
"-addext",
|
"-addext",
|
||||||
"subjectAltName=DNS:localhost"));
|
"subjectAltName=" + subjectAlternativeName(certificateHost)));
|
||||||
run(
|
run(
|
||||||
List.of(
|
List.of(
|
||||||
"openssl",
|
"openssl",
|
||||||
@@ -96,6 +101,11 @@ final class PostgreSqlTlsMaterial implements AutoCloseable {
|
|||||||
return serverPrivateKey;
|
return serverPrivateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String subjectAlternativeName(String host) {
|
||||||
|
boolean numericAddress = host.contains(":") || host.matches("\\d{1,3}(?:\\.\\d{1,3}){3}");
|
||||||
|
return (numericAddress ? "IP:" : "DNS:") + host;
|
||||||
|
}
|
||||||
|
|
||||||
private static void run(List<String> command) throws IOException, InterruptedException {
|
private static void run(List<String> command) throws IOException, InterruptedException {
|
||||||
Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
|
Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
|
||||||
String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
|||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.persistence.outbox;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
class CanonicalOutboxAppendAdapterActivationTest {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner runner =
|
||||||
|
new ApplicationContextRunner().withUserConfiguration(TestConfig.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canonicalFlagAloneDoesNotExposeAppendWhenOutboxCapabilityIsOff() {
|
||||||
|
runner
|
||||||
|
.withPropertyValues(
|
||||||
|
"ca-skeleton.outbox.enabled=false",
|
||||||
|
"ca-skeleton.outbox.canonical-transport-enabled=true")
|
||||||
|
.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).doesNotHaveBean(OutboxAppendPort.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void bothOutboxAndCanonicalFlagsExposeExactlyOneCanonicalAppendPort() {
|
||||||
|
runner
|
||||||
|
.withPropertyValues(
|
||||||
|
"ca-skeleton.outbox.enabled=true",
|
||||||
|
"ca-skeleton.outbox.canonical-transport-enabled=true")
|
||||||
|
.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).hasSingleBean(OutboxAppendPort.class);
|
||||||
|
assertThat(context.getBean(OutboxAppendPort.class))
|
||||||
|
.isInstanceOf(CanonicalOutboxAppendAdapter.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultOffDoesNotExposeCanonicalAppendPort() {
|
||||||
|
runner.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).doesNotHaveBean(OutboxAppendPort.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(CanonicalOutboxAppendAdapter.class)
|
||||||
|
static class TestConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
OutboxEventJpaRepository outboxEventJpaRepository() {
|
||||||
|
return mock(OutboxEventJpaRepository.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package dev.caskeleton.adapter.outbound.persistence.outbox;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.ArgumentCaptor.forClass;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
|
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
||||||
|
import dev.caskeleton.application.messaging.contract.ContractId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.LogicalDestinationId;
|
||||||
|
import dev.caskeleton.application.messaging.contract.Sha256;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateIdentity;
|
||||||
|
import dev.caskeleton.application.messaging.event.AggregateOrder;
|
||||||
|
import dev.caskeleton.application.messaging.event.EventId;
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
class CanonicalOutboxAppendAdapterTest {
|
||||||
|
|
||||||
|
private static final Instant OCCURRED_AT = Instant.parse("2026-09-18T03:00:00Z");
|
||||||
|
|
||||||
|
private final OutboxEventJpaRepository repository = mock(OutboxEventJpaRepository.class);
|
||||||
|
private final CanonicalOutboxAppendAdapter adapter = new CanonicalOutboxAppendAdapter(repository);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appendPersistsExactCanonicalMetadataAndLegacyCompatibilityProjection() {
|
||||||
|
byte[] exactEnvelope =
|
||||||
|
"{\"event\":\"canonical\",\"value\":\"한글\"}".getBytes(StandardCharsets.UTF_8);
|
||||||
|
ValidatedIntegrationEvent event = event(exactEnvelope);
|
||||||
|
|
||||||
|
adapter.append(event);
|
||||||
|
|
||||||
|
ArgumentCaptor<OutboxEventEntity> saved = forClass(OutboxEventEntity.class);
|
||||||
|
verify(repository).save(saved.capture());
|
||||||
|
OutboxEventEntity entity = saved.getValue();
|
||||||
|
|
||||||
|
assertThat(entity.getEventId()).isEqualTo("01994e11-4d88-7000-8000-000000000001");
|
||||||
|
assertThat(entity.getEventType()).isEqualTo("portfolio.worklog.reserved");
|
||||||
|
assertThat(entity.getAggregateId()).isEqualTo("worklog-42");
|
||||||
|
assertThat(entity.getPayload()).isEqualTo(new String(exactEnvelope, StandardCharsets.UTF_8));
|
||||||
|
assertThat(entity.getOccurredAt()).isEqualTo(OCCURRED_AT);
|
||||||
|
assertThat(entity.getStatus()).isEqualTo("PENDING");
|
||||||
|
assertThat(entity.getAttemptCount()).isZero();
|
||||||
|
assertThat(entity.getNextAttemptAt()).isEqualTo(OCCURRED_AT);
|
||||||
|
assertThat(entity.getCorrelationId()).isEqualTo("corr-1");
|
||||||
|
assertThat(entity.getIdempotencyKey()).isEqualTo(event.eventId().value());
|
||||||
|
|
||||||
|
assertThat(entity.getContractId()).isEqualTo("portfolio.worklog.reserved");
|
||||||
|
assertThat(entity.getEnvelopeVersion()).isEqualTo(1);
|
||||||
|
assertThat(entity.getPayloadVersion()).isEqualTo(3);
|
||||||
|
assertThat(entity.getLogicalDestination()).isEqualTo("portfolio-domain-events");
|
||||||
|
assertThat(entity.getTenantScope()).isEqualTo("tenant-a");
|
||||||
|
assertThat(entity.getAggregateType()).isEqualTo("worklog");
|
||||||
|
assertThat(entity.getAggregateSequence()).isEqualTo(17L);
|
||||||
|
assertThat(entity.getEventIndex()).isEqualTo(2);
|
||||||
|
assertThat(entity.getPartitionKey()).isEqualTo("a".repeat(64));
|
||||||
|
assertThat(entity.getEnvelopeBytes()).containsExactly(exactEnvelope);
|
||||||
|
assertThat(entity.getContentType()).isEqualTo("application/json");
|
||||||
|
assertThat(entity.getSchemaSetHash()).isEqualTo("1".repeat(64));
|
||||||
|
assertThat(entity.getEnvelopeSha256()).isEqualTo("2".repeat(64));
|
||||||
|
assertThat(entity.getEnvelopeSchemaHash()).isEqualTo("3".repeat(64));
|
||||||
|
assertThat(entity.getPayloadSchemaHash()).isEqualTo("4".repeat(64));
|
||||||
|
assertThat(entity.getContractCatalogRevision()).isEqualTo("catalog-r1");
|
||||||
|
assertThat(entity.getDestinationBindingRevision()).isEqualTo("binding-r1");
|
||||||
|
assertThat(entity.getCausationId()).isEqualTo("01994e11-4d88-7000-8000-000000000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidUtf8EnvelopeIsRejectedBeforeRepositoryWrite() {
|
||||||
|
ValidatedIntegrationEvent event = event(new byte[] {(byte) 0xC3, (byte) 0x28});
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> adapter.append(event))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessageContaining("UTF-8")
|
||||||
|
.hasMessageContaining("compatibility");
|
||||||
|
|
||||||
|
verifyNoInteractions(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValidatedIntegrationEvent event(byte[] envelopeBytes) {
|
||||||
|
String partition = "a".repeat(64);
|
||||||
|
return new ValidatedIntegrationEvent(
|
||||||
|
new EventId("01994e11-4d88-7000-8000-000000000001"),
|
||||||
|
new ContractId("portfolio.worklog.reserved"),
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
new LogicalDestinationId("portfolio-domain-events"),
|
||||||
|
new AggregateIdentity("tenant-a", "worklog", "worklog-42"),
|
||||||
|
new AggregateOrder(17, 2),
|
||||||
|
OCCURRED_AT,
|
||||||
|
"corr-1",
|
||||||
|
Optional.of("01994e11-4d88-7000-8000-000000000000"),
|
||||||
|
partition,
|
||||||
|
partition.getBytes(StandardCharsets.US_ASCII),
|
||||||
|
envelopeBytes,
|
||||||
|
"application/json",
|
||||||
|
sha("1"),
|
||||||
|
sha("2"),
|
||||||
|
sha("3"),
|
||||||
|
sha("4"),
|
||||||
|
"catalog-r1",
|
||||||
|
"binding-r1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Sha256 sha(String nibble) {
|
||||||
|
return new Sha256(java.util.HexFormat.of().parseHex(nibble.repeat(64)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
-4
@@ -11,6 +11,8 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.entity.OutboxEventEntity;
|
||||||
|
import dev.caskeleton.application.outbox.CanonicalClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.ClaimedOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxEvent;
|
import dev.caskeleton.application.outbox.OutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
||||||
@@ -66,6 +68,35 @@ class OutboxStoreAdapterTest {
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OutboxEventEntity canonicalPendingEntity(String eventId) {
|
||||||
|
OutboxEventEntity e = pendingEntity(eventId);
|
||||||
|
byte[] envelope = "{\"wire\":\"exact\"}".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
e.setAggregateId("worklog-42");
|
||||||
|
e.setEventType("portfolio.worklog.reserved");
|
||||||
|
e.setPayload(new String(envelope, java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
e.setCorrelationId("corr-1");
|
||||||
|
e.setIdempotencyKey(eventId);
|
||||||
|
e.setContractId("portfolio.worklog.reserved");
|
||||||
|
e.setEnvelopeVersion(1);
|
||||||
|
e.setPayloadVersion(3);
|
||||||
|
e.setLogicalDestination("portfolio-domain-events");
|
||||||
|
e.setTenantScope("tenant-a");
|
||||||
|
e.setAggregateType("worklog");
|
||||||
|
e.setAggregateSequence(17L);
|
||||||
|
e.setEventIndex(2);
|
||||||
|
e.setPartitionKey("a".repeat(64));
|
||||||
|
e.setEnvelopeBytes(envelope);
|
||||||
|
e.setContentType("application/json");
|
||||||
|
e.setSchemaSetHash("1".repeat(64));
|
||||||
|
e.setEnvelopeSha256("2".repeat(64));
|
||||||
|
e.setEnvelopeSchemaHash("3".repeat(64));
|
||||||
|
e.setPayloadSchemaHash("4".repeat(64));
|
||||||
|
e.setContractCatalogRevision("catalog-r1");
|
||||||
|
e.setDestinationBindingRevision("binding-r1");
|
||||||
|
e.setCausationId("01994e11-4d88-7000-8000-000000000000");
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
// ---- append ----
|
// ---- append ----
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -97,7 +128,7 @@ class OutboxStoreAdapterTest {
|
|||||||
OutboxEventEntity entity = pendingEntity("evt-002");
|
OutboxEventEntity entity = pendingEntity("evt-002");
|
||||||
when(claimRepo.claimEligible(eq(NOW), eq(2))).thenReturn(List.of(entity));
|
when(claimRepo.claimEligible(eq(NOW), eq(2))).thenReturn(List.of(entity));
|
||||||
|
|
||||||
List<OutboxEvent> claimed = adapter.claimBatch(2, NOW, IN_FLIGHT_TIMEOUT);
|
List<ClaimedOutboxEvent> claimed = adapter.claimBatch(2, NOW, IN_FLIGHT_TIMEOUT);
|
||||||
|
|
||||||
// status and attemptCount updated on entity
|
// status and attemptCount updated on entity
|
||||||
assertThat(entity.getStatus()).isEqualTo("IN_FLIGHT");
|
assertThat(entity.getStatus()).isEqualTo("IN_FLIGHT");
|
||||||
@@ -106,12 +137,46 @@ class OutboxStoreAdapterTest {
|
|||||||
|
|
||||||
// returned OutboxEvent reflects post-transition state
|
// returned OutboxEvent reflects post-transition state
|
||||||
assertThat(claimed).hasSize(1);
|
assertThat(claimed).hasSize(1);
|
||||||
OutboxEvent result = claimed.get(0);
|
OutboxEvent result = (OutboxEvent) claimed.get(0);
|
||||||
assertThat(result.eventId()).isEqualTo("evt-002");
|
assertThat(result.eventId()).isEqualTo("evt-002");
|
||||||
assertThat(result.status()).isEqualTo(OutboxEventStatus.IN_FLIGHT);
|
assertThat(result.status()).isEqualTo(OutboxEventStatus.IN_FLIGHT);
|
||||||
assertThat(result.attemptCount()).isEqualTo(1);
|
assertThat(result.attemptCount()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void claimBatchMapsCompleteCanonicalMetadataToCanonicalClaim() {
|
||||||
|
OutboxEventEntity entity = canonicalPendingEntity("01994e11-4d88-7000-8000-000000000001");
|
||||||
|
when(claimRepo.claimEligible(eq(NOW), eq(2))).thenReturn(List.of(entity));
|
||||||
|
|
||||||
|
List<ClaimedOutboxEvent> claimed = adapter.claimBatch(2, NOW, IN_FLIGHT_TIMEOUT);
|
||||||
|
|
||||||
|
assertThat(claimed).hasSize(1);
|
||||||
|
assertThat(claimed.getFirst()).isInstanceOf(CanonicalClaimedOutboxEvent.class);
|
||||||
|
CanonicalClaimedOutboxEvent result = (CanonicalClaimedOutboxEvent) claimed.getFirst();
|
||||||
|
assertThat(result.event().eventId().value()).isEqualTo("01994e11-4d88-7000-8000-000000000001");
|
||||||
|
assertThat(result.event().contractId().value()).isEqualTo("portfolio.worklog.reserved");
|
||||||
|
assertThat(result.event().aggregate().tenantScope()).isEqualTo("tenant-a");
|
||||||
|
assertThat(result.event().aggregate().aggregateType()).isEqualTo("worklog");
|
||||||
|
assertThat(result.event().order().sequence()).isEqualTo(17);
|
||||||
|
assertThat(result.event().order().eventIndex()).isEqualTo(2);
|
||||||
|
assertThat(result.event().envelopeBytes())
|
||||||
|
.containsExactly("{\"wire\":\"exact\"}".getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
assertThat(result.status()).isEqualTo(OutboxEventStatus.IN_FLIGHT);
|
||||||
|
assertThat(result.attemptCount()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void claimBatchRejectsPartialCanonicalMetadataInsteadOfDowngradingToLegacy() {
|
||||||
|
OutboxEventEntity entity = pendingEntity("partial-canonical");
|
||||||
|
entity.setEnvelopeVersion(1);
|
||||||
|
when(claimRepo.claimEligible(eq(NOW), eq(1))).thenReturn(List.of(entity));
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> adapter.claimBatch(1, NOW, IN_FLIGHT_TIMEOUT))
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("partial canonical")
|
||||||
|
.hasMessageContaining("partial-canonical");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void claimBatchReturnsEmptyWhenRepoReturnsNothing() {
|
void claimBatchReturnsEmptyWhenRepoReturnsNothing() {
|
||||||
when(claimRepo.claimEligible(any(), anyInt())).thenReturn(List.of());
|
when(claimRepo.claimEligible(any(), anyInt())).thenReturn(List.of());
|
||||||
@@ -145,12 +210,12 @@ class OutboxStoreAdapterTest {
|
|||||||
|
|
||||||
when(claimRepo.claimEligible(eq(NOW), eq(10))).thenReturn(List.of(head, tail));
|
when(claimRepo.claimEligible(eq(NOW), eq(10))).thenReturn(List.of(head, tail));
|
||||||
|
|
||||||
List<OutboxEvent> claimed = adapter.claimBatch(10, NOW, IN_FLIGHT_TIMEOUT);
|
List<ClaimedOutboxEvent> claimed = adapter.claimBatch(10, NOW, IN_FLIGHT_TIMEOUT);
|
||||||
|
|
||||||
// Adapter must return both rows — no in-memory FIFO filtering
|
// Adapter must return both rows — no in-memory FIFO filtering
|
||||||
assertThat(claimed)
|
assertThat(claimed)
|
||||||
.hasSize(2)
|
.hasSize(2)
|
||||||
.extracting(OutboxEvent::eventId)
|
.extracting(ClaimedOutboxEvent::eventId)
|
||||||
.containsExactly("evt-head", "evt-tail");
|
.containsExactly("evt-head", "evt-tail");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -2,7 +2,6 @@ package dev.caskeleton.adapter.outbound.persistence.testkit;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseGate;
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseManifest;
|
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseManifest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@@ -47,7 +46,7 @@ class JpaDocumentationContractTest {
|
|||||||
void everyRegistryGateIsNamedInTheSupportMatrix() {
|
void everyRegistryGateIsNamedInTheSupportMatrix() {
|
||||||
String matrix = read("docs/jpa/support-matrix.md");
|
String matrix = read("docs/jpa/support-matrix.md");
|
||||||
|
|
||||||
for (String gate : JpaReleaseGate.required()) {
|
for (String gate : manifest.gates()) {
|
||||||
assertThat(matrix).as("gate %s is declared but undocumented", gate).contains(gate);
|
assertThat(matrix).as("gate %s is declared but undocumented", gate).contains(gate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -3,7 +3,6 @@ package dev.caskeleton.adapter.outbound.persistence.testkit;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseGate;
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseManifest;
|
import dev.caskeleton.adapter.outbound.persistence.testkit.release.JpaReleaseManifest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
@@ -41,8 +40,7 @@ class JpaReleaseManifestTest {
|
|||||||
@Test
|
@Test
|
||||||
@DisplayName("every required gate is declared and names a task")
|
@DisplayName("every required gate is declared and names a task")
|
||||||
void everyRequiredGateIsDeclaredAndNamesATask() {
|
void everyRequiredGateIsDeclaredAndNamesATask() {
|
||||||
assertThat(manifest.declaresEveryRequiredGate()).isTrue();
|
for (String gate : manifest.gates()) {
|
||||||
for (String gate : JpaReleaseGate.required()) {
|
|
||||||
assertThat(manifest.taskFor(gate))
|
assertThat(manifest.taskFor(gate))
|
||||||
.as("gate %s must name the task that produces its evidence", gate)
|
.as("gate %s must name the task that produces its evidence", gate)
|
||||||
.startsWith(":");
|
.startsWith(":");
|
||||||
|
|||||||
+16
@@ -84,6 +84,22 @@ class JpaReleaseRenderingTest {
|
|||||||
.isNotEqualTo(manifest.gates());
|
.isNotEqualTo(manifest.gates());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("the release workflow invokes the registry-driven aggregate instead of duplicating gate tasks")
|
||||||
|
void theReleaseWorkflowUsesTheRegistryDrivenAggregate() {
|
||||||
|
String flowed = releaseWorkflow.replaceAll("[\t\n\r ]+", " ");
|
||||||
|
|
||||||
|
assertThat(flowed).contains("./gradlew jpaReleaseQualification");
|
||||||
|
for (String task : manifest.gateTasks().values()) {
|
||||||
|
assertThat(releaseWorkflow)
|
||||||
|
.as("CI must not own the release gate task inventory: %s", task)
|
||||||
|
.doesNotContain(task);
|
||||||
|
}
|
||||||
|
assertThat(releaseWorkflow)
|
||||||
|
.as("pool pressure remains a nightly lane unless the release registry promotes it")
|
||||||
|
.doesNotContain(":adapter:outbound:persistence-jpa:jpaPlatformPoolContractTest");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("the release workflow fans out to exactly the Stable majors")
|
@DisplayName("the release workflow fans out to exactly the Stable majors")
|
||||||
void theReleaseWorkflowFansOutToExactlyTheStableMajors() {
|
void theReleaseWorkflowFansOutToExactlyTheStableMajors() {
|
||||||
|
|||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
package dev.caskeleton.adapter.outbound.persistence.testkit.release;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The gates a release must satisfy (design §41).
|
|
||||||
*
|
|
||||||
* <p>Each one names a way the platform could pass its tests while being wrong in production: a
|
|
||||||
* suite that only ran against H2, a runtime with OSIV left on, a deployment where Hibernate can
|
|
||||||
* still alter the schema, a retry path that re-runs an unknown commit, or a runtime credential that
|
|
||||||
* can execute DDL.
|
|
||||||
*/
|
|
||||||
public final class JpaReleaseGate {
|
|
||||||
|
|
||||||
/** The real database ran the contract suite, not H2. */
|
|
||||||
public static final String POSTGRESQL_CONTRACT = "postgresql-contract";
|
|
||||||
|
|
||||||
/** Completion-unknown failures are never automatically retried. */
|
|
||||||
public static final String COMPLETION_UNKNOWN_NO_RETRY = "completion-unknown-no-retry";
|
|
||||||
|
|
||||||
/** Open Session In View is off in every runtime profile. */
|
|
||||||
public static final String OSIV_DISABLED = "osiv-disabled";
|
|
||||||
|
|
||||||
/** Hibernate validates the schema and never mutates it. */
|
|
||||||
public static final String FLYWAY_VALIDATE = "flyway-validate";
|
|
||||||
|
|
||||||
/** The runtime database role cannot execute DDL. */
|
|
||||||
public static final String RUNTIME_ROLE_NO_DDL = "runtime-role-no-ddl";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection fetch pagination is bounded in SQL by the Stable provider.
|
|
||||||
*
|
|
||||||
* <p>Renamed off the provider version. It was {@code hibernate-7.4-fetch-pagination} while every
|
|
||||||
* run of this gate used the version the Boot BOM resolves — 7.1.8.Final — so the evidence was
|
|
||||||
* labelled with a provider the gate had never executed against. The registry records both the
|
|
||||||
* tested baseline and the compatibility target; the gate name records neither, because a gate
|
|
||||||
* that renames itself when a dependency moves is a gate whose history cannot be compared.
|
|
||||||
*/
|
|
||||||
public static final String FETCH_PAGINATION = "collection-fetch-pagination";
|
|
||||||
|
|
||||||
private JpaReleaseGate() {}
|
|
||||||
|
|
||||||
/** Every gate a release must pass. */
|
|
||||||
public static List<String> required() {
|
|
||||||
return List.of(
|
|
||||||
POSTGRESQL_CONTRACT,
|
|
||||||
COMPLETION_UNKNOWN_NO_RETRY,
|
|
||||||
OSIV_DISABLED,
|
|
||||||
FLYWAY_VALIDATE,
|
|
||||||
RUNTIME_ROLE_NO_DDL,
|
|
||||||
FETCH_PAGINATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-5
@@ -147,11 +147,6 @@ public record JpaReleaseManifest(
|
|||||||
return new JpaReleaseManifest(stable, experimental, gates, tasks, baseline.group(1));
|
return new JpaReleaseManifest(stable, experimental, gates, tasks, baseline.group(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether every required release gate is declared. */
|
|
||||||
public boolean declaresEveryRequiredGate() {
|
|
||||||
return gates.containsAll(JpaReleaseGate.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The Gradle task that produces a gate's evidence. */
|
/** The Gradle task that produces a gate's evidence. */
|
||||||
public String taskFor(String gate) {
|
public String taskFor(String gate) {
|
||||||
String task = gateTasks.get(gate);
|
String task = gateTasks.get(gate);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ plugins {
|
|||||||
id 'ca.spring-library'
|
id 'ca.spring-library'
|
||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
id 'java-test-fixtures'
|
id 'java-test-fixtures'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
|
id 'ca.mongo-verification'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. Applied here rather than
|
// Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. Applied here rather than
|
||||||
@@ -61,7 +63,7 @@ dependencies {
|
|||||||
// variant whether or not anybody asks for them. ADR-BUILD-001 records that as an accepted loss —
|
// variant whether or not anybody asks for them. ADR-BUILD-001 records that as an accepted loss —
|
||||||
// unconsumed fixtures are unconsumed, and a leaf that genuinely must not offer them needs a module,
|
// unconsumed fixtures are unconsumed, and a leaf that genuinely must not offer them needs a module,
|
||||||
// not a third convention.
|
// not a third convention.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
sourceSet('mongoPerformanceTest') { compilesAgainst 'main', 'testFixtures' }
|
sourceSet('mongoPerformanceTest') { compilesAgainst 'main', 'testFixtures' }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,18 +88,6 @@ dependencies {
|
|||||||
testFixturesImplementation libs.toxiproxy.java
|
testFixturesImplementation libs.toxiproxy.java
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinned server images. The design forbids `latest` for a certification lane (Task 44): a mutable
|
|
||||||
// tag makes a red run unattributable. `-PmongoPrimaryImage=` / `-PmongoCompatibilityImage=`
|
|
||||||
// override them for a one-off run.
|
|
||||||
Closure<Void> applyMongoImageSelection = { task ->
|
|
||||||
task.systemProperty 'mongodb.primary.image',
|
|
||||||
(project.findProperty('mongoPrimaryImage') ?: 'mongo:8.0.16').toString()
|
|
||||||
task.systemProperty 'mongodb.compatibility.image',
|
|
||||||
(project.findProperty('mongoCompatibilityImage') ?: 'mongo:7.0.28').toString()
|
|
||||||
task.systemProperty 'mongodb.toxiproxy.image',
|
|
||||||
(project.findProperty('mongoToxiproxyImage') ?: 'ghcr.io/shopify/toxiproxy:2.12.0').toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Docker-backed lanes are excluded from the default unit run: they fail closed without Docker, and
|
// Docker-backed lanes are excluded from the default unit run: they fail closed without Docker, and
|
||||||
// a `check` that fails on a laptop without Docker teaches people to skip `check`.
|
// a `check` that fails on a laptop without Docker teaches people to skip `check`.
|
||||||
//
|
//
|
||||||
@@ -122,33 +112,63 @@ tasks.named('test', Test) {
|
|||||||
// copied once per lane here and again in four other leaves.
|
// copied once per lane here and again in four other leaves.
|
||||||
strictTestLanes {
|
strictTestLanes {
|
||||||
lane('mongoReplicaSetTest') {
|
lane('mongoReplicaSetTest') {
|
||||||
|
integration()
|
||||||
tag = 'mongodb-replicaset'
|
tag = 'mongodb-replicaset'
|
||||||
description = 'Single-node replica set contract lane: mapping, atomic write, transaction, ' +
|
description = 'Single-node replica set contract lane: mapping, atomic write, transaction, ' +
|
||||||
'change stream (design §29).'
|
'change stream (design §29).'
|
||||||
customize = { test -> applyMongoImageSelection(test) }
|
systemProperty 'mongodb.primary.image',
|
||||||
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
|
systemProperty 'mongodb.compatibility.image',
|
||||||
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
}
|
}
|
||||||
lane('mongoFailoverTest') {
|
lane('mongoFailoverTest') {
|
||||||
|
integration()
|
||||||
tag = 'mongodb-failover'
|
tag = 'mongodb-failover'
|
||||||
description = 'Three-node replica set failover lane: primary kill, partition, unknown ' +
|
description = 'Three-node replica set failover lane: primary kill, partition, unknown ' +
|
||||||
'commit, resume (design §29).'
|
'commit, resume (design §29).'
|
||||||
customize = { test -> applyMongoImageSelection(test) }
|
systemProperty 'mongodb.primary.image',
|
||||||
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
|
systemProperty 'mongodb.compatibility.image',
|
||||||
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
}
|
}
|
||||||
lane('mongoMigrationTest') {
|
lane('mongoMigrationTest') {
|
||||||
|
integration()
|
||||||
tag = 'mongodb-migration'
|
tag = 'mongodb-migration'
|
||||||
description = 'Migration lane: empty / N-1 / oldest-supported snapshots, lock, checkpoint ' +
|
description = 'Migration lane: empty / N-1 / oldest-supported snapshots, lock, checkpoint ' +
|
||||||
'restart (design §12).'
|
'restart (design §12).'
|
||||||
customize = { test -> applyMongoImageSelection(test) }
|
systemProperty 'mongodb.primary.image',
|
||||||
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
|
systemProperty 'mongodb.compatibility.image',
|
||||||
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
}
|
}
|
||||||
lane('mongoCompatibilityTest') {
|
lane('mongoCompatibilityTest') {
|
||||||
|
integration()
|
||||||
tag = 'mongodb-compatibility'
|
tag = 'mongodb-compatibility'
|
||||||
description = 'MongoDB 7.0 compatibility and 8.0 primary certification matrix (design §30).'
|
description = 'MongoDB 7.0 compatibility and 8.0 primary certification matrix (design §30).'
|
||||||
customize = { test -> applyMongoImageSelection(test) }
|
systemProperty 'mongodb.primary.image',
|
||||||
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
|
systemProperty 'mongodb.compatibility.image',
|
||||||
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
}
|
}
|
||||||
lane('mongoSecurityIntegrationTest') {
|
lane('mongoSecurityIntegrationTest') {
|
||||||
|
integration()
|
||||||
tag = 'mongodb-security-integration'
|
tag = 'mongodb-security-integration'
|
||||||
description = 'RBAC, TLS, injection and redaction release gate against a real server ' +
|
description = 'RBAC, TLS, injection and redaction release gate against a real server ' +
|
||||||
'(design §26).'
|
'(design §26).'
|
||||||
customize = { test -> applyMongoImageSelection(test) }
|
systemProperty 'mongodb.primary.image',
|
||||||
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
|
systemProperty 'mongodb.compatibility.image',
|
||||||
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
}
|
}
|
||||||
|
|
||||||
lane('mongoStableContractTest') {
|
lane('mongoStableContractTest') {
|
||||||
@@ -160,17 +180,19 @@ strictTestLanes {
|
|||||||
// Driven by its own source set rather than a tag: for this shape the source set is the
|
// Driven by its own source set rather than a tag: for this shape the source set is the
|
||||||
// selection, so the convention asks for no tag.
|
// selection, so the convention asks for no tag.
|
||||||
lane('mongoPerformanceTest') {
|
lane('mongoPerformanceTest') {
|
||||||
|
performance()
|
||||||
sourceSet = 'mongoPerformanceTest'
|
sourceSet = 'mongoPerformanceTest'
|
||||||
description = 'Certifies contention, aggregation spill, pagination and pool resource ' +
|
description = 'Certifies contention, aggregation spill, pagination and pool resource ' +
|
||||||
'bounds (design §29).'
|
'bounds (design §29).'
|
||||||
customize = { test ->
|
systemProperty 'mongodb.primary.image',
|
||||||
applyMongoImageSelection(test)
|
providers.gradleProperty('mongoPrimaryImage').orElse('mongo:8.0.16').get()
|
||||||
// Assertions on by default. They defaulted to false, so the lane measured numbers and
|
systemProperty 'mongodb.compatibility.image',
|
||||||
// compared them to nothing — a performance gate whose bounds are never evaluated is a
|
providers.gradleProperty('mongoCompatibilityImage').orElse('mongo:7.0.28').get()
|
||||||
// report, and the release evidence called it a certification.
|
systemProperty 'mongodb.toxiproxy.image',
|
||||||
test.systemProperty 'performance.assertions.enabled',
|
providers.gradleProperty('mongoToxiproxyImage').orElse('ghcr.io/shopify/toxiproxy:2.12.0').get()
|
||||||
(project.findProperty('performance.assertions.enabled') ?: 'true').toString()
|
// Assertions on by default: a performance gate without bounds is only a report.
|
||||||
}
|
systemProperty 'performance.assertions.enabled',
|
||||||
|
providers.gradleProperty('performance.assertions.enabled').orElse('true').get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,91 +203,9 @@ tasks.named('check') {
|
|||||||
'verifyMongoReleaseContractLanes'
|
'verifyMongoReleaseContractLanes'
|
||||||
}
|
}
|
||||||
|
|
||||||
// The tag exclusion above is a claim about two task configurations. This checks the claim against
|
// The two verification tasks are registered by `ca.mongo-verification`. The leaf owns only
|
||||||
// what the two tasks actually ran, because the failure it prevents — every hermetic contract test
|
// the semantic lane selection above and the lifecycle dependency below; JSON/JUnit parsing and
|
||||||
// executing twice per `check` — is invisible in a green build and only shows up as time.
|
// evidence decisions live in typed Java build tooling.
|
||||||
tasks.register('verifyMongoTestLaneDisjointness') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Fails when the unit lane and the stable contract lane execute the same test.'
|
|
||||||
dependsOn 'test', 'mongoStableContractTest'
|
|
||||||
def unitResults = layout.buildDirectory.dir('test-results/test')
|
|
||||||
def contractResults = layout.buildDirectory.dir('test-results/mongoStableContractTest')
|
|
||||||
inputs.dir(unitResults)
|
|
||||||
inputs.dir(contractResults)
|
|
||||||
outputs.file(layout.buildDirectory.file('reports/mongo-test-lane-disjointness.txt'))
|
|
||||||
doLast {
|
|
||||||
def executed = { java.io.File directory ->
|
|
||||||
def names = [] as Set
|
|
||||||
directory.listFiles({ File file -> file.name.endsWith('.xml') } as FileFilter)
|
|
||||||
?.each { file ->
|
|
||||||
new groovy.xml.XmlParser().parse(file).testcase.each { testcase ->
|
|
||||||
names << "${testcase.@classname}#${testcase.@name}".toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
names
|
|
||||||
}
|
|
||||||
def unit = executed(unitResults.get().asFile)
|
|
||||||
def contract = executed(contractResults.get().asFile)
|
|
||||||
def overlap = unit.intersect(contract)
|
|
||||||
if (!overlap.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
"${overlap.size()} tests run in both the unit lane and the stable contract lane, " +
|
|
||||||
"so `check` executes them twice: ${overlap.take(5)}")
|
|
||||||
}
|
|
||||||
def report = outputs.files.singleFile
|
|
||||||
report.parentFile.mkdirs()
|
|
||||||
report.text = "unit=${unit.size()} contract=${contract.size()} overlap=0\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splitting the two lanes moved every tagged contract out of `test`, and the release manifest kept
|
|
||||||
// naming the lane it had left. `MongoReleaseEvidenceVerifier` resolves
|
|
||||||
// `test-results/<task>/TEST-<className>.xml`, so a contract whose class now runs somewhere else
|
|
||||||
// resolves to a file that will never exist: the Stable gate reports the transaction retry
|
|
||||||
// invariants as evidence the run failed to produce, for a suite that ran them.
|
|
||||||
//
|
|
||||||
// Checked against the XML the lanes wrote rather than against a tag table, because a tag table here
|
|
||||||
// would be a second copy of the selection above, and the copy is what drifted the first time.
|
|
||||||
tasks.register('verifyMongoReleaseContractLanes') {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Fails when a blocking release contract names a lane that did not run its class.'
|
|
||||||
dependsOn 'test', 'mongoStableContractTest'
|
|
||||||
def manifest = rootProject.file('../src/config/mongodb/release-contracts.json')
|
|
||||||
def hermeticLanes = ['test', 'mongoStableContractTest']
|
|
||||||
def resultsRoot = layout.buildDirectory.dir('test-results')
|
|
||||||
inputs.file(manifest)
|
|
||||||
inputs.dir(resultsRoot)
|
|
||||||
outputs.file(layout.buildDirectory.file('reports/mongo-release-contract-lanes.txt'))
|
|
||||||
doLast {
|
|
||||||
def contracts = new groovy.json.JsonSlurper().parse(manifest).contracts
|
|
||||||
def checked = []
|
|
||||||
def wrongLane = []
|
|
||||||
contracts.findAll { hermeticLanes.contains(it.task) }.each { contract ->
|
|
||||||
def results = resultsRoot.get().dir(contract.task).file(
|
|
||||||
"TEST-${contract.className}.xml").asFile
|
|
||||||
if (!results.isFile()) {
|
|
||||||
wrongLane << "${contract.id} names lane '${contract.task}', which did not run " +
|
|
||||||
"${contract.className}"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
def suite = new groovy.xml.XmlParser().parse(results)
|
|
||||||
int executed = (suite.@tests as int) - (suite.@skipped as int)
|
|
||||||
if (executed < contract.minimumExecuted) {
|
|
||||||
wrongLane << "${contract.id} requires ${contract.minimumExecuted} executed test(s) " +
|
|
||||||
"in '${contract.task}' and the lane ran ${executed}"
|
|
||||||
}
|
|
||||||
checked << contract.id
|
|
||||||
}
|
|
||||||
if (!wrongLane.isEmpty()) {
|
|
||||||
throw new GradleException(
|
|
||||||
'the Mongo release manifest points at lanes that cannot produce its evidence: ' +
|
|
||||||
wrongLane.join('; '))
|
|
||||||
}
|
|
||||||
def report = outputs.files.singleFile
|
|
||||||
report.parentFile.mkdirs()
|
|
||||||
report.text = "hermetic release contracts verified: ${checked.join(', ')}\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// verifyMongoApiSurface — every public type this leaf exposes is a committed decision.
|
// verifyMongoApiSurface — every public type this leaf exposes is a committed decision.
|
||||||
|
|||||||
+50
-101
@@ -3,8 +3,13 @@ plugins {
|
|||||||
id 'ca.spring-config'
|
id 'ca.spring-config'
|
||||||
id 'org.springframework.boot'
|
id 'org.springframework.boot'
|
||||||
id 'ca.config-contract'
|
id 'ca.config-contract'
|
||||||
|
id 'ca.auxiliary-source-set'
|
||||||
|
id 'ca.bootrun-dotenv'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def contractRegistriesDirectory = rootProject.projectDir.parentFile.toPath()
|
||||||
|
.resolve('docs/registries').toFile()
|
||||||
|
|
||||||
// Application entry point. Wires the default runtime module set and runs Spring Boot.
|
// Application entry point. Wires the default runtime module set and runs Spring Boot.
|
||||||
// Optional leaves require an explicit registry allowance plus a composition-root dependency.
|
// Optional leaves require an explicit registry allowance plus a composition-root dependency.
|
||||||
// The infrastructure lane. Testcontainers-backed tests boot a real PostgreSQL, so they belong to a
|
// The infrastructure lane. Testcontainers-backed tests boot a real PostgreSQL, so they belong to a
|
||||||
@@ -13,7 +18,7 @@ plugins {
|
|||||||
// feedback loop starts a database is a leaf nobody runs the fast loop on. This is the same shape
|
// feedback loop starts a database is a leaf nobody runs the fast loop on. This is the same shape
|
||||||
// persistence-jpa, web, websocket and objectstorage already use, declared through the repository's
|
// persistence-jpa, web, websocket and objectstorage already use, declared through the repository's
|
||||||
// own strictTestLanes DSL rather than by hand.
|
// own strictTestLanes DSL rather than by hand.
|
||||||
strictTestLanes {
|
auxiliarySourceSets {
|
||||||
sourceSet('integrationTest') {
|
sourceSet('integrationTest') {
|
||||||
compilesAgainst 'main'
|
compilesAgainst 'main'
|
||||||
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
||||||
@@ -21,31 +26,45 @@ strictTestLanes {
|
|||||||
sourceSet('architectureTest') {
|
sourceSet('architectureTest') {
|
||||||
compilesAgainst 'main', 'test'
|
compilesAgainst 'main', 'test'
|
||||||
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
||||||
|
runtimeFrom 'test'
|
||||||
}
|
}
|
||||||
sourceSet('bootCompositionTest') {
|
sourceSet('systemTest') {
|
||||||
compilesAgainst 'main', 'test'
|
compilesAgainst 'main', 'test'
|
||||||
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor'
|
||||||
}
|
runtimeFrom 'test'
|
||||||
lane('integrationTest') {
|
|
||||||
description = 'Testcontainers-backed real-PostgreSQL integration contracts.'
|
|
||||||
sourceSet = 'integrationTest'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// These two lanes were split physically from `test`, but they must preserve the ordinary test
|
strictTestLanes {
|
||||||
// runtime semantics. The generic auxiliary-source-set helper includes compileClasspath in runtime,
|
lane('integrationTest') {
|
||||||
// which is useful for some tool/qualification lanes but would leak testCompileOnly dependencies
|
integration()
|
||||||
// (notably Spring Cloud refresh scope) into these behavioral tests.
|
description = 'Testcontainers-backed real-PostgreSQL integration contracts.'
|
||||||
sourceSets.architectureTest.runtimeClasspath =
|
sourceSet = 'integrationTest'
|
||||||
sourceSets.architectureTest.output + sourceSets.test.runtimeClasspath
|
}
|
||||||
sourceSets.bootCompositionTest.runtimeClasspath =
|
lane('architectureTest') {
|
||||||
sourceSets.bootCompositionTest.output + sourceSets.test.runtimeClasspath
|
architecture()
|
||||||
|
description = 'Runs the whole-repository ArchUnit and architecture contract source set.'
|
||||||
|
sourceSet = 'architectureTest'
|
||||||
|
maxHeapSize = '2g'
|
||||||
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
|
shouldRunAfter 'test'
|
||||||
|
inputDirectory contractRegistriesDirectory
|
||||||
|
}
|
||||||
|
lane('systemTest') {
|
||||||
|
system()
|
||||||
|
description = 'Runs full-application startup and shipped-profile characterization tests.'
|
||||||
|
sourceSet = 'systemTest'
|
||||||
|
maxHeapSize = '1g'
|
||||||
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
|
shouldRunAfter 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// architectureTest and systemTest compile against test fixtures but intentionally mirror
|
||||||
|
// the ordinary test runtime rather than their compile classpath. `runtimeFrom 'test'` above keeps
|
||||||
|
// testCompileOnly analysis dependencies out of these behavioral lanes.
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
functionalTest {
|
|
||||||
java.srcDir 'src/functionalTest/java'
|
|
||||||
resources.srcDir 'src/functionalTest/resources'
|
|
||||||
}
|
|
||||||
conditionalTransportTest {
|
conditionalTransportTest {
|
||||||
java.srcDir 'src/conditionalTransportTest/java'
|
java.srcDir 'src/conditionalTransportTest/java'
|
||||||
resources.srcDir 'src/conditionalTransportTest/resources'
|
resources.srcDir 'src/conditionalTransportTest/resources'
|
||||||
@@ -164,14 +183,11 @@ dependencies {
|
|||||||
testCompileOnly 'org.springframework:spring-webmvc' // SseEmitter, ResponseBodyEmitter, StreamingResponseBody
|
testCompileOnly 'org.springframework:spring-webmvc' // SseEmitter, ResponseBodyEmitter, StreamingResponseBody
|
||||||
testCompileOnly 'org.springframework:spring-websocket' // org.springframework.web.socket..
|
testCompileOnly 'org.springframework:spring-websocket' // org.springframework.web.socket..
|
||||||
testCompileOnly 'jakarta.websocket:jakarta.websocket-api' // jakarta.websocket..
|
testCompileOnly 'jakarta.websocket:jakarta.websocket-api' // jakarta.websocket..
|
||||||
// The Kafka client, on the runtime classpath so KafkaSenderConfig can supply the broker bridge
|
// architecture-only: positive-control fixture imports Kafka TopicPartition to prove domain code
|
||||||
// the messaging adapter declares as a seam and nothing implemented (MSG-INT-003). Inert unless
|
// cannot depend on broker SDK types. The shipped main/test compile classpaths must not inherit it.
|
||||||
// app.messaging.broker=kafka: no producer bean, no connection, no sender thread otherwise.
|
architectureTestCompileOnly 'org.apache.kafka:kafka-clients'
|
||||||
//
|
// Kafka producer ownership lives in adapter:outbound:messaging. The composition root sees only
|
||||||
// It also serves the transport-free domain-event fixtures that used to need it testCompileOnly;
|
// the adapter module; it no longer implements the broker seam itself.
|
||||||
// an implementation dependency is visible to the test compile classpath, so the narrower
|
|
||||||
// declaration is now redundant rather than removed for a different reason.
|
|
||||||
implementation 'org.apache.kafka:kafka-clients' // org.apache.kafka..
|
|
||||||
testCompileOnly 'jakarta.ws.rs:jakarta.ws.rs-api' // jakarta.ws.rs..
|
testCompileOnly 'jakarta.ws.rs:jakarta.ws.rs-api' // jakarta.ws.rs..
|
||||||
// test-only: @RefreshScope for the no-refresh-scope violation fixture (version pinned; not in the BOM). See README.
|
// test-only: @RefreshScope for the no-refresh-scope violation fixture (version pinned; not in the BOM). See README.
|
||||||
testCompileOnly libs.spring.cloud.context // org.springframework.cloud.context..
|
testCompileOnly libs.spring.cloud.context // org.springframework.cloud.context..
|
||||||
@@ -195,12 +211,6 @@ dependencies {
|
|||||||
// test-only: JUnit Platform Test Kit — proves optional-adapter tests report SKIPPED (never FAILED)
|
// test-only: JUnit Platform Test Kit — proves optional-adapter tests report SKIPPED (never FAILED)
|
||||||
// when their enable-flag env var is unset (feature-contract-verification-test-suite D3, Claims #7). See README.
|
// when their enable-flag env var is unset (feature-contract-verification-test-suite D3, Claims #7). See README.
|
||||||
testImplementation 'org.junit.platform:junit-platform-testkit'
|
testImplementation 'org.junit.platform:junit-platform-testkit'
|
||||||
// functional-test-only: executes isolated Gradle fixtures without placing Gradle's SLF4J
|
|
||||||
// provider on the ordinary test runtime classpath.
|
|
||||||
functionalTestImplementation gradleTestKit()
|
|
||||||
functionalTestImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
functionalTestImplementation 'org.assertj:assertj-core'
|
|
||||||
functionalTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
// Explicit qualification-only composition. These projects remain absent from main
|
// Explicit qualification-only composition. These projects remain absent from main
|
||||||
// api/implementation/compileOnly/runtimeOnly and therefore from both shipped runtime graphs.
|
// api/implementation/compileOnly/runtimeOnly and therefore from both shipped runtime graphs.
|
||||||
conditionalTransportTestImplementation project(':adapter:inbound:graphql')
|
conditionalTransportTestImplementation project(':adapter:inbound:graphql')
|
||||||
@@ -213,8 +223,6 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def repositoryRootForContractTests = rootProject.projectDir.parentFile.absolutePath
|
def repositoryRootForContractTests = rootProject.projectDir.parentFile.absolutePath
|
||||||
def contractRegistriesDirectory = rootProject.projectDir.parentFile.toPath()
|
|
||||||
.resolve('docs/registries').toFile()
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test).configureEach {
|
||||||
systemProperty 'ca.repository.root', repositoryRootForContractTests
|
systemProperty 'ca.repository.root', repositoryRootForContractTests
|
||||||
}
|
}
|
||||||
@@ -233,53 +241,15 @@ tasks.named('test', Test) {
|
|||||||
jvmArgs '-Duser.timezone=UTC'
|
jvmArgs '-Duser.timezone=UTC'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('architectureTest', Test) {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Runs the whole-repository ArchUnit and architecture contract source set.'
|
|
||||||
testClassesDirs = sourceSets.architectureTest.output.classesDirs
|
|
||||||
classpath = sourceSets.architectureTest.runtimeClasspath
|
|
||||||
useJUnitPlatform()
|
|
||||||
failOnNoDiscoveredTests = true
|
|
||||||
shouldRunAfter tasks.named('test')
|
|
||||||
maxHeapSize = '2g'
|
|
||||||
inputs.dir(contractRegistriesDirectory)
|
|
||||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
|
||||||
jvmArgs '-Duser.timezone=UTC'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('bootCompositionTest', Test) {
|
def conditionalTransportCompositionQualification = extensions.getByName('strictQualification').register(
|
||||||
group = 'verification'
|
'conditionalTransportCompositionTest',
|
||||||
description = 'Runs slow full-composition startup and shipped-profile characterization tests.'
|
sourceSets.conditionalTransportTest,
|
||||||
testClassesDirs = sourceSets.bootCompositionTest.output.classesDirs
|
[
|
||||||
classpath = sourceSets.bootCompositionTest.runtimeClasspath
|
|
||||||
useJUnitPlatform()
|
|
||||||
failOnNoDiscoveredTests = true
|
|
||||||
shouldRunAfter tasks.named('test')
|
|
||||||
maxHeapSize = '1g'
|
|
||||||
jvmArgs '-Duser.timezone=UTC'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('functionalTest', Test) {
|
|
||||||
group = 'verification'
|
|
||||||
description = 'Runs isolated Gradle TestKit contracts for repository build behavior.'
|
|
||||||
testClassesDirs = sourceSets.functionalTest.output.classesDirs
|
|
||||||
classpath = sourceSets.functionalTest.runtimeClasspath
|
|
||||||
useJUnitPlatform()
|
|
||||||
failOnNoDiscoveredTests = true
|
|
||||||
shouldRunAfter tasks.named('test')
|
|
||||||
// The expensive memory is in the nested Gradle invocations, not this JUnit worker. Keeping the
|
|
||||||
// TestKit worker bounded prevents a repository build from reserving another multi-GiB JVM.
|
|
||||||
maxHeapSize = '768m'
|
|
||||||
jvmArgs '-Duser.timezone=UTC'
|
|
||||||
}
|
|
||||||
|
|
||||||
def conditionalTransportCompositionQualification = registerStrictQualificationTest(
|
|
||||||
name: 'conditionalTransportCompositionTest',
|
|
||||||
sourceSet: sourceSets.conditionalTransportTest,
|
|
||||||
requiredClasses: [
|
|
||||||
'dev.caskeleton.bootstrap.transport.ConditionalTransportCompositionContractTest'
|
'dev.caskeleton.bootstrap.transport.ConditionalTransportCompositionContractTest'
|
||||||
],
|
],
|
||||||
description: 'Proves the explicit test-only GraphQL/gRPC/WebSocket opt-in classpath.')
|
'Proves the explicit test-only GraphQL/gRPC/WebSocket opt-in classpath.'
|
||||||
|
)
|
||||||
conditionalTransportCompositionQualification.configure {
|
conditionalTransportCompositionQualification.configure {
|
||||||
shouldRunAfter tasks.named('test')
|
shouldRunAfter tasks.named('test')
|
||||||
}
|
}
|
||||||
@@ -295,29 +265,8 @@ tasks.register('stageDockerJar', Sync) {
|
|||||||
rename { 'application.jar' }
|
rename { 'application.jar' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run from the repo's src/ root and inject src/.env into the Java process environment.
|
// `ca.bootrun-dotenv` owns root working-directory and .env injection for bootRun.
|
||||||
// Boot 4 initializes profiles/logging before spring-dotenv can reliably contribute .env values.
|
// Boot 4 initializes profiles/logging before spring-dotenv can reliably contribute these values.
|
||||||
bootRun {
|
|
||||||
workingDir = rootProject.projectDir
|
|
||||||
doFirst {
|
|
||||||
File envFile = rootProject.file('.env')
|
|
||||||
if (!envFile.isFile()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
envFile.eachLine { raw ->
|
|
||||||
String line = raw.trim()
|
|
||||||
if (line.isEmpty() || line.startsWith('#') || !line.contains('=')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
int separator = line.indexOf('=')
|
|
||||||
String key = line.substring(0, separator).trim()
|
|
||||||
String value = line.substring(separator + 1).trim()
|
|
||||||
if (!key.isEmpty() && System.getenv(key) == null && !environment.containsKey(key)) {
|
|
||||||
environment key, value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// JPA-INT-003 — the H2 developer convenience, which now has to be asked for by name.
|
// JPA-INT-003 — the H2 developer convenience, which now has to be asked for by name.
|
||||||
//
|
//
|
||||||
|
|||||||
+346
-346
@@ -1,100 +1,100 @@
|
|||||||
# This is a Gradle generated file for dependency locking.
|
# This is a Gradle generated file for dependency locking.
|
||||||
# Manual edits can break the build and are not advised.
|
# Manual edits can break the build and are not advised.
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
aopalliance:aopalliance:1.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
aopalliance:aopalliance:1.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
at.yawk.lz4:lz4-java:1.10.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
at.yawk.lz4:lz4-java:1.10.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
ch.qos.logback:logback-classic:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
ch.qos.logback:logback-classic:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
ch.qos.logback:logback-core:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
ch.qos.logback:logback-core:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.approvaltests:approvaltests-util:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.approvaltests:approvaltests-util:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.approvaltests:approvaltests:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.approvaltests:approvaltests:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.ethlo.time:itu:1.14.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.ethlo.time:itu:1.14.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson.core:jackson-annotations:2.21=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.core:jackson-annotations:2.21=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson.core:jackson-core:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.core:jackson-core:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson.core:jackson-databind:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.core:jackson-databind:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.fasterxml.jackson:jackson-bom:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml.jackson:jackson-bom:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.fasterxml:classmate:1.7.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.fasterxml:classmate:1.7.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.github.docker-java:docker-java-api:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.github.docker-java:docker-java-api:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.github.docker-java:docker-java-transport-zerodep:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.github.docker-java:docker-java-transport-zerodep:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.github.docker-java:docker-java-transport:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.github.docker-java:docker-java-transport:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.github.luben:zstd-jni:1.5.6-10=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.github.luben:zstd-jni:1.5.6-10=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs
|
com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs
|
||||||
com.github.spotbugs:spotbugs:4.10.2=spotbugs
|
com.github.spotbugs:spotbugs:4.10.2=spotbugs
|
||||||
com.github.stephenc.jcip:jcip-annotations:1.0-1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
|
com.github.stephenc.jcip:jcip-annotations:1.0-1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.google.android:annotations:4.1.1.4=conditionalTransportTestRuntimeClasspath
|
com.google.android:annotations:4.1.1.4=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.api.grpc:proto-google-common-protos:2.41.0=conditionalTransportTestRuntimeClasspath
|
com.google.api.grpc:proto-google-common-protos:2.41.0=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.auto:auto-common:1.2.2=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.auto:auto-common:1.2.2=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.code.findbugs:jsr305:3.0.2=checkstyle,conditionalTransportTestRuntimeClasspath,spotbugs
|
com.google.code.findbugs:jsr305:3.0.2=checkstyle,conditionalTransportTestRuntimeClasspath,spotbugs
|
||||||
com.google.code.gson:gson:2.13.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,testRuntimeClasspath
|
com.google.code.gson:gson:2.13.2=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.google.errorprone:error_prone_annotation:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.errorprone:error_prone_annotation:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_annotations:2.38.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
com.google.errorprone:error_prone_annotations:2.38.0=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
com.google.errorprone:error_prone_annotations:2.41.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,testRuntimeClasspath
|
com.google.errorprone:error_prone_annotations:2.41.0=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.google.errorprone:error_prone_annotations:2.47.0=checkstyle
|
com.google.errorprone:error_prone_annotations:2.47.0=checkstyle
|
||||||
com.google.errorprone:error_prone_annotations:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.errorprone:error_prone_annotations:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_check_api:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.errorprone:error_prone_check_api:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.errorprone:error_prone_core:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.errorprone:error_prone_core:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.googlejavaformat:google-java-format:1.35.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.googlejavaformat:google-java-format:1.35.0=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.guava:failureaccess:1.0.2=conditionalTransportTestRuntimeClasspath
|
com.google.guava:failureaccess:1.0.2=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.guava:failureaccess:1.0.3=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.guava:failureaccess:1.0.3=annotationProcessor,architectureTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.guava:guava:33.2.1-jre=conditionalTransportTestRuntimeClasspath
|
com.google.guava:guava:33.2.1-jre=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.guava:guava:33.5.0-jre=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.guava:guava:33.5.0-jre=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.guava:guava:33.6.0-jre=checkstyle
|
com.google.guava:guava:33.6.0-jre=checkstyle
|
||||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,architectureTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,conditionalTransportTestRuntimeClasspath,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.j2objc:j2objc-annotations:2.8=conditionalTransportTestRuntimeClasspath
|
com.google.j2objc:j2objc-annotations:2.8=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,architectureTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.google.protobuf:protobuf-java-util:3.25.5=conditionalTransportTestRuntimeClasspath
|
com.google.protobuf:protobuf-java-util:3.25.5=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.protobuf:protobuf-java:3.25.5=conditionalTransportTestRuntimeClasspath
|
com.google.protobuf:protobuf-java:3.25.5=conditionalTransportTestRuntimeClasspath
|
||||||
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
com.graphql-java:graphql-java:25.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.graphql-java:graphql-java:25.0=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.graphql-java:java-dataloader:6.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.graphql-java:java-dataloader:6.0.0=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.h2database:h2:2.4.240=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.h2database:h2:2.4.240=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0=spotbugsPlugins
|
com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0=spotbugsPlugins
|
||||||
com.jayway.jsonpath:json-path:2.10.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.jayway.jsonpath:json-path:2.10.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.networknt:json-schema-validator:3.0.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.networknt:json-schema-validator:3.0.2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.nimbusds:content-type:2.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.nimbusds:content-type:2.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.nimbusds:lang-tag:1.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.nimbusds:lang-tag:1.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.nimbusds:nimbus-jose-jwt:10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.nimbusds:nimbus-jose-jwt:10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.nimbusds:oauth2-oidc-sdk:11.26.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.nimbusds:oauth2-oidc-sdk:11.26.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.puppycrawl.tools:checkstyle:13.5.0=checkstyle
|
com.puppycrawl.tools:checkstyle:13.5.0=checkstyle
|
||||||
com.rabbitmq:amqp-client:5.27.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.rabbitmq:amqp-client:5.27.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.squareup.okhttp3:okhttp-jvm:5.2.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.squareup.okhttp3:okhttp-jvm:5.2.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.squareup.okhttp3:okhttp:5.2.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.squareup.okhttp3:okhttp:5.2.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.squareup.okio:okio-jvm:3.16.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.squareup.okio:okio-jvm:3.16.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.squareup.okio:okio:3.16.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.squareup.okio:okio:3.16.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.sun.istack:istack-commons-runtime:4.1.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
com.sun.istack:istack-commons-runtime:4.1.2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.tngtech.archunit:archunit-junit5-api:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.tngtech.archunit:archunit-junit5-api:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.tngtech.archunit:archunit-junit5-engine:1.3.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
com.tngtech.archunit:archunit-junit5-engine:1.3.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
com.tngtech.archunit:archunit-junit5:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.tngtech.archunit:archunit-junit5:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.tngtech.archunit:archunit:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.tngtech.archunit:archunit:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.vaadin.external.google:android-json:0.0.20131108.vaadin1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.vaadin.external.google:android-json:0.0.20131108.vaadin1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
com.zaxxer:HikariCP:7.0.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
com.zaxxer:HikariCP:7.0.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
commons-beanutils:commons-beanutils:1.11.0=checkstyle
|
commons-beanutils:commons-beanutils:1.11.0=checkstyle
|
||||||
commons-codec:commons-codec:1.19.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
commons-codec:commons-codec:1.19.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
commons-collections:commons-collections:3.2.2=checkstyle
|
commons-collections:commons-collections:3.2.2=checkstyle
|
||||||
commons-io:commons-io:2.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
commons-io:commons-io:2.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
commons-io:commons-io:2.21.0=spotbugs
|
commons-io:commons-io:2.21.0=spotbugs
|
||||||
commons-logging:commons-logging:1.3.6=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
commons-logging:commons-logging:1.3.6=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
eu.rekawek.toxiproxy:toxiproxy-java:2.1.11=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
eu.rekawek.toxiproxy:toxiproxy-java:2.1.11=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
info.picocli:picocli:4.7.7=checkstyle
|
info.picocli:picocli:4.7.7=checkstyle
|
||||||
io.cloudevents:cloudevents-api:4.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.cloudevents:cloudevents-api:4.0.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.cloudevents:cloudevents-core:4.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.cloudevents:cloudevents-core:4.0.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.cdimascio:dotenv-java:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.cdimascio:dotenv-java:3.0.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
io.github.resilience4j:resilience4j-bulkhead:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-bulkhead:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-circuitbreaker:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-circuitbreaker:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-core:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-core:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-micrometer:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-micrometer:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-ratelimiter:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-ratelimiter:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-retry:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-retry:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.github.resilience4j:resilience4j-timelimiter:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.github.resilience4j:resilience4j-timelimiter:2.2.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.grpc:grpc-api:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-api:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
io.grpc:grpc-context:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-context:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
io.grpc:grpc-core:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-core:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
@@ -104,310 +104,310 @@ io.grpc:grpc-protobuf:1.68.1=conditionalTransportTestRuntimeClasspath
|
|||||||
io.grpc:grpc-services:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-services:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
io.grpc:grpc-stub:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-stub:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
io.grpc:grpc-util:1.68.1=conditionalTransportTestRuntimeClasspath
|
io.grpc:grpc-util:1.68.1=conditionalTransportTestRuntimeClasspath
|
||||||
io.lettuce:lettuce-core:6.8.2.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.lettuce:lettuce-core:6.8.2.RELEASE=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.micrometer:context-propagation:1.2.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:context-propagation:1.2.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-commons:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-commons:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-core:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-core:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-jakarta9:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-jakarta9:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-observation:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-observation:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-registry-prometheus:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-registry-prometheus:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-tracing-bridge-otel:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-tracing-bridge-otel:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.micrometer:micrometer-tracing:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.micrometer:micrometer-tracing:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-buffer:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-buffer:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-base:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-base:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-classes-quic:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-classes-quic:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-compression:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-compression:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-dns:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-dns:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-http2:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-http2:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-http3:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-http3:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-http:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-http:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-marshalling:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-marshalling:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-native-quic:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-native-quic:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-protobuf:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-protobuf:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec-socks:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec-socks:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-codec:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-codec:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-common:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-common:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-handler-proxy:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-handler-proxy:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-handler:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-handler:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-resolver-dns-classes-macos:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-resolver-dns-classes-macos:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-resolver-dns-native-macos:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-resolver-dns-native-macos:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-resolver-dns:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-resolver-dns:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-resolver:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-resolver:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-transport-classes-epoll:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-transport-classes-epoll:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-transport-native-epoll:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-transport-native-epoll:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-transport-native-unix-common:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-transport-native-unix-common:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.netty:netty-transport:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.netty:netty-transport:4.2.17.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry.semconv:opentelemetry-semconv:1.37.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry.semconv:opentelemetry-semconv:1.37.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-api:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-api:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-context:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-context:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-exporter-common:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-exporter-common:1.55.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-exporter-otlp-common:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-exporter-otlp-common:1.55.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-exporter-otlp:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-exporter-otlp:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.55.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-extension-trace-propagators:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-extension-trace-propagators:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.55.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk-logs:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk-logs:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk-metrics:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk-metrics:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk-trace:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk-trace:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.opentelemetry:opentelemetry-sdk:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.opentelemetry:opentelemetry-sdk:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.perfmark:perfmark-api:0.27.0=conditionalTransportTestRuntimeClasspath
|
io.perfmark:perfmark-api:0.27.0=conditionalTransportTestRuntimeClasspath
|
||||||
io.projectreactor.netty:reactor-netty-core:1.3.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.projectreactor.netty:reactor-netty-core:1.3.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.projectreactor.netty:reactor-netty-http:1.3.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.projectreactor.netty:reactor-netty-http:1.3.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.projectreactor:reactor-core:3.8.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.projectreactor:reactor-core:3.8.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-config:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-config:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-core:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-core:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-exposition-formats:1.4.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-exposition-formats:1.4.3=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-exposition-textformats:1.4.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-exposition-textformats:1.4.3=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-model:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-model:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.prometheus:prometheus-metrics-tracer-common:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
io.prometheus:prometheus-metrics-tracer-common:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
io.smallrye:jandex:3.3.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
io.smallrye:jandex:3.3.2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.swagger.core.v3:swagger-annotations-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.swagger.core.v3:swagger-annotations-jakarta:2.2.38=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.swagger.core.v3:swagger-core-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.swagger.core.v3:swagger-core-jakarta:2.2.38=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
io.swagger.core.v3:swagger-models-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
io.swagger.core.v3:swagger-models-jakarta:2.2.38=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
jakarta.activation:jakarta.activation-api:2.1.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.activation:jakarta.activation-api:2.1.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.annotation:jakarta.annotation-api:3.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.annotation:jakarta.annotation-api:3.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.inject:jakarta.inject-api:2.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
jakarta.inject:jakarta.inject-api:2.0.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
jakarta.mail:jakarta.mail-api:2.1.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
jakarta.mail:jakarta.mail-api:2.1.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
jakarta.persistence:jakarta.persistence-api:3.2.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.persistence:jakarta.persistence-api:3.2.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.servlet:jakarta.servlet-api:6.1.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
jakarta.servlet:jakarta.servlet-api:6.1.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
jakarta.transaction:jakarta.transaction-api:2.0.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.transaction:jakarta.transaction-api:2.0.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.validation:jakarta.validation-api:3.1.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.validation:jakarta.validation-api:3.1.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
jakarta.websocket:jakarta.websocket-api:2.2.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
jakarta.websocket:jakarta.websocket-api:2.2.0=architectureTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
jakarta.ws.rs:jakarta.ws.rs-api:4.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
jakarta.ws.rs:jakarta.ws.rs-api:4.0.0=architectureTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
javax.inject:javax.inject:1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
javax.inject:javax.inject:1=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
jaxen:jaxen:2.0.6=spotbugs
|
jaxen:jaxen:2.0.6=spotbugs
|
||||||
me.paulschwarz:spring-dotenv:4.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
me.paulschwarz:spring-dotenv:4.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.bytebuddy:byte-buddy-agent:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.bytebuddy:byte-buddy-agent:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.bytebuddy:byte-buddy:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.bytebuddy:byte-buddy:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.java.dev.jna:jna:5.18.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.java.dev.jna:jna:5.18.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.logstash.logback:logstash-logback-encoder:8.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.logstash.logback:logstash-logback-encoder:8.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.minidev:accessors-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.minidev:accessors-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.minidev:json-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
net.minidev:json-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
net.sf.saxon:Saxon-HE:12.9=checkstyle,spotbugs
|
net.sf.saxon:Saxon-HE:12.9=checkstyle,spotbugs
|
||||||
ognl:ognl:3.3.4=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
ognl:ognl:3.3.4=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.antlr:antlr4-runtime:4.13.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.antlr:antlr4-runtime:4.13.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.bcel:bcel:6.12.0=spotbugs
|
org.apache.bcel:bcel:6.12.0=spotbugs
|
||||||
org.apache.commons:commons-compress:1.28.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.commons:commons-compress:1.28.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.commons:commons-lang3:3.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
|
org.apache.commons:commons-lang3:3.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.commons:commons-text:1.15.0=spotbugs
|
org.apache.commons:commons-text:1.15.0=spotbugs
|
||||||
org.apache.commons:commons-text:1.3=checkstyle
|
org.apache.commons:commons-text:1.3=checkstyle
|
||||||
org.apache.groovy:groovy-bom:5.0.8=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.apache.groovy:groovy-bom:5.0.8=architectureTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.apache.groovy:groovy:5.0.8=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.apache.groovy:groovy:5.0.8=architectureTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.apache.httpcomponents.client5:httpclient5:5.5.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.apache.httpcomponents.client5:httpclient5:5.5.2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.apache.httpcomponents.core5:httpcore5-h2:5.3.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.apache.httpcomponents.core5:httpcore5-h2:5.3.6=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.apache.httpcomponents.core5:httpcore5:5.3.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.apache.httpcomponents.core5:httpcore5:5.3.6=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
org.apache.httpcomponents:httpclient:4.5.13=checkstyle
|
||||||
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
org.apache.httpcomponents:httpcore:4.4.16=checkstyle
|
||||||
org.apache.kafka:kafka-clients:4.1.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.kafka:kafka-clients:4.1.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.apache.logging.log4j:log4j-api:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
|
org.apache.logging.log4j:log4j-api:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.logging.log4j:log4j-core:2.25.5=spotbugs
|
org.apache.logging.log4j:log4j-core:2.25.5=spotbugs
|
||||||
org.apache.logging.log4j:log4j-to-slf4j:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.logging.log4j:log4j-to-slf4j:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.maven.doxia:doxia-core:1.12.0=checkstyle
|
org.apache.maven.doxia:doxia-core:1.12.0=checkstyle
|
||||||
org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
|
org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
|
||||||
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
|
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
|
||||||
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
|
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
|
||||||
org.apache.tomcat.embed:tomcat-embed-core:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.tomcat.embed:tomcat-embed-core:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.tomcat.embed:tomcat-embed-el:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.tomcat.embed:tomcat-embed-el:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.tomcat.embed:tomcat-embed-websocket:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.apache.tomcat.embed:tomcat-embed-websocket:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.apache.xbean:xbean-reflect:3.7=checkstyle
|
org.apache.xbean:xbean-reflect:3.7=checkstyle
|
||||||
org.apiguardian:apiguardian-api:1.1.2=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,conditionalTransportTestCompileClasspath,functionalTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.apiguardian:apiguardian-api:1.1.2=architectureTestCompileClasspath,conditionalTransportTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.aspectj:aspectjweaver:1.9.25.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.aspectj:aspectjweaver:1.9.25.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.assertj:assertj-core:3.27.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.assertj:assertj-core:3.27.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.attoparser:attoparser:2.0.7.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.attoparser:attoparser:2.0.7.RELEASE=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.awaitility:awaitility:4.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.awaitility:awaitility:4.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.checkerframework:checker-qual:3.42.0=conditionalTransportTestRuntimeClasspath
|
org.checkerframework:checker-qual:3.42.0=conditionalTransportTestRuntimeClasspath
|
||||||
org.checkerframework:checker-qual:3.55.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.checkerframework:checker-qual:3.55.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.codehaus.mojo:animal-sniffer-annotations:1.24=conditionalTransportTestRuntimeClasspath
|
org.codehaus.mojo:animal-sniffer-annotations:1.24=conditionalTransportTestRuntimeClasspath
|
||||||
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
|
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
|
||||||
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
|
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
|
||||||
org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle
|
org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle
|
||||||
org.codehaus.plexus:plexus-utils:3.3.0=checkstyle
|
org.codehaus.plexus:plexus-utils:3.3.0=checkstyle
|
||||||
org.dom4j:dom4j:2.2.0=spotbugs
|
org.dom4j:dom4j:2.2.0=spotbugs
|
||||||
org.eclipse.angus:angus-activation:2.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.angus:angus-activation:2.0.3=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.angus:angus-mail:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.angus:angus-mail:2.0.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty.compression:jetty-compression-common:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty.compression:jetty-compression-common:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty.compression:jetty-compression-gzip:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty.compression:jetty-compression-gzip:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty:jetty-alpn-client:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty:jetty-alpn-client:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty:jetty-client:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty:jetty-client:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty:jetty-http:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty:jetty-http:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty:jetty-io:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty:jetty-io:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.eclipse.jetty:jetty-util:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.eclipse.jetty:jetty-util:12.1.12=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.flywaydb:flyway-core:11.14.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.flywaydb:flyway-core:11.14.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.flywaydb:flyway-database-postgresql:11.14.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.flywaydb:flyway-database-postgresql:11.14.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.glassfish.jaxb:jaxb-core:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.glassfish.jaxb:jaxb-core:4.0.9=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.glassfish.jaxb:jaxb-runtime:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.glassfish.jaxb:jaxb-runtime:4.0.9=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.glassfish.jaxb:txw2:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.glassfish.jaxb:txw2:4.0.9=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.hamcrest:hamcrest:3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.hamcrest:hamcrest:3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.hdrhistogram:HdrHistogram:2.2.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.hdrhistogram:HdrHistogram:2.2.2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.hibernate.models:hibernate-models:1.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.hibernate.models:hibernate-models:1.0.1=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.hibernate.orm:hibernate-core:7.2.24.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.hibernate.orm:hibernate-core:7.2.24.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.hibernate.orm:hibernate-envers:7.2.24.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
org.hibernate.orm:hibernate-envers:7.2.24.Final=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.hibernate.validator:hibernate-validator:9.0.1.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.hibernate.validator:hibernate-validator:9.0.1.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.javassist:javassist:3.28.0-GA=checkstyle
|
org.javassist:javassist:3.28.0-GA=checkstyle
|
||||||
org.javassist:javassist:3.29.0-GA=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.javassist:javassist:3.29.0-GA=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.jboss.logging:jboss-logging:3.6.3.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.jboss.logging:jboss-logging:3.6.3.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.jetbrains:annotations:13.0=productionRuntimeClasspath,runtimeClasspath
|
org.jetbrains:annotations:13.0=productionRuntimeClasspath,runtimeClasspath
|
||||||
org.jetbrains:annotations:17.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.jetbrains:annotations:17.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.jspecify:jspecify:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestAnnotationProcessor,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,compileClasspath,conditionalTransportTestAnnotationProcessor,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,functionalTestCompileClasspath,integrationTestAnnotationProcessor,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
org.jspecify:jspecify:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,architectureTestCompileClasspath,architectureTestRuntimeClasspath,checkstyle,compileClasspath,conditionalTransportTestAnnotationProcessor,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestAnnotationProcessor,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestAnnotationProcessor,systemTestCompileClasspath,systemTestRuntimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter-api:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-api:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter-engine:6.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-engine:6.0.3=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter-params:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter-params:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.jupiter:junit-jupiter:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.jupiter:junit-jupiter:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-commons:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-commons:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-engine:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-engine:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-launcher:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-launcher:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit.platform:junit-platform-testkit:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit.platform:junit-platform-testkit:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit:junit-bom:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.junit:junit-bom:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.junit:junit-bom:6.1.0=spotbugs
|
org.junit:junit-bom:6.1.0=spotbugs
|
||||||
org.latencyutils:LatencyUtils:2.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.latencyutils:LatencyUtils:2.0.3=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.mockito:mockito-core:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,mockitoAgent,testCompileClasspath,testRuntimeClasspath
|
org.mockito:mockito-core:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,mockitoAgent,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.mockito:mockito-junit-jupiter:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.mockito:mockito-junit-jupiter:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.mongodb:bson-record-codec:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.mongodb:bson-record-codec:5.6.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.mongodb:bson:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.mongodb:bson:5.6.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.mongodb:mongodb-driver-core:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.mongodb:mongodb-driver-core:5.6.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.mongodb:mongodb-driver-sync:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.mongodb:mongodb-driver-sync:5.6.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.objenesis:objenesis:3.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
org.objenesis:objenesis:3.3=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.openapitools:jackson-databind-nullable:0.2.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.openapitools:jackson-databind-nullable:0.2.6=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.opentest4j:opentest4j:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.opentest4j:opentest4j:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.osgi:org.osgi.annotation.bundle:2.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.osgi:org.osgi.annotation.bundle:2.0.0=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.osgi:org.osgi.annotation.versioning:1.1.2=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.osgi:org.osgi.annotation.versioning:1.1.2=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.osgi:org.osgi.resource:1.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.osgi:org.osgi.resource:1.0.0=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.osgi:org.osgi.service.serviceloader:1.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.osgi:org.osgi.service.serviceloader:1.0.0=architectureTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.ow2.asm:asm-analysis:9.10.1=spotbugs
|
org.ow2.asm:asm-analysis:9.10.1=spotbugs
|
||||||
org.ow2.asm:asm-commons:9.10.1=spotbugs
|
org.ow2.asm:asm-commons:9.10.1=spotbugs
|
||||||
org.ow2.asm:asm-tree:9.10.1=spotbugs
|
org.ow2.asm:asm-tree:9.10.1=spotbugs
|
||||||
org.ow2.asm:asm-util:9.10.1=spotbugs
|
org.ow2.asm:asm-util:9.10.1=spotbugs
|
||||||
org.ow2.asm:asm:9.10.1=spotbugs
|
org.ow2.asm:asm:9.10.1=spotbugs
|
||||||
org.ow2.asm:asm:9.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.ow2.asm:asm:9.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.pcollections:pcollections:4.0.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor
|
org.pcollections:pcollections:4.0.1=annotationProcessor,architectureTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,integrationTestAnnotationProcessor,systemTestAnnotationProcessor,testAnnotationProcessor
|
||||||
org.postgresql:postgresql:42.7.13=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.postgresql:postgresql:42.7.13=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.reactivestreams:reactive-streams:1.0.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.reactivestreams:reactive-streams:1.0.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.reflections:reflections:0.10.2=checkstyle
|
org.reflections:reflections:0.10.2=checkstyle
|
||||||
org.rnorth.duct-tape:duct-tape:1.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.rnorth.duct-tape:duct-tape:1.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.skyscreamer:jsonassert:1.5.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.skyscreamer:jsonassert:1.5.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.slf4j:jul-to-slf4j:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.slf4j:jul-to-slf4j:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.slf4j:slf4j-api:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath
|
org.slf4j:slf4j-api:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,spotbugsSlf4j,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j
|
org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j
|
||||||
org.slf4j:slf4j-simple:2.0.18=checkstyle
|
org.slf4j:slf4j-simple:2.0.18=checkstyle
|
||||||
org.springdoc:springdoc-openapi-starter-common:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springdoc:springdoc-openapi-starter-common:3.0.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.0=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.amqp:spring-amqp:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.amqp:spring-amqp:4.0.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.amqp:spring-rabbit:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.amqp:spring-rabbit:4.0.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-configuration-processor:4.0.8=annotationProcessor
|
org.springframework.boot:spring-boot-configuration-processor:4.0.8=annotationProcessor
|
||||||
org.springframework.boot:spring-boot-data-commons:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-data-commons:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-data-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-data-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-data-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-data-mongodb:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-flyway:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-flyway:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-graphql:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-graphql:4.0.8=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-health:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-health:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-hibernate:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-hibernate:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-http-converter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-http-converter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jdbc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jdbc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-mail:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-mail:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-micrometer-observation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-micrometer-observation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-mongodb:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-persistence:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-persistence:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-reactor:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-reactor:4.0.8=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-resttestclient:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-resttestclient:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-servlet:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-servlet:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-sql:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-sql:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-data-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-data-mongodb:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-flyway:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-flyway:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-graphql:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-graphql:4.0.8=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-jackson-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-jackson-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-json:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-json:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-logging:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-logging:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-mail:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-mail:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-mongodb:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-tomcat-runtime:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-tomcat-runtime:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-web:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-web:4.0.8=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter-websocket:4.0.8=conditionalTransportTestRuntimeClasspath
|
org.springframework.boot:spring-boot-starter-websocket:4.0.8=conditionalTransportTestRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-starter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-starter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-test-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-test-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-transaction:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-transaction:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-web-server:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-web-server:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot-websocket:4.0.8=conditionalTransportTestRuntimeClasspath
|
org.springframework.boot:spring-boot-websocket:4.0.8=conditionalTransportTestRuntimeClasspath
|
||||||
org.springframework.boot:spring-boot:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.boot:spring-boot:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.cloud:spring-cloud-context:4.1.4=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.springframework.cloud:spring-cloud-context:4.1.4=architectureTestCompileClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.springframework.data:spring-data-commons:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.data:spring-data-commons:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.data:spring-data-jpa:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.data:spring-data-jpa:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.data:spring-data-mongodb:5.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.data:spring-data-mongodb:5.0.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.graphql:spring-graphql:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.graphql:spring-graphql:2.0.5=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.integration:spring-integration-core:7.0.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.integration:spring-integration-core:7.0.6=architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.integration:spring-integration-jdbc:7.0.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.integration:spring-integration-jdbc:7.0.6=architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.kafka:spring-kafka:4.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.kafka:spring-kafka:4.0.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-config:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-config:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-crypto:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-crypto:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-oauth2-client:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-oauth2-client:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-oauth2-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-oauth2-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-oauth2-jose:7.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-oauth2-jose:7.0.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-oauth2-resource-server:7.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-oauth2-resource-server:7.0.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-test:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-test:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.security:spring-security-web:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework.security:spring-security-web:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework.session:spring-session-core:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework.session:spring-session-core:4.0.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-aop:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-aop:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-aspects:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-aspects:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-beans:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-beans:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-context-support:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework:spring-context-support:7.0.9=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-context:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-context:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-core:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-core:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-expression:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-expression:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-jdbc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-jdbc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-messaging:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework:spring-messaging:7.0.9=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-orm:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-orm:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-test:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-test:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-tx:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-tx:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-web:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-web:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-webflux:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.springframework:spring-webflux:7.0.9=architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-webmvc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.springframework:spring-webmvc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.springframework:spring-websocket:7.0.9=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,testCompileClasspath
|
org.springframework:spring-websocket:7.0.9=architectureTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,systemTestCompileClasspath,testCompileClasspath
|
||||||
org.testcontainers:testcontainers-database-commons:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers-database-commons:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.testcontainers:testcontainers-jdbc:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers-jdbc:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.testcontainers:testcontainers-junit-jupiter:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers-junit-jupiter:2.0.5=architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.testcontainers:testcontainers-postgresql:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers-postgresql:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.testcontainers:testcontainers-toxiproxy:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers-toxiproxy:2.0.5=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.testcontainers:testcontainers:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.testcontainers:testcontainers:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.thymeleaf:thymeleaf:3.1.5.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.thymeleaf:thymeleaf:3.1.5.RELEASE=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.unbescape:unbescape:1.1.6.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.unbescape:unbescape:1.1.6.RELEASE=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.xerial.snappy:snappy-java:1.1.10.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
org.xerial.snappy:snappy-java:1.1.10.7=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs
|
org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs
|
||||||
org.xmlunit:xmlunit-core:2.10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.xmlunit:xmlunit-core:2.10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
org.yaml:snakeyaml:2.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
org.yaml:snakeyaml:2.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,compileClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
redis.clients.authentication:redis-authx-core:0.1.1-beta2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
redis.clients.authentication:redis-authx-core:0.1.1-beta2=architectureTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestRuntimeClasspath,testRuntimeClasspath
|
||||||
tools.jackson.core:jackson-core:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
tools.jackson.core:jackson-core:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
tools.jackson.core:jackson-databind:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
tools.jackson.core:jackson-databind:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
tools.jackson:jackson-bom:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
tools.jackson:jackson-bom:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,systemTestCompileClasspath,systemTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||||
empty=developmentOnly,testAndDevelopmentOnly
|
empty=developmentOnly,testAndDevelopmentOnly
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ public final class ProductionClassImportOption implements ImportOption {
|
|||||||
// sets.
|
// sets.
|
||||||
// Keep production scans source-set neutral as verification lanes are split physically.
|
// Keep production scans source-set neutral as verification lanes are split physically.
|
||||||
&& !location.contains("/architectureTest/")
|
&& !location.contains("/architectureTest/")
|
||||||
&& !location.contains("/bootCompositionTest/")
|
&& !location.contains("/systemTest/")
|
||||||
// The JPA testkit is on this module's test classpath so the production architecture suite
|
// The JPA testkit is on this module's test classpath so the production architecture suite
|
||||||
// can use its rule pack. Its fixtures exist to be violations — an entity with a
|
// can use its rule pack. Its fixtures exist to be violations — an entity with a
|
||||||
// varchar(255) id, a controller returning an entity — so importing them would have the
|
// varchar(255) id, a controller returning an entity — so importing them would have the
|
||||||
|
|||||||
+20
-3
@@ -6,6 +6,7 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -44,9 +45,9 @@ class ConditionalTransportCompositionContractTest {
|
|||||||
case "adapter-inbound-websocket" -> ":adapter:inbound:websocket";
|
case "adapter-inbound-websocket" -> ":adapter:inbound:websocket";
|
||||||
default -> throw new IllegalArgumentException(moduleId);
|
default -> throw new IllegalArgumentException(moduleId);
|
||||||
};
|
};
|
||||||
assertThat(bootstrapBuild)
|
assertThat(productionProjectDependency(bootstrapBuild, gradlePath))
|
||||||
.as("%s is qualification-only and must not ship in app-bootstrap", moduleId)
|
.as("%s is qualification-only and must not ship in app-bootstrap", moduleId)
|
||||||
.doesNotContain("project('" + gradlePath + "')");
|
.isFalse();
|
||||||
assertThatCodeLoads(typeName);
|
assertThatCodeLoads(typeName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -60,7 +61,8 @@ class ConditionalTransportCompositionContractTest {
|
|||||||
SWITCH_GATED_TRANSPORTS
|
SWITCH_GATED_TRANSPORTS
|
||||||
.values()
|
.values()
|
||||||
.forEach(ConditionalTransportCompositionContractTest::assertThatCodeLoads);
|
.forEach(ConditionalTransportCompositionContractTest::assertThatCodeLoads);
|
||||||
assertThat(shippedApplicationYaml()).contains("${APP_GRAPHQL_ENABLED:false}");
|
assertThat(shippedApplicationYaml()).contains("classpath:config/graphql.yml");
|
||||||
|
assertThat(shippedGraphQlYaml()).contains("${APP_GRAPHQL_ENABLED:false}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -86,6 +88,21 @@ class ConditionalTransportCompositionContractTest {
|
|||||||
repositoryRoot().resolve("src/app-bootstrap/src/main/resources/application.yml"));
|
repositoryRoot().resolve("src/app-bootstrap/src/main/resources/application.yml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String shippedGraphQlYaml() throws IOException {
|
||||||
|
return Files.readString(
|
||||||
|
repositoryRoot().resolve("src/app-bootstrap/src/main/resources/config/graphql.yml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean productionProjectDependency(String buildScript, String gradlePath) {
|
||||||
|
String quotedPath = Pattern.quote(gradlePath);
|
||||||
|
Pattern productionDependency =
|
||||||
|
Pattern.compile(
|
||||||
|
"(?m)^\\s*(?:api|implementation|compileOnly|runtimeOnly)\\s*\\(?\\s*project\\(\\s*['\"]"
|
||||||
|
+ quotedPath
|
||||||
|
+ "['\"]\\s*\\)\\s*\\)?");
|
||||||
|
return productionDependency.matcher(buildScript).find();
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertThatCodeLoads(String typeName) {
|
private static void assertThatCodeLoads(String typeName) {
|
||||||
try {
|
try {
|
||||||
assertThat(Class.forName(typeName)).isNotNull();
|
assertThat(Class.forName(typeName)).isNotNull();
|
||||||
|
|||||||
-148
@@ -1,148 +0,0 @@
|
|||||||
package dev.caskeleton.bootstrap.contract;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import org.gradle.testkit.runner.BuildResult;
|
|
||||||
import org.gradle.testkit.runner.GradleRunner;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies that release provenance is a release concern rather than a configuration-time build
|
|
||||||
* prerequisite.
|
|
||||||
*/
|
|
||||||
class ReleaseProvenanceFunctionalTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void aBuildWithNoGitMetadataConfiguresAndIsNotCalledARelease(@TempDir Path fixtureRoot)
|
|
||||||
throws Exception {
|
|
||||||
Path fixtureSrc = fixtureRoot.resolve("src");
|
|
||||||
copyBuildFixture(fixtureSrc);
|
|
||||||
assertThat(fixtureSrc.resolve(".git")).doesNotExist();
|
|
||||||
|
|
||||||
BuildResult configured = run(fixtureSrc, "help");
|
|
||||||
assertThat(configured.getOutput()).contains("BUILD SUCCESSFUL");
|
|
||||||
|
|
||||||
BuildResult provenance = runAndFail(fixtureSrc, "verifyReleaseProvenance");
|
|
||||||
assertThat(provenance.getOutput()).contains("no source revision");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void anAttestedRevisionSatisfiesReleaseProvenanceWithoutGit(@TempDir Path fixtureRoot)
|
|
||||||
throws Exception {
|
|
||||||
Path fixtureSrc = fixtureRoot.resolve("src");
|
|
||||||
copyBuildFixture(fixtureSrc);
|
|
||||||
|
|
||||||
BuildResult result =
|
|
||||||
run(
|
|
||||||
fixtureSrc,
|
|
||||||
"verifyReleaseProvenance",
|
|
||||||
"-PgitRevision=0123456789abcdef0123456789abcdef01234567");
|
|
||||||
|
|
||||||
assertThat(result.getOutput()).contains("verifyReleaseProvenance: OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BuildResult run(Path projectDirectory, String... arguments) {
|
|
||||||
return runner(projectDirectory, arguments).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BuildResult runAndFail(Path projectDirectory, String... arguments) {
|
|
||||||
return runner(projectDirectory, arguments).buildAndFail();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GradleRunner runner(Path projectDirectory, String... arguments) {
|
|
||||||
String[] fullArguments = new String[arguments.length + 2];
|
|
||||||
fullArguments[0] = "--console=plain";
|
|
||||||
fullArguments[1] = "--stacktrace";
|
|
||||||
System.arraycopy(arguments, 0, fullArguments, 2, arguments.length);
|
|
||||||
return GradleRunner.create()
|
|
||||||
.withProjectDir(projectDirectory.toFile())
|
|
||||||
// Fixtures stay isolated; Gradle's immutable dependency/plugin caches and daemon do not.
|
|
||||||
.withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile())
|
|
||||||
.withArguments(fullArguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyBuildFixture(Path fixtureSrc) throws IOException {
|
|
||||||
Path sourceRoot = sourceRoot();
|
|
||||||
copyTree(sourceRoot.resolve("gradle"), fixtureSrc.resolve("gradle"));
|
|
||||||
copyTree(sourceRoot.resolve("config"), fixtureSrc.resolve("config"));
|
|
||||||
Path gradleProperties = sourceRoot.resolve("gradle.properties");
|
|
||||||
if (Files.isRegularFile(gradleProperties)) {
|
|
||||||
copyFile(gradleProperties, fixtureSrc.resolve("gradle.properties"));
|
|
||||||
}
|
|
||||||
copyIncludedBuildSources(sourceRoot.resolve("build-logic"), fixtureSrc.resolve("build-logic"));
|
|
||||||
copyIncludedBuildSources(
|
|
||||||
sourceRoot.resolve("build-qualification"), fixtureSrc.resolve("build-qualification"));
|
|
||||||
|
|
||||||
try (Stream<Path> paths = Files.walk(sourceRoot)) {
|
|
||||||
paths
|
|
||||||
.filter(Files::isRegularFile)
|
|
||||||
.filter(
|
|
||||||
path ->
|
|
||||||
path.getFileName().toString().equals("build.gradle")
|
|
||||||
|| path.getFileName().toString().equals("gradle.lockfile")
|
|
||||||
|| path.equals(sourceRoot.resolve("settings.gradle")))
|
|
||||||
.filter(path -> !path.startsWith(sourceRoot.resolve("build")))
|
|
||||||
.forEach(
|
|
||||||
source -> {
|
|
||||||
try {
|
|
||||||
copyFile(source, fixtureSrc.resolve(sourceRoot.relativize(source)));
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"failed to copy Gradle fixture input " + source, exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyIncludedBuildSources(Path sourceRoot, Path targetRoot)
|
|
||||||
throws IOException {
|
|
||||||
copyFile(sourceRoot.resolve("settings.gradle"), targetRoot.resolve("settings.gradle"));
|
|
||||||
copyFile(sourceRoot.resolve("build.gradle"), targetRoot.resolve("build.gradle"));
|
|
||||||
Path sourceDirectory = sourceRoot.resolve("src");
|
|
||||||
if (Files.isDirectory(sourceDirectory)) {
|
|
||||||
copyTree(sourceDirectory, targetRoot.resolve("src"));
|
|
||||||
}
|
|
||||||
Path lockfile = sourceRoot.resolve("gradle.lockfile");
|
|
||||||
if (Files.isRegularFile(lockfile)) {
|
|
||||||
copyFile(lockfile, targetRoot.resolve("gradle.lockfile"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyTree(Path sourceRoot, Path targetRoot) throws IOException {
|
|
||||||
try (Stream<Path> paths = Files.walk(sourceRoot)) {
|
|
||||||
paths
|
|
||||||
.filter(Files::isRegularFile)
|
|
||||||
.forEach(
|
|
||||||
source -> {
|
|
||||||
try {
|
|
||||||
copyFile(source, targetRoot.resolve(sourceRoot.relativize(source)));
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new IllegalStateException("failed to copy tree input " + source, exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyFile(Path source, Path target) throws IOException {
|
|
||||||
Files.createDirectories(target.getParent());
|
|
||||||
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path sourceRoot() {
|
|
||||||
for (Path candidate = Path.of("").toAbsolutePath();
|
|
||||||
candidate != null;
|
|
||||||
candidate = candidate.getParent()) {
|
|
||||||
if (Files.isRegularFile(candidate.resolve("gradlew"))
|
|
||||||
&& Files.isDirectory(candidate.resolve("app-bootstrap"))) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("repository src root not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-3
@@ -3,8 +3,8 @@ package dev.caskeleton.bootstrap.integration.outbox;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxEventJpaRepository;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxEventJpaRepository;
|
||||||
|
import dev.caskeleton.application.outbox.LegacyOutboxAppendPort;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
|
||||||
import dev.caskeleton.application.transaction.TransactionPort;
|
import dev.caskeleton.application.transaction.TransactionPort;
|
||||||
import dev.caskeleton.bootstrap.integration.PostgreSqlTestContainer;
|
import dev.caskeleton.bootstrap.integration.PostgreSqlTestContainer;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
@@ -57,7 +57,7 @@ class OutboxAppendTransactionalContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
|
|
||||||
String eventId = "txn-rollback-" + System.nanoTime();
|
String eventId = "txn-rollback-" + System.nanoTime();
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class OutboxAppendTransactionalContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
|
|
||||||
String eventId = "txn-commit-" + System.nanoTime();
|
String eventId = "txn-commit-" + System.nanoTime();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@ package dev.caskeleton.bootstrap.integration.outbox;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.outbox.LegacyOutboxAppendPort;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
|
||||||
import dev.caskeleton.application.outbox.OutboxRelayResult;
|
import dev.caskeleton.application.outbox.OutboxRelayResult;
|
||||||
import dev.caskeleton.application.outbox.PublishPendingOutboxEventsCommand;
|
import dev.caskeleton.application.outbox.PublishPendingOutboxEventsCommand;
|
||||||
import dev.caskeleton.application.outbox.PublishPendingOutboxEventsUseCase;
|
import dev.caskeleton.application.outbox.PublishPendingOutboxEventsUseCase;
|
||||||
@@ -76,7 +76,7 @@ class OutboxPublisherLeaderElectionContractTest {
|
|||||||
AnnotationConfigApplicationContext seedCtx =
|
AnnotationConfigApplicationContext seedCtx =
|
||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock);
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock);
|
||||||
TransactionPort seedTx = seedCtx.getBean(TransactionPort.class);
|
TransactionPort seedTx = seedCtx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort appendPort = seedCtx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort appendPort = seedCtx.getBean(LegacyOutboxAppendPort.class);
|
||||||
|
|
||||||
seedTx.inWrite(
|
seedTx.inWrite(
|
||||||
() -> {
|
() -> {
|
||||||
|
|||||||
+13
-13
@@ -5,10 +5,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxEventJpaRepository;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxEventJpaRepository;
|
||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxReaper;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxReaper;
|
||||||
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxStoreAdapter;
|
import dev.caskeleton.adapter.outbound.persistence.outbox.OutboxStoreAdapter;
|
||||||
|
import dev.caskeleton.application.outbox.ClaimedOutboxEvent;
|
||||||
|
import dev.caskeleton.application.outbox.LegacyOutboxAppendPort;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
|
||||||
import dev.caskeleton.application.outbox.OutboxBackoffPolicy;
|
import dev.caskeleton.application.outbox.OutboxBackoffPolicy;
|
||||||
import dev.caskeleton.application.outbox.OutboxEvent;
|
|
||||||
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
import dev.caskeleton.application.outbox.OutboxEventStatus;
|
||||||
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
||||||
import dev.caskeleton.application.outbox.OutboxRelayResult;
|
import dev.caskeleton.application.outbox.OutboxRelayResult;
|
||||||
@@ -82,7 +82,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String eventId = "lifecycle-happy-" + System.nanoTime();
|
String eventId = "lifecycle-happy-" + System.nanoTime();
|
||||||
@@ -132,7 +132,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
clock)) {
|
clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String eventId = "lifecycle-fail-" + System.nanoTime();
|
String eventId = "lifecycle-fail-" + System.nanoTime();
|
||||||
@@ -188,7 +188,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
appendClock)) {
|
appendClock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String eventId = "lifecycle-dead-" + System.nanoTime();
|
String eventId = "lifecycle-dead-" + System.nanoTime();
|
||||||
@@ -255,7 +255,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
|
|
||||||
String aggId = "agg-fifo-order-" + System.nanoTime();
|
String aggId = "agg-fifo-order-" + System.nanoTime();
|
||||||
String headId = "fifo-order-head-" + System.nanoTime();
|
String headId = "fifo-order-head-" + System.nanoTime();
|
||||||
@@ -324,7 +324,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
|
|
||||||
String aggregateId = "agg-fifo-tie-" + System.nanoTime();
|
String aggregateId = "agg-fifo-tie-" + System.nanoTime();
|
||||||
String firstId = "fifo-tie-first-" + System.nanoTime();
|
String firstId = "fifo-tie-first-" + System.nanoTime();
|
||||||
@@ -378,7 +378,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
clock)) {
|
clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String aggId = "agg-fifo-blocked-" + System.nanoTime();
|
String aggId = "agg-fifo-blocked-" + System.nanoTime();
|
||||||
@@ -446,7 +446,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String aggId = "agg-fifo-unblock-" + System.nanoTime();
|
String aggId = "agg-fifo-unblock-" + System.nanoTime();
|
||||||
@@ -513,7 +513,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
Clock.fixed(t0, ZoneOffset.UTC))) {
|
Clock.fixed(t0, ZoneOffset.UTC))) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String aggId = "agg-fifo-dead-" + System.nanoTime();
|
String aggId = "agg-fifo-dead-" + System.nanoTime();
|
||||||
@@ -589,7 +589,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, claimClock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, claimClock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String eventId = "lifecycle-orphan-" + System.nanoTime();
|
String eventId = "lifecycle-orphan-" + System.nanoTime();
|
||||||
@@ -646,7 +646,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
OutboxContainerTestSupport.buildContext(sharedDataSource, e -> {}, clock)) {
|
||||||
|
|
||||||
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
TransactionPort tx = ctx.getBean(TransactionPort.class);
|
||||||
OutboxAppendPort append = ctx.getBean(OutboxAppendPort.class);
|
LegacyOutboxAppendPort append = ctx.getBean(LegacyOutboxAppendPort.class);
|
||||||
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
OutboxStoreAdapter store = ctx.getBean(OutboxStoreAdapter.class);
|
||||||
|
|
||||||
String eventId = "lifecycle-reap-" + System.nanoTime();
|
String eventId = "lifecycle-reap-" + System.nanoTime();
|
||||||
@@ -659,7 +659,7 @@ class OutboxRowLifecycleContractTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Claim and mark as PUBLISHED
|
// Claim and mark as PUBLISHED
|
||||||
List<OutboxEvent> claimed =
|
List<ClaimedOutboxEvent> claimed =
|
||||||
tx.inWrite(() -> store.claimBatch(1, eventTime, Duration.ofSeconds(1)));
|
tx.inWrite(() -> store.claimBatch(1, eventTime, Duration.ofSeconds(1)));
|
||||||
assertThat(claimed).hasSize(1);
|
assertThat(claimed).hasSize(1);
|
||||||
tx.inWrite(
|
tx.inWrite(
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package dev.caskeleton.bootstrap.autoconfigure.outbox;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.bootstrap.runtime.startup.StartupFailures;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail-closed activation guard for the transport-only canonical outbox path.
|
||||||
|
*
|
||||||
|
* <p>Turning on canonical transport without a canonical publisher would let the legacy relay claim
|
||||||
|
* a canonical row and then spend its retry budget against a disabled transport. The flag therefore
|
||||||
|
* requires the application-owned canonical publish port at startup.
|
||||||
|
*/
|
||||||
|
public final class OutboxCanonicalTransportRequirementValidator implements InitializingBean {
|
||||||
|
|
||||||
|
private static final String CANONICAL_KEY = "ca-skeleton.outbox.canonical-transport-enabled";
|
||||||
|
|
||||||
|
private final OutboxSettings settings;
|
||||||
|
private final IntegrationEventPublishPort publisher;
|
||||||
|
|
||||||
|
public OutboxCanonicalTransportRequirementValidator(
|
||||||
|
OutboxSettings settings, IntegrationEventPublishPort publisher) {
|
||||||
|
this.settings = Objects.requireNonNull(settings, "settings must not be null");
|
||||||
|
this.publisher = publisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate() {
|
||||||
|
if (!settings.canonicalTransportEnabled() || publisher != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw StartupFailures.requiredAdapterDisabled(
|
||||||
|
CANONICAL_KEY
|
||||||
|
+ "=true requires an IntegrationEventPublishPort so canonical claimed rows cannot be "
|
||||||
|
+ "consumed by the relay without a platform publication route. Configure the canonical "
|
||||||
|
+ "messaging bridge (including app.messaging.producer-id) or set "
|
||||||
|
+ CANONICAL_KEY
|
||||||
|
+ "=false.");
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -1,5 +1,6 @@
|
|||||||
package dev.caskeleton.bootstrap.autoconfigure.outbox;
|
package dev.caskeleton.bootstrap.autoconfigure.outbox;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
import dev.caskeleton.application.outbox.OutboxBackoffPolicy;
|
import dev.caskeleton.application.outbox.OutboxBackoffPolicy;
|
||||||
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
import dev.caskeleton.application.outbox.OutboxMessagePublishPort;
|
||||||
import dev.caskeleton.application.outbox.OutboxRelayFailureReportPort;
|
import dev.caskeleton.application.outbox.OutboxRelayFailureReportPort;
|
||||||
@@ -43,6 +44,15 @@ public class OutboxConfig {
|
|||||||
return new OutboxRelayBrokerRequirementValidator(environment);
|
return new OutboxRelayBrokerRequirementValidator(environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Requires the canonical messaging bridge whenever canonical outbox transport is enabled. */
|
||||||
|
@Bean
|
||||||
|
public OutboxCanonicalTransportRequirementValidator outboxCanonicalTransportRequirementValidator(
|
||||||
|
OutboxSettings settings,
|
||||||
|
ObjectProvider<IntegrationEventPublishPort> canonicalPublisherProvider) {
|
||||||
|
return new OutboxCanonicalTransportRequirementValidator(
|
||||||
|
settings, canonicalPublisherProvider.getIfAvailable());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relay use case is assembled manually here (not a bean) from {@link OutboxSettings} values. See
|
* Relay use case is assembled manually here (not a bean) from {@link OutboxSettings} values. See
|
||||||
* README for the design rationale.
|
* README for the design rationale.
|
||||||
|
|||||||
+6
@@ -10,6 +10,8 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
* placeholders). See README for the design rationale.
|
* placeholders). See README for the design rationale.
|
||||||
*
|
*
|
||||||
* @param relayEnabled whether the relay scheduler is enabled; default {@code true}
|
* @param relayEnabled whether the relay scheduler is enabled; default {@code true}
|
||||||
|
* @param canonicalTransportEnabled whether canonical rows may use the platform transport; default
|
||||||
|
* {@code false}
|
||||||
* @param pollInterval how often the relay polls the outbox table; default {@code PT5S}
|
* @param pollInterval how often the relay polls the outbox table; default {@code PT5S}
|
||||||
* @param batchSize maximum rows claimed per relay cycle; default {@code 20}
|
* @param batchSize maximum rows claimed per relay cycle; default {@code 20}
|
||||||
* @param inFlightTimeout in-flight orphan visibility window; default {@code PT5M}
|
* @param inFlightTimeout in-flight orphan visibility window; default {@code PT5M}
|
||||||
@@ -21,6 +23,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
@ConfigurationProperties(prefix = "ca-skeleton.outbox")
|
@ConfigurationProperties(prefix = "ca-skeleton.outbox")
|
||||||
public record OutboxSettings(
|
public record OutboxSettings(
|
||||||
Boolean relayEnabled,
|
Boolean relayEnabled,
|
||||||
|
Boolean canonicalTransportEnabled,
|
||||||
Duration pollInterval,
|
Duration pollInterval,
|
||||||
Integer batchSize,
|
Integer batchSize,
|
||||||
Duration inFlightTimeout,
|
Duration inFlightTimeout,
|
||||||
@@ -31,6 +34,9 @@ public record OutboxSettings(
|
|||||||
if (relayEnabled == null) {
|
if (relayEnabled == null) {
|
||||||
relayEnabled = true;
|
relayEnabled = true;
|
||||||
}
|
}
|
||||||
|
if (canonicalTransportEnabled == null) {
|
||||||
|
canonicalTransportEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (pollInterval == null) {
|
if (pollInterval == null) {
|
||||||
pollInterval = Duration.ofSeconds(5);
|
pollInterval = Duration.ofSeconds(5);
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ ca-skeleton:
|
|||||||
# port that a database-less deployment does not have.
|
# port that a database-less deployment does not have.
|
||||||
enabled: ${APP_OUTBOX_ENABLED:false}
|
enabled: ${APP_OUTBOX_ENABLED:false}
|
||||||
relay-enabled: ${APP_OUTBOX_RELAY_ENABLED:false}
|
relay-enabled: ${APP_OUTBOX_RELAY_ENABLED:false}
|
||||||
|
# Transport-only MSG-015 cutover gate. It does not activate POLLING_V2 or a second relay.
|
||||||
|
# When true, startup also requires the canonical IntegrationEventPublishPort.
|
||||||
|
canonical-transport-enabled: ${APP_OUTBOX_CANONICAL_TRANSPORT_ENABLED:false}
|
||||||
# ISO-8601 duration — how often the relay polls for pending rows
|
# ISO-8601 duration — how often the relay polls for pending rows
|
||||||
poll-interval: PT5S
|
poll-interval: PT5S
|
||||||
# integer >= 1 — maximum rows claimed per relay cycle
|
# integer >= 1 — maximum rows claimed per relay cycle
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package dev.caskeleton.bootstrap.autoconfigure.outbox;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxStorePort;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
|
||||||
|
class OutboxCanonicalTransportCompositionTest {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner runner =
|
||||||
|
new ApplicationContextRunner()
|
||||||
|
.withUserConfiguration(OutboxConfig.class)
|
||||||
|
.withBean(OutboxStorePort.class, () -> mock(OutboxStorePort.class))
|
||||||
|
.withPropertyValues(
|
||||||
|
"ca-skeleton.outbox.enabled=true", "ca-skeleton.outbox.relay-enabled=false");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void defaultCanonicalTransportOffStartsWithoutCanonicalPublisher() {
|
||||||
|
runner.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).hasSingleBean(OutboxCanonicalTransportRequirementValidator.class);
|
||||||
|
assertThat(context).doesNotHaveBean(IntegrationEventPublishPort.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canonicalTransportOnWithoutPublisherFailsTheComposedContext() {
|
||||||
|
runner
|
||||||
|
.withPropertyValues("ca-skeleton.outbox.canonical-transport-enabled=true")
|
||||||
|
.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasFailed();
|
||||||
|
assertThat(context.getStartupFailure())
|
||||||
|
.hasMessageContaining("IntegrationEventPublishPort");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void canonicalTransportOnWithPublisherStartsWithoutCreatingAnotherScheduler() {
|
||||||
|
runner
|
||||||
|
.withPropertyValues("ca-skeleton.outbox.canonical-transport-enabled=true")
|
||||||
|
.withBean(
|
||||||
|
IntegrationEventPublishPort.class,
|
||||||
|
() -> event -> CompletableFuture.completedFuture(OutboxPublishOutcome.CONFIRMED))
|
||||||
|
.run(
|
||||||
|
context -> {
|
||||||
|
assertThat(context).hasNotFailed();
|
||||||
|
assertThat(context).hasSingleBean(IntegrationEventPublishPort.class);
|
||||||
|
assertThat(context).doesNotHaveBean(OutboxRelayScheduler.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package dev.caskeleton.bootstrap.autoconfigure.outbox;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.IntegrationEventPublishPort;
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class OutboxCanonicalTransportRequirementValidatorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void enabledCanonicalTransportWithoutPublisherFailsStartup() {
|
||||||
|
OutboxSettings settings = new OutboxSettings(true, true, null, null, null, null, null);
|
||||||
|
|
||||||
|
assertThatThrownBy(
|
||||||
|
() -> new OutboxCanonicalTransportRequirementValidator(settings, null).validate())
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("canonical-transport-enabled")
|
||||||
|
.hasMessageContaining("IntegrationEventPublishPort");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disabledCanonicalTransportDoesNotRequirePublisher() {
|
||||||
|
OutboxSettings settings = new OutboxSettings(true, false, null, null, null, null, null);
|
||||||
|
|
||||||
|
assertThatCode(
|
||||||
|
() -> new OutboxCanonicalTransportRequirementValidator(settings, null).validate())
|
||||||
|
.doesNotThrowAnyException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void enabledCanonicalTransportWithPublisherStarts() {
|
||||||
|
OutboxSettings settings = new OutboxSettings(true, true, null, null, null, null, null);
|
||||||
|
IntegrationEventPublishPort publisher =
|
||||||
|
event -> CompletableFuture.completedFuture(OutboxPublishOutcome.CONFIRMED);
|
||||||
|
|
||||||
|
assertThatCode(
|
||||||
|
() -> new OutboxCanonicalTransportRequirementValidator(settings, publisher).validate())
|
||||||
|
.doesNotThrowAnyException();
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -52,6 +52,19 @@ class OutboxRelayBrokerRequirementValidatorTest {
|
|||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("canonical transport does not waive the legacy broker during the mixed-row window")
|
||||||
|
void canonicalTransportStillRequiresLegacyBrokerDuringCompatibilityWindow() {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
environment.setProperty("ca-skeleton.outbox.relay-enabled", "true");
|
||||||
|
environment.setProperty("ca-skeleton.outbox.canonical-transport-enabled", "true");
|
||||||
|
environment.setProperty("app.messaging.broker", "");
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> new OutboxRelayBrokerRequirementValidator(environment).validate())
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("app.messaging.broker");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("a disabled relay with no broker starts, and its PENDING rows are preserved")
|
@DisplayName("a disabled relay with no broker starts, and its PENDING rows are preserved")
|
||||||
void aDisabledRelayWithNoBrokerStarts() {
|
void aDisabledRelayWithNoBrokerStarts() {
|
||||||
|
|||||||
+13
-9
@@ -15,8 +15,9 @@ class OutboxSettingsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void bindsAllSixDefaultsWhenFieldsAreNull() {
|
void bindsAllSixDefaultsWhenFieldsAreNull() {
|
||||||
OutboxSettings props = new OutboxSettings(null, null, null, null, null, null);
|
OutboxSettings props = new OutboxSettings(null, null, null, null, null, null, null);
|
||||||
assertThat(props.relayEnabled()).isTrue();
|
assertThat(props.relayEnabled()).isTrue();
|
||||||
|
assertThat(props.canonicalTransportEnabled()).isFalse();
|
||||||
assertThat(props.pollInterval()).isEqualTo(Duration.ofSeconds(5));
|
assertThat(props.pollInterval()).isEqualTo(Duration.ofSeconds(5));
|
||||||
assertThat(props.batchSize()).isEqualTo(20);
|
assertThat(props.batchSize()).isEqualTo(20);
|
||||||
assertThat(props.inFlightTimeout()).isEqualTo(Duration.ofMinutes(5));
|
assertThat(props.inFlightTimeout()).isEqualTo(Duration.ofMinutes(5));
|
||||||
@@ -29,12 +30,14 @@ class OutboxSettingsTest {
|
|||||||
OutboxSettings props =
|
OutboxSettings props =
|
||||||
new OutboxSettings(
|
new OutboxSettings(
|
||||||
false,
|
false,
|
||||||
|
true,
|
||||||
Duration.ofSeconds(10),
|
Duration.ofSeconds(10),
|
||||||
50,
|
50,
|
||||||
Duration.ofMinutes(2),
|
Duration.ofMinutes(2),
|
||||||
Duration.ofHours(1),
|
Duration.ofHours(1),
|
||||||
Duration.ofDays(14));
|
Duration.ofDays(14));
|
||||||
assertThat(props.relayEnabled()).isFalse();
|
assertThat(props.relayEnabled()).isFalse();
|
||||||
|
assertThat(props.canonicalTransportEnabled()).isTrue();
|
||||||
assertThat(props.pollInterval()).isEqualTo(Duration.ofSeconds(10));
|
assertThat(props.pollInterval()).isEqualTo(Duration.ofSeconds(10));
|
||||||
assertThat(props.batchSize()).isEqualTo(50);
|
assertThat(props.batchSize()).isEqualTo(50);
|
||||||
assertThat(props.inFlightTimeout()).isEqualTo(Duration.ofMinutes(2));
|
assertThat(props.inFlightTimeout()).isEqualTo(Duration.ofMinutes(2));
|
||||||
@@ -44,35 +47,35 @@ class OutboxSettingsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsZeroBatchSize() {
|
void rejectsZeroBatchSize() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, 0, null, null, null))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, null, 0, null, null, null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("batchSize");
|
.hasMessageContaining("batchSize");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsNegativeBatchSize() {
|
void rejectsNegativeBatchSize() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, -1, null, null, null))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, null, -1, null, null, null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("batchSize");
|
.hasMessageContaining("batchSize");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsNonPositivePollInterval() {
|
void rejectsNonPositivePollInterval() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, Duration.ZERO, null, null, null, null))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, Duration.ZERO, null, null, null, null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("pollInterval");
|
.hasMessageContaining("pollInterval");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsNonPositiveInFlightTimeout() {
|
void rejectsNonPositiveInFlightTimeout() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, null, Duration.ZERO, null, null))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, null, null, Duration.ZERO, null, null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("inFlightTimeout");
|
.hasMessageContaining("inFlightTimeout");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsZeroReaperInterval() {
|
void rejectsZeroReaperInterval() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, null, null, Duration.ZERO, null))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, null, null, null, Duration.ZERO, null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("reaperInterval");
|
.hasMessageContaining("reaperInterval");
|
||||||
}
|
}
|
||||||
@@ -80,21 +83,22 @@ class OutboxSettingsTest {
|
|||||||
@Test
|
@Test
|
||||||
void rejectsNegativeReaperInterval() {
|
void rejectsNegativeReaperInterval() {
|
||||||
assertThatThrownBy(
|
assertThatThrownBy(
|
||||||
() -> new OutboxSettings(true, null, null, null, Duration.ofMinutes(-1), null))
|
() -> new OutboxSettings(true, false, null, null, null, Duration.ofMinutes(-1), null))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("reaperInterval");
|
.hasMessageContaining("reaperInterval");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsZeroPublishedRetention() {
|
void rejectsZeroPublishedRetention() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, null, null, null, Duration.ZERO))
|
assertThatThrownBy(() -> new OutboxSettings(true, false, null, null, null, null, Duration.ZERO))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("publishedRetention");
|
.hasMessageContaining("publishedRetention");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsNegativePublishedRetention() {
|
void rejectsNegativePublishedRetention() {
|
||||||
assertThatThrownBy(() -> new OutboxSettings(true, null, null, null, null, Duration.ofHours(-1)))
|
assertThatThrownBy(
|
||||||
|
() -> new OutboxSettings(true, false, null, null, null, null, Duration.ofHours(-1)))
|
||||||
.isInstanceOf(StartupValidationException.class)
|
.isInstanceOf(StartupValidationException.class)
|
||||||
.hasMessageContaining("publishedRetention");
|
.hasMessageContaining("publishedRetention");
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-18
@@ -31,39 +31,29 @@ class ConditionalTransportQualificationContractTest {
|
|||||||
@Test
|
@Test
|
||||||
void ownerQualificationsNameEveryRequiredWireClassAndRootOnlyAggregates() throws IOException {
|
void ownerQualificationsNameEveryRequiredWireClassAndRootOnlyAggregates() throws IOException {
|
||||||
Path root = repositoryRoot();
|
Path root = repositoryRoot();
|
||||||
String convention =
|
|
||||||
Files.readString(
|
|
||||||
root.resolve("src/build-logic/src/main/groovy/ca.strict-qualification.gradle"));
|
|
||||||
String rootBuild = Files.readString(root.resolve("src/build.gradle"));
|
String rootBuild = Files.readString(root.resolve("src/build.gradle"));
|
||||||
String graphql = Files.readString(root.resolve("src/adapter/inbound/graphql/build.gradle"));
|
String graphql = Files.readString(root.resolve("src/adapter/inbound/graphql/build.gradle"));
|
||||||
String grpc = Files.readString(root.resolve("src/adapter/inbound/grpc/build.gradle"));
|
String grpc = Files.readString(root.resolve("src/adapter/inbound/grpc/build.gradle"));
|
||||||
String websocket = Files.readString(root.resolve("src/adapter/inbound/websocket/build.gradle"));
|
String websocket = Files.readString(root.resolve("src/adapter/inbound/websocket/build.gradle"));
|
||||||
|
|
||||||
assertThat(convention)
|
|
||||||
.contains("failOnNoMatchingTests = true")
|
|
||||||
.contains("failOnNoDiscoveredTests = true")
|
|
||||||
.contains("forbids skipped tests")
|
|
||||||
.contains("verifyRequiredJUnitClasses");
|
|
||||||
assertThat(graphql)
|
assertThat(graphql)
|
||||||
.contains("registerStrictQualificationTest")
|
.contains("extensions.getByName('strictQualification').register(")
|
||||||
.contains("dev.caskeleton.adapter.inbound.graphql.GraphqlHttpBoundaryQualificationTest");
|
.contains("dev.caskeleton.adapter.inbound.graphql.GraphqlHttpBoundaryQualificationTest");
|
||||||
assertThat(grpc)
|
assertThat(grpc)
|
||||||
.contains("registerStrictQualificationTest")
|
.contains("extensions.getByName('strictQualification').register(")
|
||||||
.contains("dev.caskeleton.adapter.inbound.grpc.GrpcSafeActivationTest")
|
.contains("dev.caskeleton.adapter.inbound.grpc.GrpcSafeActivationTest")
|
||||||
.contains("dev.caskeleton.adapter.inbound.grpc.GrpcP1BoundaryWireTest");
|
.contains("dev.caskeleton.adapter.inbound.grpc.GrpcP1BoundaryWireTest");
|
||||||
assertThat(websocket)
|
assertThat(websocket)
|
||||||
.contains("registerStrictQualificationTest")
|
.contains("extensions.getByName('strictQualification').register(")
|
||||||
.contains(
|
.contains(
|
||||||
"dev.caskeleton.adapter.inbound.websocket.stomp.WebSocketBoundaryQualificationTest");
|
"dev.caskeleton.adapter.inbound.websocket.stomp.WebSocketBoundaryQualificationTest");
|
||||||
|
|
||||||
// The root aggregates and knows no test class name. A root that named the wire classes would be
|
// The root delegates aggregation to the Java convention and knows no wire-test class name.
|
||||||
// a second place to update when a leaf renames one, and the leaf's own lane is the one that
|
// The convention's TestKit contract owns the concrete aggregate task graph; this repository
|
||||||
// fails closed on a class it cannot discover.
|
// contract only prevents the root script from growing a second implementation of it.
|
||||||
assertThat(rootBuild)
|
assertThat(rootBuild)
|
||||||
.contains("tasks.register('conditionalTransportQualification')")
|
.contains("id 'ca.conditional-transport-qualification'")
|
||||||
.contains(":adapter:inbound:graphql:graphqlTransportQualificationTest")
|
.doesNotContain("tasks.register('conditionalTransportQualification')")
|
||||||
.contains(":adapter:inbound:grpc:grpcTransportQualificationTest")
|
|
||||||
.contains(":adapter:inbound:websocket:websocketTransportQualificationTest")
|
|
||||||
.doesNotContain("registerConditionalTransportQualificationTest")
|
.doesNotContain("registerConditionalTransportQualificationTest")
|
||||||
.doesNotContain("GraphqlHttpBoundaryQualificationTest")
|
.doesNotContain("GraphqlHttpBoundaryQualificationTest")
|
||||||
.doesNotContain("GrpcP1BoundaryWireTest")
|
.doesNotContain("GrpcP1BoundaryWireTest")
|
||||||
|
|||||||
+12
-10
@@ -33,19 +33,21 @@ class DeveloperExperienceContractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void bootstrapIsOneOrderedFourStageGradleEntrypoint() throws IOException {
|
void bootstrapEntrypointIsOwnedByDeveloperBootstrapPlugin() throws IOException {
|
||||||
String build = read("src/build.gradle");
|
String build = read("src/build.gradle");
|
||||||
|
|
||||||
|
// Registration and ordering are executable behavior and are covered by
|
||||||
|
// DeveloperBootstrapPluginFunctionalTest. The repository contract only fixes ownership: the
|
||||||
|
// root applies one Java convention and must not duplicate the bootstrap implementation in
|
||||||
|
// Groovy.
|
||||||
assertThat(build)
|
assertThat(build)
|
||||||
.contains("tasks.register('bootstrapCompile')")
|
.contains("id 'ca.developer-bootstrap'")
|
||||||
.contains("tasks.register('bootstrapDependencies'")
|
.doesNotContain("tasks.register('bootstrapCompile')")
|
||||||
.contains("tasks.register('bootstrapMigrateAndStart'")
|
.doesNotContain("tasks.register('bootstrapDockerPreflight')")
|
||||||
.contains("tasks.register('bootstrapSmoke'")
|
.doesNotContain("tasks.register('bootstrapDependencies')")
|
||||||
.contains("tasks.register('bootstrap')");
|
.doesNotContain("tasks.register('bootstrapMigrateAndStart')")
|
||||||
assertThat(build)
|
.doesNotContain("tasks.register('bootstrapSmoke')")
|
||||||
.contains("bootstrapDependencies.configure { dependsOn bootstrapCompile }")
|
.doesNotContain("tasks.register('bootstrap')");
|
||||||
.contains("bootstrapMigrateAndStart.configure { dependsOn bootstrapDependencies }")
|
|
||||||
.contains("bootstrapSmoke.configure { dependsOn bootstrapMigrateAndStart }");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
-50
@@ -352,56 +352,6 @@ class MessagingCapabilityRegistryContractTest {
|
|||||||
.containsEntry("minLength", 1);
|
.containsEntry("minLength", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void noUnimplementedQualificationIsRegisteredAsAGradleTask() throws Exception {
|
|
||||||
// The inverse of the assertion that used to be here.
|
|
||||||
//
|
|
||||||
// This test previously required the root build to register all nine R2 skeleton tasks and to
|
|
||||||
// route them through a guard that threw `FAIL_CLOSED`. It was pinning the existence of tasks
|
|
||||||
// that could not succeed: a card said "my evidence comes from verifyMessagingSecurityR2", the
|
|
||||||
// task existed, and running it always failed — so the registry looked wired to a build that
|
|
||||||
// could substantiate nothing. Registering a task for work with no producer does not make the
|
|
||||||
// absence safer; it makes the absence look like a gate.
|
|
||||||
//
|
|
||||||
// What is worth holding is that they are NOT registered, so nobody wires a release lane to one.
|
|
||||||
for (Path script : qualificationScripts()) {
|
|
||||||
String text = Files.readString(script);
|
|
||||||
for (String planned : PLANNED_VERIFICATION_TASKS) {
|
|
||||||
assertThat(text)
|
|
||||||
.as("%s must not register the unimplemented task %s", script.getFileName(), planned)
|
|
||||||
.doesNotContain("tasks.register('" + planned + "')")
|
|
||||||
.doesNotContain("tasks.register(\"" + planned + "\")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void messagingQualificationSchemaValidatesTheEvidenceItWrites() throws Exception {
|
|
||||||
// Qualification lives in build-qualification/src/main/groovy/ca.messaging-qualification.gradle
|
|
||||||
// now, not in the
|
|
||||||
// root build file. The property held here is the one that makes the manifest evidence rather
|
|
||||||
// than a file: each producer is finalized by a JSON Schema validation of the exact bytes it
|
|
||||||
// wrote, and the combined producer runs after the JSON-schema one.
|
|
||||||
String qualification =
|
|
||||||
Files.readString(
|
|
||||||
repositorySrcRoot()
|
|
||||||
.resolve("build-qualification/src/main/groovy/ca.messaging-qualification.gradle"));
|
|
||||||
|
|
||||||
assertThat(qualification)
|
|
||||||
.contains(
|
|
||||||
"MessagingEvidenceManifestSchemaValidator",
|
|
||||||
"finalizedBy validateMessagingJsonSchemaV1EvidenceManifestSchema",
|
|
||||||
"dependsOn validateMessagingJsonSchemaV1EvidenceManifestSchema",
|
|
||||||
"finalizedBy validateMessagingContractsEvidenceManifestSchema");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Path> qualificationScripts() throws Exception {
|
|
||||||
Path src = repositorySrcRoot();
|
|
||||||
return List.of(
|
|
||||||
src.resolve("build.gradle"),
|
|
||||||
src.resolve("build-qualification/src/main/groovy/ca.messaging-qualification.gradle"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path requiredConfig(String relativePath) {
|
private static Path requiredConfig(String relativePath) {
|
||||||
Path path = repositorySrcRoot().resolve("config/messaging").resolve(relativePath);
|
Path path = repositorySrcRoot().resolve("config/messaging").resolve(relativePath);
|
||||||
assertThat(path).as("required Messaging configuration %s", path).isRegularFile();
|
assertThat(path).as("required Messaging configuration %s", path).isRegularFile();
|
||||||
|
|||||||
@@ -12,19 +12,20 @@ dependencies {
|
|||||||
testImplementation libs.jqwik
|
testImplementation libs.jqwik
|
||||||
}
|
}
|
||||||
|
|
||||||
def messagingApplicationContractQualification = registerStrictQualificationTest(
|
def messagingApplicationContractQualification = extensions.getByName('strictQualification').register(
|
||||||
name: 'messagingApplicationContractQualificationTest',
|
'messagingApplicationContractQualificationTest',
|
||||||
sourceSet: sourceSets.test,
|
sourceSets.test,
|
||||||
requiredClasses: [
|
[
|
||||||
'dev.caskeleton.application.messaging.contract.IntegrationEventContractContributionTest',
|
'dev.caskeleton.application.messaging.contract.IntegrationEventContractContributionTest',
|
||||||
'dev.caskeleton.application.messaging.event.IntegrationEventDraftTest',
|
'dev.caskeleton.application.messaging.event.IntegrationEventDraftTest',
|
||||||
'dev.caskeleton.application.messaging.event.ValidatedIntegrationEventTest'
|
'dev.caskeleton.application.messaging.event.ValidatedIntegrationEventTest'
|
||||||
],
|
],
|
||||||
junitXmlOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence/application'),
|
'test-results/messaging-evidence/application'),
|
||||||
binaryResultsOutput: rootProject.layout.buildDirectory.dir(
|
rootProject.layout.buildDirectory.dir(
|
||||||
'test-results/messaging-evidence-binary/application'),
|
'test-results/messaging-evidence-binary/application'),
|
||||||
description: 'Runs exact Messaging application contract qualification tests.')
|
'Runs exact Messaging application contract qualification tests.'
|
||||||
|
)
|
||||||
messagingApplicationContractQualification.configure {
|
messagingApplicationContractQualification.configure {
|
||||||
dependsOn ':prepareMessagingContractEvidence'
|
dependsOn ':prepareMessagingContractEvidence'
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.caskeleton.application.messaging.event;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.outbox.OutboxPublishOutcome;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application-owned boundary for publishing one fully validated integration event.
|
||||||
|
*
|
||||||
|
* <p>The application exposes its canonical event model and outcome vocabulary only. Broker,
|
||||||
|
* transport, codec, and messaging-platform types remain on the outbound side of the boundary.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface IntegrationEventPublishPort {
|
||||||
|
|
||||||
|
CompletionStage<OutboxPublishOutcome> publish(ValidatedIntegrationEvent event);
|
||||||
|
}
|
||||||
+3
-3
@@ -3,8 +3,8 @@ package dev.caskeleton.application.operation;
|
|||||||
import dev.caskeleton.application.capability.Idempotency;
|
import dev.caskeleton.application.capability.Idempotency;
|
||||||
import dev.caskeleton.application.capability.RepositoryAccess;
|
import dev.caskeleton.application.capability.RepositoryAccess;
|
||||||
import dev.caskeleton.application.capability.UseCaseCapability;
|
import dev.caskeleton.application.capability.UseCaseCapability;
|
||||||
|
import dev.caskeleton.application.outbox.LegacyOutboxAppendPort;
|
||||||
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
import dev.caskeleton.application.outbox.NewOutboxEvent;
|
||||||
import dev.caskeleton.application.outbox.OutboxAppendPort;
|
|
||||||
import dev.caskeleton.application.security.RequiresPermission;
|
import dev.caskeleton.application.security.RequiresPermission;
|
||||||
import dev.caskeleton.application.transaction.TransactionMode;
|
import dev.caskeleton.application.transaction.TransactionMode;
|
||||||
import dev.caskeleton.application.transaction.TransactionPort;
|
import dev.caskeleton.application.transaction.TransactionPort;
|
||||||
@@ -53,7 +53,7 @@ import java.util.Objects;
|
|||||||
public class SubmitDurableOperationUseCase {
|
public class SubmitDurableOperationUseCase {
|
||||||
|
|
||||||
private final DurableOperationStorePort operations;
|
private final DurableOperationStorePort operations;
|
||||||
private final OutboxAppendPort outbox;
|
private final LegacyOutboxAppendPort outbox;
|
||||||
private final TransactionPort transactions;
|
private final TransactionPort transactions;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ public class SubmitDurableOperationUseCase {
|
|||||||
*/
|
*/
|
||||||
public SubmitDurableOperationUseCase(
|
public SubmitDurableOperationUseCase(
|
||||||
DurableOperationStorePort operations,
|
DurableOperationStorePort operations,
|
||||||
OutboxAppendPort outbox,
|
LegacyOutboxAppendPort outbox,
|
||||||
TransactionPort transactions,
|
TransactionPort transactions,
|
||||||
Clock clock) {
|
Clock clock) {
|
||||||
this.operations = Objects.requireNonNull(operations, "operations");
|
this.operations = Objects.requireNonNull(operations, "operations");
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package dev.caskeleton.application.outbox;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claimed legacy-authority row whose immutable payload is a complete canonical integration event.
|
||||||
|
*/
|
||||||
|
public record CanonicalClaimedOutboxEvent(
|
||||||
|
ValidatedIntegrationEvent event, OutboxEventStatus status, int attemptCount)
|
||||||
|
implements ClaimedOutboxEvent {
|
||||||
|
|
||||||
|
public CanonicalClaimedOutboxEvent {
|
||||||
|
Objects.requireNonNull(event, "event must not be null");
|
||||||
|
Objects.requireNonNull(status, "status must not be null");
|
||||||
|
if (attemptCount < 0) {
|
||||||
|
throw new IllegalArgumentException("attemptCount must be >= 0, was " + attemptCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String eventId() {
|
||||||
|
return event.eventId().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String eventType() {
|
||||||
|
return event.contractId().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String aggregateId() {
|
||||||
|
return event.aggregate().aggregateId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String correlationId() {
|
||||||
|
return event.correlationId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instant occurredAt() {
|
||||||
|
return event.occurredAt();
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package dev.caskeleton.application.outbox;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relay-facing claimed row identity shared by legacy and canonical outbox generations.
|
||||||
|
*
|
||||||
|
* <p>Only fields needed by the legacy relay state machine and diagnostics live here. Payload and
|
||||||
|
* canonical envelope data remain on their concrete subtype.
|
||||||
|
*/
|
||||||
|
public sealed interface ClaimedOutboxEvent permits OutboxEvent, CanonicalClaimedOutboxEvent {
|
||||||
|
|
||||||
|
String eventId();
|
||||||
|
|
||||||
|
String eventType();
|
||||||
|
|
||||||
|
String aggregateId();
|
||||||
|
|
||||||
|
String correlationId();
|
||||||
|
|
||||||
|
Instant occurredAt();
|
||||||
|
|
||||||
|
OutboxEventStatus status();
|
||||||
|
|
||||||
|
int attemptCount();
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package dev.caskeleton.application.outbox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility-only append boundary for the pre-canonical outbox payload model.
|
||||||
|
*
|
||||||
|
* <p>New integration-event code must use {@link OutboxAppendPort}. This port remains only while
|
||||||
|
* legacy/sample producers still persist {@link NewOutboxEvent} rows through the transport-only
|
||||||
|
* cutover window.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface LegacyOutboxAppendPort {
|
||||||
|
|
||||||
|
void append(NewOutboxEvent event);
|
||||||
|
}
|
||||||
+9
-12
@@ -1,19 +1,16 @@
|
|||||||
package dev.caskeleton.application.outbox;
|
package dev.caskeleton.application.outbox;
|
||||||
|
|
||||||
|
import dev.caskeleton.application.messaging.event.ValidatedIntegrationEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outbound port for appending a new event to the transactional outbox. <strong>Dual-write
|
* Canonical durable append boundary for a fully validated integration event.
|
||||||
* prohibition</strong>: must be called inside the same DB transaction as the business operation
|
*
|
||||||
* that generates the event ({@code TransactionPort.inWrite(...)}, opened by the caller);
|
* <p>The implementation participates in the caller's existing write transaction and persists the
|
||||||
* implementations must not open their own transaction. See README.
|
* exact validated envelope bytes. Raw legacy payload append is intentionally separated into {@link
|
||||||
|
* LegacyOutboxAppendPort}.
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
public interface OutboxAppendPort {
|
public interface OutboxAppendPort {
|
||||||
|
|
||||||
/**
|
void append(ValidatedIntegrationEvent event);
|
||||||
* Appends {@code event} to the outbox table, participating in the caller's existing write
|
|
||||||
* transaction. Calling outside {@code TransactionPort.inWrite(...)} is a contract violation
|
|
||||||
* (silent event loss).
|
|
||||||
*
|
|
||||||
* @param event the new event to persist; must not be {@code null}
|
|
||||||
*/
|
|
||||||
void append(NewOutboxEvent event);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ public record OutboxEvent(
|
|||||||
String correlationId,
|
String correlationId,
|
||||||
String idempotencyKey,
|
String idempotencyKey,
|
||||||
OutboxEventStatus status,
|
OutboxEventStatus status,
|
||||||
int attemptCount) {
|
int attemptCount)
|
||||||
|
implements ClaimedOutboxEvent {
|
||||||
|
|
||||||
public OutboxEvent {
|
public OutboxEvent {
|
||||||
Objects.requireNonNull(eventId, "eventId");
|
Objects.requireNonNull(eventId, "eventId");
|
||||||
|
|||||||
+2
-2
@@ -16,7 +16,7 @@ public interface OutboxMessagePublishPort {
|
|||||||
* @param event the claimed outbox event to publish; must not be {@code null}
|
* @param event the claimed outbox event to publish; must not be {@code null}
|
||||||
* @throws RuntimeException if the publish fails for any reason
|
* @throws RuntimeException if the publish fails for any reason
|
||||||
*/
|
*/
|
||||||
void publish(OutboxEvent event);
|
void publish(ClaimedOutboxEvent event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publishes and reports what was achieved.
|
* Publishes and reports what was achieved.
|
||||||
@@ -29,7 +29,7 @@ public interface OutboxMessagePublishPort {
|
|||||||
* @param event the claimed outbox event
|
* @param event the claimed outbox event
|
||||||
* @return what the attempt achieved
|
* @return what the attempt achieved
|
||||||
*/
|
*/
|
||||||
default OutboxPublishOutcome publishForOutcome(OutboxEvent event) {
|
default OutboxPublishOutcome publishForOutcome(ClaimedOutboxEvent event) {
|
||||||
publish(event);
|
publish(event);
|
||||||
// A method that either returns or throws can only report these two. Reporting CONFIRMED here
|
// A method that either returns or throws can only report these two. Reporting CONFIRMED here
|
||||||
// is honest for such an adapter — it is what "returned without throwing" has always meant —
|
// is honest for such an adapter — it is what "returned without throwing" has always meant —
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user