Skip to content

Commit 4c5103e

Browse files
Add SL4J Extensions & Kotest library (#71)
Adds two new libraries: * sl4jx-kotlin - A client library for connecting to Liberator over StreamLink with reactive bindings to Kotlin Flow. * datasourcex-kotest - A utility library for easy end to end testing with a Liberator container and real StreamLink and DataSource connectivity.
1 parent 1959997 commit 4c5103e

95 files changed

Lines changed: 2096 additions & 208 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@ jobs:
6868
if: always()
6969
with:
7070
report_paths: |
71-
reactive/api/build/test-results/test/TEST-*.xml
72-
reactive/core/build/test-results/test/TEST-*.xml
73-
reactive/java-flow/build/test-results/test/TEST-*.xml
74-
reactive/kotlin/build/test-results/test/TEST-*.xml
75-
reactive/reactivestreams/build/test-results/test/TEST-*.xml
71+
reactive/datasource/api/build/test-results/test/TEST-*.xml
72+
reactive/datasource/core/build/test-results/test/TEST-*.xml
73+
reactive/datasource/java-flow/build/test-results/test/TEST-*.xml
74+
reactive/datasource/kotlin/build/test-results/test/TEST-*.xml
75+
reactive/datasource/reactivestreams/build/test-results/test/TEST-*.xml
76+
reactive/streamlink/kotlin/build/test-results/test/TEST-*.xml
7677
spring/build/test-results/test/TEST-*.xml
7778
util/build/test-results/test/TEST-*.xml
7879

.idea/compiler.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

AGENTS.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,36 +35,44 @@ The repo is typically checked out on Windows. PowerShell uses `.\gradlew.bat`; B
3535
### Module layout and dependency direction
3636

3737
```
38-
reactive/api ← config DSL types (ActiveConfig, ChannelConfig, ContainerEvent, …)
39-
reactive/core ← Binder + IFlowAdapter — does the real DataSource SDK plumbing
40-
Everything funnels through here as kotlinx.coroutines Flow
41-
reactive/kotlin ← Bind DSL for Flow } each contributes a Bind facade
42-
reactive/java-flow ← Bind DSL for java.util.concurrent.Flow.Publisher
43-
reactive/reactivestreams ← Bind DSL for org.reactivestreams.Publisher
44-
45-
spring ← spring-boot-starter-datasource (depends on reactive/kotlin)
38+
reactive/datasource/api ← config DSL types (ActiveConfig, ChannelConfig, ContainerEvent, …)
39+
reactive/datasource/core ← Binder + IFlowAdapter — does the real DataSource SDK plumbing
40+
Everything funnels through here as kotlinx.coroutines Flow
41+
reactive/datasource/kotlin ← Bind DSL for Flow } each contributes a Bind facade
42+
reactive/datasource/java-flow ← Bind DSL for java.util.concurrent.Flow.Publisher
43+
reactive/datasource/reactivestreams ← Bind DSL for org.reactivestreams.Publisher
44+
45+
reactive/streamlink/kotlin ← sl4jx-kotlin — Flow-based StreamLink client wrapper
46+
(StreamLink + KeyMaster). Depends on datasourcex-util for Subject.
47+
48+
kotest ← datasourcex-kotest — Liberator-in-a-container Kotest harness
49+
(LiberatorContainerExtension) for StreamLink client tests. Client-side
50+
only: api-depends on sl4jx-kotlin + testcontainers, no datasourcex
51+
runtime dependency. Its docker-backed integrationTest is not in `check`.
52+
spring ← spring-boot-starter-datasource (depends on reactive/datasource/kotlin)
4653
util ← datasourcex-util — FlowMap, custom Flow operators, AntPatternNamespace,
47-
Fory serialization helpers. No DataSource SDK dependency.
54+
Subject, Fory serialization helpers. `api`-depends on the DataSource
55+
server SDK (`api(libs.datasource)`).
4856
examples/ ← spring-java, spring-kotlin, spring-kotlin-chat — manual smoke tests
4957
api-docs/ ← aggregated Dokka site (published to GitHub Pages from main)
5058
```
5159

52-
The published Maven coordinates use the module's renamed name (`datasourcex-kotlin`, `spring-boot-starter-datasource`, etc.), not the Gradle path — set up in `settings.gradle.kts`.
60+
The five datasource reactive modules sit under `reactive/datasource/` on disk so the StreamLink SDK family can sit beside them under `reactive/streamlink/`. A `projectDir` override in `settings.gradle.kts` relocates only the directory; the `include("reactive:kotlin")` coordinate, the resulting project path (`:reactive:datasourcex-kotlin`, `:reactive:datasourcex-reactive-core`, `:reactive:sl4jx-kotlin`, etc. — set by the `.name` overrides), and the published Maven coordinates are all unchanged. Published coordinates use the renamed name (`datasourcex-kotlin`, `sl4jx-kotlin`, `spring-boot-starter-datasource`, etc.), not the include path.
5361

5462
### Code generation (read this before editing reactive modules)
5563

56-
The three `reactive/{kotlin,java-flow,reactivestreams}` modules are NOT three parallel hand-written implementations. Each runs a `generateApi` Gradle task (defined in `buildSrc/src/main/kotlin/GenerateApi.kt`, applied via `common-reactive-library`) that emits Kotlin source for the `Bind`, `BindActive`, `BindActiveContainer`, `BindChannel`, `BindBroadcast` DSL classes plus their `Mapping`/`Json`/`Record` flavours.
64+
The three `reactive/datasource/{kotlin,java-flow,reactivestreams}` modules are NOT three parallel hand-written implementations. Each runs a `generateApi` Gradle task (defined in `buildSrc/src/main/kotlin/GenerateApi.kt`, applied via `common-reactive-library`) that emits Kotlin source for the `Bind`, `BindActive`, `BindActiveContainer`, `BindChannel`, `BindBroadcast` DSL classes plus their `Mapping`/`Json`/`Record` flavours.
5765

5866
Each module's `build.gradle.kts` sets the variant: `tasks.generateApi { publisherType = "kotlin" | "java" | "reactivestreams" }`.
5967

6068
Implications:
61-
- If you grep for `BindActiveJson` and find nothing, that's because it's generated. Run `./gradlew :reactive:datasourcex-kotlin:generateApi` and look in `reactive/kotlin/build/generated/sources/generateApi/main/kotlin/`.
69+
- If you grep for `BindActiveJson` and find nothing, that's because it's generated. Run `./gradlew :reactive:datasourcex-kotlin:generateApi` and look in `reactive/datasource/kotlin/build/generated/sources/generateApi/main/kotlin/`.
6270
- The shape of the generated DSL lives in `buildSrc/src/main/kotlin/{Active,ActiveContainer,Channel,Broadcast,Functions}.kt`. Edit those to change the generated API, not the build output.
63-
- All three published variants share the runtime path through `reactive/core/Binder`. `IFlowAdapter` is the small bridge that converts each library's publisher type to/from `Flow<Any>`.
71+
- All three published variants share the runtime path through `reactive/datasource/core/Binder`. `IFlowAdapter` is the small bridge that converts each library's publisher type to/from `Flow<Any>`.
6472

6573
### Spring starter
6674

67-
`spring` depends on `reactive/kotlin` and exposes endpoints via custom annotations on top of Spring Messaging:
75+
`spring` depends on `reactive/datasource/kotlin` and exposes endpoints via custom annotations on top of Spring Messaging:
6876
- `@DataService` marks a controller. `@DataMessageMapping("/subject/{var}")` maps a subject pattern to a method (analogous to `@MessageMapping`).
6977
- `@IngressDestinationVariable` extracts path variables.
7078
- Methods return `Flow<T>` / `Flux<T>` / `Publisher<T>` for active subjects, or accept/return `Flow` pairs for channels.

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ Then refer to the documentation:
4242
* [datasourcex-kotlin](https://caplin.github.io/DataSource-Extensions/reactive/datasourcex-kotlin)
4343
* [datasourcex-java-flow](https://caplin.github.io/DataSource-Extensions/reactive/datasourcex-java-flow)
4444

45+
## StreamLink
46+
47+
`sl4jx-kotlin` is a [Kotlin Flow](https://kotlinlang.org/docs/flow.html)-based client wrapper over
48+
Caplin's StreamLink and KeyMaster SDKs, for consuming data from a Liberator.
49+
50+
### Usage
51+
52+
Add the following dependency to your project:
53+
54+
```kotlin
55+
dependencies {
56+
implementation("com.caplin.integration.datasourcex:sl4jx-kotlin:<version>")
57+
}
58+
```
59+
60+
Then refer to
61+
the [documentation](https://caplin.github.io/DataSource-Extensions/reactive/sl4jx-kotlin).
62+
4563
## Spring
4664

4765
This module provides a starter for integrating Caplin DataSource with your
@@ -61,4 +79,22 @@ dependencies {
6179

6280
Then refer to
6381
the [documentation](https://caplin.github.io/DataSource-Extensions/spring-boot-starter-datasource),
64-
[hands-on tutorial](./spring/docs/GUIDE.md) and [examples](./examples).
82+
[hands-on tutorial](./spring/docs/GUIDE.md) and [examples](./examples).
83+
84+
## Testing
85+
86+
`datasourcex-kotest` is a [Kotest](https://kotest.io/) test-support harness that runs a Liberator in
87+
a [Testcontainers](https://testcontainers.com/) container for StreamLink client tests.
88+
89+
### Usage
90+
91+
Add the following dependency to your project:
92+
93+
```kotlin
94+
dependencies {
95+
testImplementation("com.caplin.integration.datasourcex:datasourcex-kotest:<version>")
96+
}
97+
```
98+
99+
Then refer to
100+
the [documentation](https://caplin.github.io/DataSource-Extensions/datasourcex-kotest).

api-docs/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ dependencies {
77
dokka(project(":reactive:datasourcex-java-flow"))
88
dokka(project(":reactive:datasourcex-kotlin"))
99
dokka(project(":reactive:datasourcex-reactivestreams"))
10+
dokka(project(":reactive:sl4jx-kotlin"))
11+
dokka(project(":datasourcex-kotest"))
1012
dokka(project(":spring-boot-starter-datasource"))
1113
dokka(project(":datasourcex-util"))
1214
}

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies {
1717
kover(project(":reactive:datasourcex-java-flow"))
1818
kover(project(":reactive:datasourcex-kotlin"))
1919
kover(project(":reactive:datasourcex-reactivestreams"))
20+
kover(project(":reactive:sl4jx-kotlin"))
2021
kover(project(":spring-boot-starter-datasource"))
2122
kover(project(":datasourcex-util"))
2223
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# A subject's string form is named `path`; a `Subject` object is named `subject`
2+
3+
Across both product lines (`datasourcex` and `sl4jx`), a variable, parameter, or property is named
4+
by the type it holds: `path` when it is the `String` rendering of a subject, `subject` when it is a
5+
`Subject`. The convention applies to public and internal code alike and to the `buildSrc` code
6+
generation, which emits `path: String` DSL parameters (`pathParameter`). Method names describe an
7+
operation rather than a variable — `getSubject(subject: Subject)`, `extractPathParameters(path: String)`
8+
and are governed by what they do, not by this rule.
9+
10+
## Consequences
11+
12+
New and renamed members follow the convention directly. A rename that would break binary
13+
compatibility — a public `subject: String` property, whose getter would change from `getSubject()` to
14+
`getPath()` — is introduced by **deprecate-and-add**: the `path` member is added and the old name is
15+
kept as a `@Deprecated` alias delegating to it. This keeps the convention shippable within a minor
16+
(3.x) release. The deprecated aliases are removed in a later major release.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Public value types are plain classes, not `data class`es
2+
3+
Public API value types across both product lines (`datasourcex`, `sl4jx`) are declared as ordinary
4+
classes with hand-written `equals`/`hashCode`/`toString` where value semantics are wanted, never
5+
`data class`. A `data class` bakes `copy(...)` and `componentN()` into the binary-compatible surface,
6+
so adding or reordering a constructor property later is a breaking change — the `copy`/`componentN`
7+
signatures shift, and `@JvmOverloads` does not extend to the generated `copy`. Hand-writing the value
8+
methods keeps the constructor free to evolve within a minor release. See
9+
[Public API challenges in Kotlin](https://jakewharton.com/public-api-challenges-in-kotlin/).
10+
11+
## Consequences
12+
13+
- New public value types follow this directly — e.g. the sl4jx `UpdateEvent`/`StatusEvent`/
14+
`ErrorEvent` and `ContainerChange.*`, matching the existing `reactive.api` events (`ContainerEvent`,
15+
`BroadcastEvent`), which expose only the `componentN` they choose to hand-write.
16+
- Losing `data` does not mean losing `copy`: hand-write it when it is wanted. Because you own the
17+
signature, a later parameter is added as an overload rather than silently shifting the generated
18+
one. `AntPatternNamespace` is the in-repo example — a plain class with a hand-written `copy`.
19+
- A type may support destructuring by hand-writing `componentN`; it need not expose `copy`.
20+
- A type that holds non-value fields (streams, containers, connections) is a plain class with no
21+
`equals`/`hashCode` at all, rather than a `data class` whose generated equality would be meaningless.
22+
- Removing a shipped `data class`'s generated `copy`/`componentN` is itself binary-breaking, so a
23+
released type is converted by hand-writing whichever of those members callers already use; only a
24+
genuinely unused member is dropped outright.

examples/spring-kotlin/build.gradle.kts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,26 @@ dependencies {
1919
implementation(project(":spring-boot-starter-datasource"))
2020
implementation("org.springframework.boot:spring-boot-starter")
2121
}
22+
23+
// Docker-backed end-to-end test: starts a Liberator Testcontainer, runs this adapter against it and
24+
// subscribes as a StreamLink client. Not wired into `check`; run on demand with
25+
// `./gradlew :examples:spring-kotlin:integrationTest`.
26+
testing {
27+
suites {
28+
register<JvmTestSuite>("integrationTest") {
29+
dependencies {
30+
implementation(project())
31+
implementation(project(":datasourcex-kotest"))
32+
implementation(libs.kotest.runner)
33+
implementation(libs.kotest.assertions)
34+
implementation(libs.turbine)
35+
}
36+
targets.configureEach { testTask.configure { useJUnitPlatform() } }
37+
}
38+
}
39+
}
40+
41+
configurations {
42+
named("integrationTestImplementation") { extendsFrom(configurations.implementation.get()) }
43+
named("integrationTestRuntimeOnly") { extendsFrom(configurations.runtimeOnly.get()) }
44+
}

0 commit comments

Comments
 (0)