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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
node-version: ${{ matrix.node-version }}
- run: npm i -g @sap/cds-dk@${{ matrix.cds-version }}
- run: npm i
- run: if [ ${{ matrix.cds-version }} -eq 9 ]; then npm i -f @sap/cds@8 @cap-js/sqlite@2 @sap/cds-mtxs@3; fi
#- run: if [ ${{ matrix.cds-version }} -eq 9 ]; then npm i -f @sap/cds@9 @cap-js/sqlite@2 @sap/cds-mtxs@3; fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The commented-out step originally forced @sap/cds@8 (now corrected to @sap/cds@9 in the comment), but the active matrix only tests cds-version: [9] while cds-version: [10] is also commented out. The better-sqlite3 rebuild step on line 40 still fires for cds-version -eq 9, so this is fine for now — but the commented line references a workaround that is no longer needed if cds@9 is already installed via line 37. The comment text says @sap/cds@9 but this step was originally meant to downgrade when cds-dk installed a newer version; the comment is now misleading about its purpose.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

- run: if [ ${{ matrix.cds-version }} -eq 9 ]; then npm explore better-sqlite3 -- npm run install; fi
- run: cds v
- run: npm run test
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
node-version: 24
registry-url: https://registry.npmjs.org/
- name: run tests
# REVISIT: remove "npm explore better-sqlite3 -- npm run install" with cds^10
run: |
npm i -g @sap/cds-dk
npm i
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,5 @@ dist
# test
test/msg-box
test/bookshop/gen
test/bookshop/.cdsrc.json

.claude/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).

- Support for OpenTelemetry SDK 2.0
- `@opentelemetry/instrumentation-undici` added to the list of default instrumentations
- Support for `@sap/cds^10`

### Changed

Expand All @@ -21,6 +22,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).

### Removed

- Support for `@sap/cds^8`

## Version 1.6.0 - 2025-12-16

### Added
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
"@opentelemetry/semantic-conventions": "^1.41"
},
"peerDependencies": {
"@sap/cds": "^8 || ^9"
"@sap/cds": "^9"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The peerDependencies was updated to "@sap/cds": "^9" but the CHANGELOG and PR description say this release adds support for cds^10 and drops cds^8. The peer dependency range should be "^9 || ^10" (or "^10" if ^9 support is also being dropped) to accurately reflect compatibility.

Suggested change
"@sap/cds": "^9"
"@sap/cds": "^9 || ^10"

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

},
"devDependencies": {
"@cap-js/cds-test": "^0",
"@cap-js/sqlite": "^1 || ^2",
"@cap-js/sqlite": "^2",
"@cap-js/telemetry": "file:.",
"@grpc/grpc-js": "^1.9.14",
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.219",
Expand All @@ -43,7 +43,7 @@
"@opentelemetry/exporter-trace-otlp-proto": "^0.219",
"@opentelemetry/instrumentation-host-metrics": "^0.2.0",
"@opentelemetry/instrumentation-runtime-node": "^0.32.0",
"@sap/cds-mtxs": "^2 || ^3",
"@sap/cds-mtxs": "^3",
"axios": "^1.6.7",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
Expand Down
102 changes: 102 additions & 0 deletions test/bookshop/.cdsrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"[logging]": {
"requires": {
"telemetry": {
"tracing": {
"sampler": {
"ignoreIncomingPaths": ["/odata/v4/admin/Genres"]
}
},
"logging": {
"exporter": {
"module": "@opentelemetry/sdk-logs",
"class": "ConsoleLogRecordExporter"
},
"processor": {
"module": "./lib/MySimpleLogRecordProcessor.js",
"class": "MySimpleLogRecordProcessor"
}
}
}
},
"log": {
"format": "json",
"cls_custom_fields": ["foo"]
}
},
"[metrics]": {
"requires": {
"telemetry": {
"metrics": {
"config": {
"exportIntervalMillis": 100
}
}
}
}
},
"[metrics-outbox]": {
"requires": {
"queue": true,
"telemetry": {
"metrics": {
"config": {
"exportIntervalMillis": 100
},
"_db_pool": false,
"_queue": true,
"exporter": {
"module": "@opentelemetry/sdk-metrics",
"class": "ConsoleMetricExporter"
}
}
}
}
},
"[metrics-outbox-disabled]": {
"requires": {
"queue": true,
"telemetry": {
"metrics": {
"config": {
"exportIntervalMillis": 250
},
"_db_pool": false,
"_queue": false,
"exporter": {
"module": "@opentelemetry/sdk-metrics",
"class": "ConsoleMetricExporter"
}
}
}
}
},
"[tracing-attributes]": {
"requires": {
"telemetry": {
"tracing": {
"exporter": {
"module": "@opentelemetry/sdk-trace-node"
}
}
}
}
},
"[persistent-outbox]": {
"requires": {
"messaging": {
"kind": "file-based-messaging",
"file": "../persistent-outbox"
}
}
},
"[without-outbox]": {
"requires": {
"messaging": {
"kind": "file-based-messaging",
"file": "../without-outbox",
"outboxed": false
}
}
}
}
40 changes: 1 addition & 39 deletions test/bookshop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@
"_kind": "file-based-messaging",
"file": "../msg-box"
},
"_outbox": {
"kind": "persistent-outbox"
},
"queue": {
"legacyLocking": false
},
"_scheduling": true,
"ExternalServiceOne": {
"kind": "odata",
"model": "srv/external/ExternalServiceOne"
Expand Down Expand Up @@ -79,42 +77,6 @@
}
}
},
"[metrics-outbox]": {
"requires": {
"queue": true,
"telemetry": {
"metrics": {
"config": {
"exportIntervalMillis": 100
},
"_db_pool": false,
"_queue": true,
"exporter": {
"module": "@opentelemetry/sdk-metrics",
"class": "ConsoleMetricExporter"
}
}
}
}
},
"[metrics-outbox-disabled]": {
"requires": {
"queue": true,
"telemetry": {
"metrics": {
"config": {
"exportIntervalMillis": 250
},
"_db_pool": false,
"_queue": false,
"exporter": {
"module": "@opentelemetry/sdk-metrics",
"class": "ConsoleMetricExporter"
}
}
}
}
},
"log": {
"cls_custom_fields": [
"tenant_id"
Expand Down
27 changes: 3 additions & 24 deletions test/logging.test.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,10 @@
/* eslint-disable no-console */

process.env.cds_log = JSON.stringify({
format: 'json',
cls_custom_fields: ['foo']
})

process.env.cds_requires_telemetry = JSON.stringify({
tracing: {
sampler: {
ignoreIncomingPaths: ['/odata/v4/admin/Genres']
}
},
logging: {
exporter: {
module: '@opentelemetry/sdk-logs',
class: 'ConsoleLogRecordExporter'
},
// experimental feature of the experimental feature!!!
processor: {
module: './lib/MySimpleLogRecordProcessor.js',
class: 'MySimpleLogRecordProcessor'
}
}
})
// REVISIT: even with profile "logging", cls_custom_fields from package.json wins
process.env.cds_log = JSON.stringify({ cls_custom_fields: ['foo'] })

const cds = require('@sap/cds')
const { expect, GET } = cds.test().in(__dirname + '/bookshop')
const { expect, GET } = cds.test(__dirname + '/bookshop', '--profile', 'logging')

describe('logging', () => {
const admin = { auth: { username: 'alice' } }
Expand Down
4 changes: 2 additions & 2 deletions test/metrics-outbox-multitenant.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('queue metrics for multi tenant service', () => {
GET('/odata/v4/proxy/proxyCallToExternalServiceOne', user[T2])
])

await wait(300) // Wait for metrics to be collected
await wait(150) // Wait for metrics to be collected

expect(metricValue(T1, 'cold_entries')).to.eq(0)
expect(metricValue(T1, 'incoming_messages')).to.eq(totalInc[T1])
Expand Down Expand Up @@ -138,7 +138,7 @@ describe('queue metrics for multi tenant service', () => {
if (timeAfterFirstRetry - timeOfInitialCall < 1000) {
await wait(1000 - (timeAfterFirstRetry - timeOfInitialCall))
}
await wait(300) // ... for metrics to be collected
await wait(150) // ... for metrics to be collected

expect(metricValue(T1, 'cold_entries')).to.eq(0)
expect(metricValue(T1, 'incoming_messages')).to.eq(totalInc[T1])
Expand Down
20 changes: 11 additions & 9 deletions test/metrics-outbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('queue metrics for single tenant service', () => {
test('metrics are collected', async () => {
await GET('/odata/v4/proxy/proxyCallToExternalServiceOne', admin)

await wait(300) // Wait for metrics to be collected
await wait(150) // Wait for metrics to be collected

expect(metricValue('cold_entries', E1)).to.eq(0)
expect(metricValue('remaining_entries', E1)).to.eq(0)
Expand All @@ -99,7 +99,7 @@ describe('queue metrics for single tenant service', () => {

await GET('/odata/v4/proxy/proxyCallToExternalServiceTwo', admin)

await wait(300) // Wait for metrics to be collected
await wait(150) // Wait for metrics to be collected

expect(metricValue('cold_entries', E2)).to.eq(0)
expect(metricValue('remaining_entries', E2)).to.eq(0)
Expand Down Expand Up @@ -149,8 +149,9 @@ describe('queue metrics for single tenant service', () => {
await GET('/odata/v4/proxy/proxyCallToExternalServiceOne', admin)
await GET('/odata/v4/proxy/proxyCallToExternalServiceTwo', admin)

await wait(300) // ... for metrics to be collected
await wait(150) // ... for metrics to be collected
expect(currentRetryCount[E1]).to.eq(1)
expect(currentRetryCount[E2]).to.eq(1)

expect(metricValue('cold_entries', E1)).to.eq(0)
expect(metricValue('remaining_entries', E1)).to.eq(1)
Expand All @@ -171,8 +172,8 @@ describe('queue metrics for single tenant service', () => {
expect(metricValue('max_storage_time_in_seconds', E2)).to.eq(0)

// Wait for the first retry to be initiated
while (currentRetryCount[E1] < 2) await wait(100)
while (currentRetryCount[E1] < 2) await wait(100)
while (currentRetryCount[E1] < 2) await wait(10)
while (currentRetryCount[E2] < 2) await wait(10)
await wait(150) // ... for the retry to be processed and metrics to be collected
expect(currentRetryCount[E1]).to.eq(2)
expect(currentRetryCount[E2]).to.eq(2)
Expand All @@ -183,7 +184,7 @@ describe('queue metrics for single tenant service', () => {
await wait(1000 - (timeAfterFirstRetry - timeOfInitialCall))
}

await wait(300) // ... for metrics to be collected again
await wait(150) // ... for metrics to be collected again

expect(metricValue('cold_entries', E1)).to.eq(0)
expect(metricValue('remaining_entries', E1)).to.eq(1)
Expand All @@ -204,8 +205,9 @@ describe('queue metrics for single tenant service', () => {
expect(metricValue('max_storage_time_in_seconds', E2)).to.be.gte(1)

// Wait for the second retry to be initiated
while (currentRetryCount[E1] < 3) await wait(100)
await wait(300) // ... for the retry to be processed and metrics to be collected
while (currentRetryCount[E1] < 3) await wait(10)
while (currentRetryCount[E2] < 3) await wait(10)
await wait(150) // ... for the retry to be processed and metrics to be collected
expect(currentRetryCount[E1]).to.eq(3)
expect(currentRetryCount[E2]).to.eq(3)

Expand Down Expand Up @@ -258,7 +260,7 @@ describe('queue metrics for single tenant service', () => {
await GET('/odata/v4/proxy/proxyCallToExternalServiceOne', admin)
await GET('/odata/v4/proxy/proxyCallToExternalServiceTwo', admin)

await wait(300) // ... for metrics to be collected
await wait(150) // ... for metrics to be collected

expect(metricValue('cold_entries', E1)).to.eq(1)
expect(metricValue('remaining_entries', E1)).to.eq(0)
Expand Down
3 changes: 1 addition & 2 deletions test/metrics.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// process.env.HOST_METRICS_RETAIN_SYSTEM = 'true' //> with this the test would fail
process.env.HOST_METRICS_LOG_SYSTEM = 'true'
process.env.cds_requires_telemetry_metrics_config = JSON.stringify({ exportIntervalMillis: 100 })

const cds = require('@sap/cds')
const { expect, GET } = cds.test().in(__dirname + '/bookshop')
const { expect, GET } = cds.test(__dirname + '/bookshop', '--profile', 'metrics')
const log = cds.test.log()

const wait = require('node:timers/promises').setTimeout
Expand Down
4 changes: 1 addition & 3 deletions test/tracing-attributes.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
process.env.cds_requires_telemetry_tracing_exporter_module = '@opentelemetry/sdk-trace-node'

const cds = require('@sap/cds')
const { expect, data } = cds.test().in(__dirname + '/bookshop')
const { expect, data } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-attributes')
const http = require('http')

describe('tracing attributes', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
const CASE = 'with_persistent-outbox'
const CASE = 'persistent-outbox'

const env = {
// REVISIT: even with profile "persistent-outbox", messaging kind and file from package.json wins
process.env.cds_requires_messaging = JSON.stringify({
kind: 'file-based-messaging',
outbox: true,
file: `../${CASE}`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The persistent-outbox profile only sets messaging kind/file but omits the persistent outbox configuration (the original code also set cds_requires_outbox = { kind: 'persistent-outbox' }). When this test is re-enabled, it will not actually exercise persistent outbox behavior — the missing outbox config should be tracked in a REVISIT comment or restored in the profile.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

}
process.env.cds_requires_messaging = JSON.stringify(env)
process.env.cds_requires_outbox = JSON.stringify({ kind: 'persistent-outbox' })

process.env.cds_requires_telemetry_metrics = null
})

// REVISIT: check json exports
const CHECK = (log, expect) => {
// 3: outbox -> consumers get new root context
// REVISIT: for some reason, span "cds.spawn run task" has no parent when running in jest
expect(log.output.match(/\[telemetry\] - elapsed times:/g).length).to.equal(4) //> actually 3
expect(log.output.match(/cds.spawn - schedule task/g).length).to.equal(1)
}

describe(`tracing messaging - ${CASE}`, () => {
// REVISIT: re-enable with switch to vitest
describe.skip(`tracing messaging - ${CASE}`, () => {
require('./tracing-messaging')(CASE, CHECK)
})
Loading