Skip to content
Open
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
98 changes: 96 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ For more information, please see [SAP Cloud Application Event Hub](https://help.

### webhookSizeLimit

To set a size limit for events accepted by the webhook, set the ``webhookSizeLimit``parameter in the ``package.json`` file in the root folder of your app, e.g.
To set a size limit for events accepted by the webhook, set the `webhookSizeLimit`parameter in the `package.json` file in the root folder of your app, e.g.

```jsonc
"cds": {
Expand All @@ -76,7 +76,101 @@ To set a size limit for events accepted by the webhook, set the ``webhookSizeLim
}
```

If the parameter is not set, the [global request body size limit](https://pages.github.tools.sap/cap/docs/node.js/cds-server#maximum-request-body-size) ``cds.env.server.body_parser.limit`` is taken into account. If this parameter is not set either, the default value of ``1mb``is used.
If the parameter is not set, the [global request body size limit](https://pages.github.tools.sap/cap/docs/node.js/cds-server#maximum-request-body-size) `cds.env.server.body_parser.limit` is taken into account. If this parameter is not set either, the default value of `1mb`is used.

## ORD Integration

When both `@cap-js/event-broker` and `@cap-js/ord` plugins are installed, the Event Broker plugin can expose consumed events as an **Integration Dependency** in the ORD document.

### Using messaging.subscribe()

Use the `subscribe()` method instead of `messaging.on()` to declare event subscriptions with ORD metadata in a single place:

```javascript
// 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.businesspartner.v1.BusinessPartner.Changed.v1",
{
eventResourceOrdId: "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1",
},
async (event) => {
console.log("Event received:", event);
},
);
});
```

The `eventResourceOrdId` value should match the event resource identifier from the SAP Business Accelerator Hub or your event source's ORD document.

### Multiple Event Types per Event Resource

A single event resource can contain multiple event types. Simply use the same `eventResourceOrdId` for related events:

```javascript
// Both events belong to the same CE_BUSINESSPARTNEREVENTS resource
messaging.subscribe(
"sap.s4.beh.businesspartner.v1.BusinessPartner.Changed.v1",
{ eventResourceOrdId: "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1" },
handleBPChanged,
);

messaging.subscribe(
"sap.s4.beh.businesspartner.v1.BusinessPartner.Created.v1",
{ eventResourceOrdId: "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1" },
handleBPCreated,
);
```

The plugin automatically groups event types by their `eventResourceOrdId`.

### How it works

At runtime, when services are served, the Event Broker plugin:

1. Collects all event subscriptions registered via `messaging.subscribe()` with `eventResourceOrdId` option
2. Groups event types by their event resource ORD ID
3. Registers the eventResources with the ORD plugin's Extension API

### Example ORD Output

```json
{
"integrationDependencies": [
{
"ordId": "customer.myapp:integrationDependency:consumedEvents:v1",
"title": "Consumed Events",
"aspects": [
{
"title": "Subscribed Event Types",
"eventResources": [
{
"ordId": "sap.s4:eventResource:CE_BUSINESSPARTNEREVENTS:v1",
"subset": [
{
"eventType": "sap.s4.beh.businesspartner.v1.BusinessPartner.Changed.v1"
},
{
"eventType": "sap.s4.beh.businesspartner.v1.BusinessPartner.Created.v1"
}
]
}
]
}
]
}
]
}
```

### Backwards Compatibility

The `subscribe()` method is **non-breaking** - existing code using `messaging.on()` continues to work. However, only events registered via `messaging.subscribe()` with the `eventResourceOrdId` option will appear in the ORD Integration Dependency.

## Support, Feedback, Contributing

Expand Down
93 changes: 93 additions & 0 deletions cds-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ function _validateCertificate(req, res, next) {
}
}

// Logger for ORD integration (outside class context)
const LOG = cds.log('event-broker')

/**
* Global registry for programmatic ORD event resource mappings.
* Populated via EventBroker.subscribe() method.
* @type {Map<string, string>} eventType -> ordId
*/
const programmaticOrdMappings = new Map()

class EventBroker extends cds.MessagingService {
async init() {
await super.init()
Expand Down Expand Up @@ -350,6 +360,89 @@ class EventBroker extends cds.MessagingService {
res.status(500).json({ message: 'Internal Server Error!' })
}
}

/**
* Subscribe to an event with ORD metadata.
* This consolidates event subscription and ORD Integration Dependency declaration.
*
* @example
* 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 (event) => {
* console.log("Event received:", event)
* })
*
* @param {string} event - The event type to subscribe to
* @param {object} options - Options for ORD Integration Dependency
* @param {string} options.eventResourceOrdId - The ORD ID of the event resource (e.g., "sap.s4:eventResource:...")
* @param {Function} handler - The event handler function
*/
subscribe(event, options, handler) {
// Store ORD mapping if provided
if (options?.eventResourceOrdId) {
programmaticOrdMappings.set(event, options.eventResourceOrdId)
}

// Delegate to standard messaging.on()
return this.on(event, handler)
}
}

// ============================================================================
// ORD Integration Dependency Provider
// ============================================================================

/**
* Register Integration Dependency provider with ORD plugin.
* Called once when services are ready.
*/
function registerOrdIntegrationDependencyProvider() {
// Check if ORD plugin is available
let ordPlugin
try {
ordPlugin = require('@cap-js/ord')
} catch (e) {
// ORD plugin not installed - that's fine
return
}

if (!ordPlugin.registerIntegrationDependencyProvider) {
// Older ORD plugin version without Extension API
return
}

// Register provider function
ordPlugin.registerIntegrationDependencyProvider(() => {
// Simply return eventResources from programmatic mappings
// No need to check subscribedTopics - if subscribe() was called, it's subscribed
if (programmaticOrdMappings.size === 0) {
return null
}

// Group events by ordId
const ordIdToEvents = new Map()
for (const [eventType, ordId] of programmaticOrdMappings) {
if (!ordIdToEvents.has(ordId)) {
ordIdToEvents.set(ordId, [])
}
ordIdToEvents.get(ordId).push(eventType)
}

// Build eventResources array
const eventResources = []
for (const [ordId, events] of ordIdToEvents) {
eventResources.push({ ordId, events })
}

LOG.info(`Providing ${eventResources.length} eventResource(s) for ORD Integration Dependency`)
return { eventResources }
})
}

// Register when services are served (runtime only)
cds.once('served', () => {
registerOrdIntegrationDependencyProvider()
})

module.exports = EventBroker
Loading