Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1d87300
Ignore local dev artifacts
AlphaSudo Mar 23, 2026
a17ba47
feat(v3): add SPI foundation module and phase execution plans
AlphaSudo Mar 24, 2026
ae52b6f
feat: implement v3 Phase 2 - Plugin Manager Core
AlphaSudo Mar 24, 2026
48509a7
feat: extract postgres and kafka into plugin modules
AlphaSudo Mar 24, 2026
0c494f8
feat: add mysql source plugin and v3 config migration
AlphaSudo Mar 24, 2026
c6b4210
feat: add source-aware api ui and query caching
AlphaSudo Mar 24, 2026
1430a08
feat: add plugin contract test harness and v3 release docs
AlphaSudo Mar 24, 2026
0f0d645
test: verify external plugins cache and metadata benchmarks
AlphaSudo Mar 24, 2026
8f69be7
chore: add v3 release smoke script
AlphaSudo Mar 24, 2026
a128d2d
test: add v4 readiness e2e coverage for source failover and lazy payload
AlphaSudo Mar 24, 2026
34a50f5
refactor(ui): EventLens shell, workspace dock, and header layout
AlphaSudo Mar 24, 2026
a618353
feat: make live stream and anomalies source-aware
AlphaSudo Mar 24, 2026
8577a55
Fix Dockerfile to map actual modules for build
AlphaSudo Mar 24, 2026
493bd5a
fix: null-guard datasourceHealth() and DatasourceListingModel to prev…
AlphaSudo Mar 24, 2026
7e1217a
fix: create fresh plugin instance per datasource to avoid shared state
AlphaSudo Mar 24, 2026
339621b
fix: resolve v3.0.0/master merge conflicts in build.gradle.kts files
AlphaSudo Mar 25, 2026
f91b3a2
Merge master into v3.0.0 to resolve conflicts
AlphaSudo Mar 25, 2026
10d4f42
fix: update ci project paths and fix jdbc instant mappings
AlphaSudo Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
run: chmod +x gradlew

- name: Compile and build (skip integration tests)
run: ./gradlew build -x :eventlens-kafka:test -x :eventlens-pg:test --no-daemon
run: ./gradlew build -x :eventlens-stream-kafka:test -x :eventlens-source-postgres:test --no-daemon

integration-tests:
runs-on: ubuntu-latest
Expand All @@ -47,7 +47,7 @@ jobs:
run: chmod +x gradlew

- name: Run Kafka integration tests
run: ./gradlew :eventlens-kafka:test --no-daemon
run: ./gradlew :eventlens-stream-kafka:test --no-daemon

- name: Run PostgreSQL integration tests
run: ./gradlew :eventlens-pg:test --no-daemon
run: ./gradlew :eventlens-source-postgres:test --no-daemon
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ eventlens-ui/.env.development.local
.idea/
*.iml
.vscode/
.cursor/
.DS_Store
Thumbs.db
.cursor/
Expand All @@ -35,6 +36,9 @@ logs/
*.log
*.txt

# Local scratchpad plans (not part of the repo)
plans/

# Config with real credentials — commit *.yaml.example instead
eventlens.yaml

Expand Down
22 changes: 20 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,24 @@ This compiles all modules, runs the React Vite build, and produces the fat JAR a
## Running Tests

```bash
# Unit tests only
# Unit tests + contract tests
./gradlew test

# Integration tests (requires Docker/Podman for Testcontainers)
# Full verification gate
./gradlew check

# Integration and contract tests (requires Docker/Podman for Testcontainers)
./gradlew test --info
```

Built-in plugins should keep passing the shared contract harness in `eventlens-plugin-test`.

For a compact v3 release smoke pass, run:

```bash
pwsh ./scripts/v3-release-smoke.ps1
```

## Running Locally

```bash
Expand Down Expand Up @@ -58,3 +69,10 @@ Please open a GitHub Issue with:
- Java version (`java -version`)
- Steps to reproduce
- Expected vs. actual behaviour

## Plugin Development

- Start with [docs/plugin-authoring.md](C:/Java%20Developer/EventDebug/docs/plugin-authoring.md).
- Reuse the shared contract harness from ventlens-plugin-test for new source or stream plugins.
- Register plugin entry points with META-INF/services/... so discovery works from classpath and /plugins.

7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ COPY settings.gradle.kts build.gradle.kts gradlew gradlew.bat ./
COPY gradle ./gradle

# Project sources
COPY eventlens-spi ./eventlens-spi
COPY eventlens-core ./eventlens-core
COPY eventlens-pg ./eventlens-pg
COPY eventlens-kafka ./eventlens-kafka
COPY eventlens-source-postgres ./eventlens-source-postgres
COPY eventlens-source-mysql ./eventlens-source-mysql
COPY eventlens-stream-kafka ./eventlens-stream-kafka
COPY eventlens-plugin-test ./eventlens-plugin-test
COPY eventlens-api ./eventlens-api
COPY eventlens-cli ./eventlens-cli
COPY eventlens-ui ./eventlens-ui
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,19 @@ In containerized environments, direct logs to stdout/stderr and collect them via
| [CHANGELOG.md](CHANGELOG.md) | Version history and release notes |
| [CONTRIBUTING.md](CONTRIBUTING.md) | Build, test, and PR guidelines |
| [eventlens.yaml.example](eventlens.yaml.example) | Annotated config template |

## v3 Plugin Docs

- [Plugin authoring guide](C:/Java%20Developer/EventDebug/docs/plugin-authoring.md)
- [v3 GA checklist](C:/Java%20Developer/EventDebug/docs/v3-ga-checklist.md)
- [SPI README](C:/Java%20Developer/EventDebug/eventlens-spi/README.md)

## v3 Release Smoke

Run the compact cross-phase smoke gate with:

`ash
pwsh ./scripts/v3-release-smoke.ps1
`

This verifies key v3 evidence files are present and runs est + check as the release gate.
223 changes: 223 additions & 0 deletions docs/plugin-authoring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
# Plugin Authoring Guide

EventLens v3 exposes a small SPI so external plugins can add new event sources, stream adapters, and reducers without modifying the core app.

## Plugin Types

### Event source plugins
Use [`EventSourcePlugin`](C:/Java%20Developer/EventDebug/eventlens-spi/src/main/java/io/eventlens/spi/EventSourcePlugin.java) when your plugin reads events from a database or event store.

Responsibilities:
- Initialize once from config.
- Stay thread-safe after initialization.
- Never mutate the underlying store.
- Return health independently from other plugins.

### Stream adapter plugins
Use [`StreamAdapterPlugin`](C:/Java%20Developer/EventDebug/eventlens-spi/src/main/java/io/eventlens/spi/StreamAdapterPlugin.java) when your plugin subscribes to live events from Kafka or another broker.

Responsibilities:
- Start non-blocking background consumption in `subscribe(...)`.
- Stop cleanly in `unsubscribe()` and `close()`.
- Keep health checks lightweight.

### Reducer plugins
Use [`ReducerPlugin`](C:/Java%20Developer/EventDebug/eventlens-spi/src/main/java/io/eventlens/spi/ReducerPlugin.java) when you want custom replay/state reconstruction behavior for a domain aggregate type.

## Module Rules

- Depend on `eventlens-spi`, not runtime modules like `eventlens-core` or existing source plugins.
- Register plugins with `META-INF/services/<interface-name>` so `ServiceLoader` can discover them.
- Prefer parent-first dependency loading assumptions: shade only when truly necessary.
- Keep constructors no-arg so built-in discovery works.
- Treat `spiVersion()` compatibility as part of your release contract.

## Minimal Event Source Example

```java
package com.acme.eventlens;

import io.eventlens.spi.*;
import com.fasterxml.jackson.databind.node.NullNode;

import java.util.Map;

public final class AcmeEventSourcePlugin implements EventSourcePlugin {

private AcmeReader reader;

@Override
public String typeId() {
return "acme-db";
}

@Override
public String displayName() {
return "Acme Event Store";
}

@Override
public void initialize(String instanceId, Map<String, Object> config) {
reader = new AcmeReader(config);
}

@Override
public EventQueryResult query(EventQuery query) {
return reader.query(query);
}

@Override
public HealthStatus healthCheck() {
return reader != null ? HealthStatus.up() : HealthStatus.down("not initialized");
}

@Override
public com.fasterxml.jackson.databind.JsonNode configSchema() {
return NullNode.getInstance();
}

@Override
public void close() {
if (reader != null) {
reader.close();
}
}
}
```

Service registration file:

```text
META-INF/services/io.eventlens.spi.EventSourcePlugin
```

Contents:

```text
com.acme.eventlens.AcmeEventSourcePlugin
```

## Config Shape

Datasource and stream instances are configured from `eventlens.yaml`.

Example datasource entry:

```yaml
datasources:
- id: reporting-mysql
type: mysql
url: jdbc:mysql://localhost:3306/eventlens_reporting
username: root
password: secret
table: event_store
```

EventLens converts each configured block into the `Map<String, Object>` passed to `initialize(...)`.
Built-in JDBC sources currently receive keys such as:
- `jdbcUrl`
- `username`
- `password`
- `tableName`
- `columnOverrides`
- `pool`
- `queryTimeoutSeconds`

If your plugin needs a schema, return it from `configSchema()` so future tooling and validation can surface it.

## Classloading and Dependency Guidance

- Compile against the SPI only.
- Avoid depending on app modules or implementation packages from built-in plugins.
- Keep transitive dependencies small and predictable.
- Prefer widely used drivers or clients that your plugin alone owns.
- Assume plugins may be loaded from `/plugins` with parent-first classloading.
- Do not rely on mutable global state or static caches shared across plugin instances.

## Versioning and Compatibility

Compatibility is checked through [`SpiVersions`](C:/Java%20Developer/EventDebug/eventlens-spi/src/main/java/io/eventlens/spi/SpiVersions.java).

Guidelines:
- Adding a `default` method is backward-compatible.
- Adding a required interface method is a breaking change.
- Changing a method signature is a breaking change.
- Keep `typeId()` stable once released.

## Using the Plugin Test Kit

The shared contract harness lives in [`eventlens-plugin-test`](C:/Java%20Developer/EventDebug/eventlens-plugin-test).

### Event source contract example

```java
@Testcontainers(disabledWithoutDocker = true)
class MyPluginContractTest extends EventSourcePluginTestKit {

@Override
protected EventSourcePlugin createPlugin() {
var plugin = new AcmeEventSourcePlugin();
plugin.initialize("contract", Map.of(
"jdbcUrl", container.getJdbcUrl(),
"username", container.getUsername(),
"password", container.getPassword()
));
return plugin;
}

@Override
protected void seedCanonicalEvents() throws Exception {
// Insert CanonicalEventSet.defaultEvents() into your backing store.
}

@Override
protected void cleanupStore() throws Exception {
// Truncate backing tables.
}
}
```

The contract kit verifies:
- health checks
- timeline ordering
- cursor pagination
- metadata-only payload behavior
- search results
- empty-state handling

### Stream adapter contract example

```java
@Testcontainers(disabledWithoutDocker = true)
class MyStreamContractTest extends StreamAdapterPluginTestKit {

@Override
protected StreamAdapterPlugin createPlugin() {
var plugin = new MyStreamPlugin();
plugin.initialize("contract", Map.of("topic", "events"));
return plugin;
}

@Override
protected void emitCanonicalEvents() throws Exception {
// Publish at least two events for aggregate ACC-001.
}
}
```

## Packaging and Publishing

- Build your plugin as a normal JAR.
- Include the ServiceLoader registration file.
- Target Java 21 to match the current EventLens runtime.
- Publish your plugin artifact independently; EventLens loads it from the classpath or `/plugins` directory.
- The SPI module is configured for Maven publishing in [`eventlens-spi/build.gradle.kts`](C:/Java%20Developer/EventDebug/eventlens-spi/build.gradle.kts).

## Practical Checklist

- Implement the correct SPI interface.
- Keep initialization deterministic and fail fast on invalid config.
- Return `HealthStatus.down(...)` with an actionable message.
- Add a contract test using `eventlens-plugin-test`.
- Register the plugin in `META-INF/services`.
- Document required config keys and expected dependencies.
46 changes: 46 additions & 0 deletions docs/v3-ga-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# v3 GA Checklist

This checklist translates the v3 release criteria from [`versions/v3.md`](C:/Java%20Developer/EventDebug/versions/v3.md) into repo-local release evidence.

## MUST

- [x] `eventlens-spi` exists as a dedicated module with stable SPI types.
- [x] PostgreSQL plugin contract coverage added through [`PostgresEventSourcePluginContractTest.java`](C:/Java%20Developer/EventDebug/eventlens-source-postgres/src/test/java/io/eventlens/pg/PostgresEventSourcePluginContractTest.java).
- [x] MySQL plugin contract coverage added through [`MySqlEventSourcePluginContractTest.java`](C:/Java%20Developer/EventDebug/eventlens-source-mysql/src/test/java/io/eventlens/mysql/MySqlEventSourcePluginContractTest.java).
- [x] Kafka stream contract coverage added through [`KafkaStreamAdapterPluginContractTest.java`](C:/Java%20Developer/EventDebug/eventlens-stream-kafka/src/test/java/io/eventlens/kafka/KafkaStreamAdapterPluginContractTest.java).
- [x] v2 single-source config remains supported through migrator and validator tests.
- [x] v3 multi-source config is implemented and exercised through config tests and runtime wiring.
- [x] Existing API routes remain in place while source-aware v3 routes were added.
- [x] Optional `source` support is implemented for search and timeline flows.
- [x] UI datasource selector and plugin health page are implemented.
- [x] Plugin authoring guide is published in [`docs/plugin-authoring.md`](C:/Java%20Developer/EventDebug/docs/plugin-authoring.md).
- [~] SPI publishing is prepared via Maven publication config in [`eventlens-spi/build.gradle.kts`](C:/Java%20Developer/EventDebug/eventlens-spi/build.gradle.kts).

## SHOULD

- [x] External plugin JAR loading is verified with a dummy plugin artifact in [`PluginDiscoveryExternalJarTest.java`](C:/Java%20Developer/EventDebug/eventlens-core/src/test/java/io/eventlens/core/plugin/PluginDiscoveryExternalJarTest.java).
- [x] Cache hit ratio is measured under repeated-query synthetic load in [`QueryResultCacheBenchmarkTest.java`](C:/Java%20Developer/EventDebug/eventlens-api/src/test/java/io/eventlens/api/cache/QueryResultCacheBenchmarkTest.java).
- [x] Metadata-only mode is benchmarked for response-size reduction in [`TimelineMetadataPayloadBenchmarkTest.java`](C:/Java%20Developer/EventDebug/eventlens-api/src/test/java/io/eventlens/api/routes/TimelineMetadataPayloadBenchmarkTest.java).

## MUST NOT

- [x] No plugin sandboxing code was introduced.
- [x] No Vault or AWS Secrets Manager integration was introduced.
- [x] No gRPC dependency was added.
- [x] No MongoDB dependency was added.
- [x] No RabbitMQ, NATS, or Pulsar dependency was added.
- [x] No scripting runtime was added.
- [x] No metadata database or Flyway layer was added.
- [x] No event store write path was introduced.

## Verification Run

Recommended gate before a release candidate:

```bash
./gradlew.bat test
./gradlew.bat check
```

These commands validate the shared plugin contracts, built-in plugin modules, API tests, and UI build.

9 changes: 7 additions & 2 deletions eventlens-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
dependencies {
implementation(project(":eventlens-core"))
implementation(project(":eventlens-pg"))
implementation(project(":eventlens-kafka"))
implementation(project(":eventlens-spi"))
implementation("io.javalin:javalin:7.1.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.21.2")
implementation("ch.qos.logback:logback-classic:1.5.32")
implementation("io.micrometer:micrometer-core:1.16.4")
implementation("io.micrometer:micrometer-registry-prometheus:1.16.4")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.21.2")
testImplementation(project(":eventlens-source-postgres"))
testImplementation(project(":eventlens-source-mysql"))
testImplementation("org.testcontainers:junit-jupiter:1.20.1")
testImplementation("org.testcontainers:postgresql:1.20.1")
testImplementation("org.testcontainers:mysql:1.20.1")
}

Loading
Loading