Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/interoperability.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Node.js interoperability

on:
pull_request:
paths:
- ".github/workflows/interoperability.yml"
- "Makefile"
- "aep-agent/**"
- "aep-core/**"
- "aep-httpserver/**"
- "aep-json-jackson2/**"
- "aep-platform/**"
- "aep-service/**"
- "examples/**"
- "pom.xml"
- "mvnw"
- ".mvn/**"
- "scripts/run-node-interoperability.sh"
- "scripts/verify-node-interoperability.mjs"
workflow_dispatch:

permissions:
contents: read

jobs:
interoperability:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
path: aep-java

- uses: actions/checkout@v7
with:
repository: aep-foundation/aep-node
path: aep-node

- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"

- uses: actions/setup-node@v6
with:
node-version: 24

- run: corepack enable

- name: Install Node.js dependencies
working-directory: aep-node
run: pnpm install --frozen-lockfile

- name: Run bidirectional interoperability
working-directory: aep-java
run: make interoperability
env:
AEP_INTEROP_OUTPUT_DIR: .interop/reports
AEP_NODE_DIR: ../aep-node

- name: Upload interoperability report
uses: actions/upload-artifact@v7
with:
name: aep-java-node-interoperability
path: aep-java/.interop/reports/aep-java-node-interoperability.json
if-no-files-found: error
retention-days: 7
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.DS_Store
.conformance/
.interop/
.idea/
.project
.classpath
Expand Down
9 changes: 9 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ Format Java sources with:
./mvnw spotless:apply
```

Run the bidirectional Java and Node.js interoperability flow with adjacent repository checkouts:

```sh
AEP_NODE_DIR=../aep-node make interoperability
```

The flow runs the Java Agent against the Node.js Service and Platform, then the Node.js Agent
against the Java Service and Platform. Its report is written under `.interop/reports/`.

## Module boundaries

`aep-core` owns transport-independent wire contracts, validation, identity and assertion
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: clean format format-check verify
.PHONY: clean format format-check interoperability verify

clean:
./mvnw clean
Expand All @@ -9,5 +9,8 @@ format:
format-check:
./mvnw spotless:check

interoperability:
./scripts/run-node-interoperability.sh

verify:
./mvnw verify
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ private void requireUsableIdentity(PlatformAgentIdentity identity, PlatformDisco
}
URI expected;
try {
expected = DidWeb.documentUri(identity.agentDid(), allowInsecureLoopback);
expected = DidWeb.documentUri(identity.agentDid(), false);
} catch (IllegalArgumentException exception) {
throw new AepAgentException(
PLATFORM_IDENTITY_INVALID, "AEP Platform returned an invalid hosted identity", exception);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,30 @@ void sharesAnInFlightDiscoveryRequestAcrossConcurrentOperations() {
assertEquals(2, listRequests.get());
}

@Test
void acceptsNormativeHttpsDidDocumentsFromALoopbackPlatform() {
String loopbackDid = "did:web:127.0.0.1%3A4310:agents:managed";
String loopbackDiscovery = discovery()
.replace(
"https://p.example/a/{agent_did_id}/did.json",
"https://127.0.0.1:4310/agents/{agent_did_id}/did.json");
String loopbackIdentity = identity()
.replace(AGENT_DID, loopbackDid)
.replace("https://p.example/a/4Yf7p2xQd9/did.json", "https://127.0.0.1:4310/agents/managed/did.json");
QueueTransport transport = new QueueTransport(
aepResponse(200, loopbackDiscovery),
aepResponse(200, "{\"count\":\"1\",\"data\":[" + loopbackIdentity + "],\"total\":\"1\"}"));
PlatformIdentityProvider provider = PlatformIdentityProvider.builder(URI.create("http://127.0.0.1:4310"))
.transport(transport)
.allowInsecureLoopback(true)
.build();

AgentIdentity selected =
provider.getOrCreate(SERVICE, SERVICE_DID).toCompletableFuture().join();

assertEquals(loopbackDid, selected.did());
}

@Test
void pendingStagesRejectAReusedIdempotencyKeyAndRetryAfterHeader() {
QueueTransport duplicateTransport = new QueueTransport(
Expand Down
Loading