-
Notifications
You must be signed in to change notification settings - Fork 12
Description
Description
Applications using @cap-js/event-broker to consume external CloudEvents can now document these dependencies in their ORD metadata. The ORD plugin generates integrationDependencies that describe which external events an application consumes.
Use Cases
- A CAP application subscribes to S/4HANA Business Partner change events via SAP Cloud Application Event Hub
- The application's ORD document declares this dependency for discoverability and governance
- External tools (like SAP Business Accelerator Hub) can display which events an application depends on
Benefits
- Complete ORD metadata for Event Broker (Event Hub) consumers
- Improved discoverability and transparency of event-driven integrations
- Alignment with the Java CAP plugin (
cds-feature-event-hub)
Suggested Solution
Architecture: Extension Registry Pattern
Instead of adding Event Broker detection logic directly to the ORD plugin (as originally proposed), we implemented an Extension Registry that allows external plugins to contribute Integration Dependency data:
┌─────────────────────────────────────────────────────────────────┐
│ Application │
├─────────────────────────────────────────────────────────────────┤
│ srv/server.js │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ messaging.subscribe("sap.s4...SalesOrder.Changed.v1", { ││
│ │ eventResourceOrdId: "sap.s4:eventResource:CE_SALES..." ││
│ │ }, handler) ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ @cap-js/event-broker │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 1. Collects eventResourceOrdId from subscribe() calls ││
│ │ 2. Groups event types by event resource ││
│ │ 3. Registers provider with ORD plugin ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ @cap-js/ord │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Extension Registry ││
│ │ - registerIntegrationDependencyProvider(fn) ││
│ │ - getProvidedIntegrationDependencies() ││
│ └─────────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ integrationDependency.js ││
│ │ - createEventIntegrationDependency() ││
│ │ - Builds ORD subset structure from provider data ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ORD Document │
│ { │
│ "integrationDependencies": [{ │
│ "ordId": "customer.app:integrationDependency:consumed...", │
│ "aspects": [{ │
│ "eventResources": [{ │
│ "ordId": "sap.s4:eventResource:CE_SALESORDER...", │
│ "subset": [{ "eventType": "sap.s4.beh.sales..." }] │
│ }] │
│ }] │
│ }] │
│ } │
└─────────────────────────────────────────────────────────────────┘
Using messaging.subscribe()
The subscribe() method consolidates event subscription and ORD declaration in a single place:
// srv/server.js
const cds = require("@sap/cds");
cds.on("loaded", async () => {
const messaging = await cds.connect.to("messaging");
// Subscribe to event with ORD Integration Dependency metadata
messaging.subscribe(
"sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1",
{
eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
},
async (event) => {
console.log("Event received:", event);
},
);
// Multiple events can share the same event resource
messaging.subscribe(
"sap.s4.beh.salesorder.v1.SalesOrder.Created.v1",
{
eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
},
async (event) => {
console.log("Event received:", event);
},
);
});Integration Dependency Generation
The generated ORD structure:
{
"integrationDependencies": [
{
"ordId": "customer.app:integrationDependency:consumedEvents:v1",
"title": "Consumed Events",
"version": "1.0.0",
"releaseStatus": "active",
"visibility": "public",
"partOfPackage": "customer.app:package:app-integrationDependency:v1",
"aspects": [
{
"title": "Subscribed Event Types",
"mandatory": false,
"eventResources": [
{
"ordId": "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
"subset": [
{
"eventType": "sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1"
},
{
"eventType": "sap.s4.beh.salesorder.v1.SalesOrder.Created.v1"
}
]
}
]
}
]
}
]
}Namespace Handling
integrationDependency.ordId: Uses consuming application's ORD namespaceeventResource.ordId: Uses external source namespace (fromeventResourceOrdIdparameter)
This matches the semantic meaning: the integration dependency belongs to the consuming app, while the event resource reference points to the external system.
Comparison: Original Proposal vs. Implementation
| Aspect | Original Proposal | Implementation |
|---|---|---|
| Event Detection | cds.services.*.subscribedTopics + annotations + config |
messaging.subscribe() with eventResourceOrdId |
| Configuration | cds.ord.consumedEventTypes in package.json |
Inline in subscribe() call |
| Event Broker Logic | In ORD plugin (lib/eventBrokerAdapter.js) |
In Event Broker plugin (cds-plugin.js) |
| Plugin Coupling | ORD plugin depends on Event Broker knowledge | Loose coupling via Extension Registry |
| Developer UX | Multiple files (CDS + config) | Single location (code) |
Comparison with Java Plugin
| Feature | Java (cds-feature-event-hub) |
Node.js (implemented) |
|---|---|---|
| Runtime Support | ✅ SPI-based | ✅ Extension Registry |
| Build-Time Support | ❌ | ❌ |
| Configuration | No config (auto-detection) | eventResourceOrdId |
| Event Detection | Spring bean introspection | subscribe() calls |
| Multiple Services | Aggregated | Aggregated |
Files Changed
@cap-js/ord
| File | Change |
|---|---|
lib/extensionRegistry.js |
New: Extension Registry API |
lib/integrationDependency.js |
Modified: Added createEventIntegrationDependency() |
cds-plugin.js |
Modified: Export registerIntegrationDependencyProvider() |
__tests__/unit/extensionRegistry.test.js |
New: Unit tests |
__tests__/unit/extensionRegistry.integration.test.js |
New: Integration tests |
@cap-js/event-broker
| File | Change |
|---|---|
cds-plugin.js |
Modified: Added subscribe() method with ORD support |
README.md |
Modified: Updated ORD Integration documentation |
Example Configuration
Service Implementation (srv/server.js)
const cds = require("@sap/cds");
cds.on("loaded", async () => {
const messaging = await cds.connect.to("messaging");
messaging.subscribe(
"sap.s4.beh.salesorder.v1.SalesOrder.Changed.v1",
{
eventResourceOrdId: "sap.s4:eventResource:CE_SALESORDEREVENTS:v1",
},
async (msg) => {
// Handle event
},
);
});Event Broker Configuration (package.json)
{
"cds": {
"requires": {
"messaging": {
"kind": "event-broker"
}
}
}
}No additional ORD configuration needed - the annotation provides the mapping.
Related Issues
- Consumed S/4 Events are exposed as eventResources instead as integrationDependencies #208 - Original feature request
- ORD and integration dependencies (consumed events) #81 - ORD and integration dependencies (consumed events)
- Auto-generate IntegrationDependencies for consumed Data Products #343 - Generic Integration Dependency support