Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
66b1387
Fixed scope of ObjectModel.semanticKey to be valid on Entity, not Typ…
Fannon May 13, 2025
234cf23
run generate
Fannon May 13, 2025
687197a
Update spec/v1/annotations/object-model.yaml
Fannon May 13, 2025
2cfd609
run generate
Fannon May 13, 2025
0d5e118
Prepare for v1.0.4 release
Fannon May 13, 2025
5347021
Migrate to latest json-schema-library (breaking changes)
Fannon May 13, 2025
d3b9b04
Use rimraf instead of bash rm -rf
Fannon May 13, 2025
e51a8a9
Try to get jest working, but still problems
Fannon May 13, 2025
cec949b
State EntityRelationship ID constrains and fix links to ORD (#88)
Fannon May 14, 2025
3333b27
fix spec-toolkit, scope is missing for properties of type object def…
maiargu May 14, 2025
525efc8
Forbit v0 and v1 in entityType and propertyType IDs (#92)
Fannon May 14, 2025
5d8470b
Fixed scope of ObjectModel.semanticKey to be valid on Entity, not Typ…
Fannon May 15, 2025
012df54
fix trigger re-generate json schema files (#93)
maiargu May 15, 2025
716bfb9
fixed package to contain typescript types (#94)
maiargu May 16, 2025
11e0aa8
docusaurus UI disable "edit this page" button (#97)
maiargu May 21, 2025
df76503
fix regex special characters escaping in markdown result (#96)
maiargu May 21, 2025
335179f
Add @ObjectModel.custom annotation (#64)
Fannon May 27, 2025
7cdd967
Update mapping tables for ABAP and Apache Spark (#95)
frankejoe Jun 4, 2025
904824c
Update TestEntity.cds to be compatible with latest CAP CDS compiler (…
Fannon Jun 12, 2025
88c084b
Mention that ER references are to-one cardinality only (#101)
Fannon Jun 24, 2025
a19636b
Copy as part of generate (#103)
Fannon Jun 25, 2025
d215a3e
Define media type for CSN Interop (#102)
Fannon Jun 25, 2025
fa1db7b
switch to trusted publishing for npmjs.com (#107)
Shegox Aug 5, 2025
f7a1ffe
Add key atribute to cds.Decimal (#106)
Fannon Aug 5, 2025
1bf9a7b
Add meta.document.name and .namespace (#105)
Fannon Aug 5, 2025
15f43fe
Release v1.0.7 (#108)
Fannon Aug 5, 2025
a48495f
Adding cds.Binary and cds.LargeBinary (#115)
Fannon Nov 12, 2025
8c58a96
Update dependencies and use lefthook instead of husky
Fannon Nov 13, 2025
fca8def
Resolve merge conflicts: keep version 1.1.0 and updated dependencies
Fannon Nov 13, 2025
bb5911e
Fix json-schema-library version to stable 10.0.0
Fannon Nov 13, 2025
6fbba3d
Update spec toolkit package-lock
Fannon Nov 13, 2025
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
8 changes: 5 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ jobs:
# registry-url is required for releasing packages
registry-url: "https://registry.npmjs.org"

- name: Install latest npm cli
run: npm install -g npm@latest

- name: npm install
run: npm ci

- name: build
run: npm run build

- name: Publish package
# --provenance enables the automatic generation of provenance statements
# --provenance enables the automatic generation of provenance statements (when using trusted publisher, this is automatically enabled and therefore optional)
# --access public is only hard required for the initial release, but it doesn't hurt having it setup
# npm version >=11.5.1 required for trusted publisher
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
69 changes: 69 additions & 0 deletions .husky/_/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/sh

if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then
set -x
fi

if [ "$LEFTHOOK" = "0" ]; then
exit 0
fi

call_lefthook()
{
if test -n "$LEFTHOOK_BIN"
then
"$LEFTHOOK_BIN" "$@"
elif lefthook -h >/dev/null 2>&1
then
lefthook "$@"
else
dir="$(git rev-parse --show-toplevel)"
osArch=$(uname | tr '[:upper:]' '[:lower:]')
cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/')
if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook"
then
"$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@"
elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook"
then
"$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@"
elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook"
then
"$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@"
elif test -f "$dir/node_modules/lefthook/bin/index.js"
then
"$dir/node_modules/lefthook/bin/index.js" "$@"

elif go tool lefthook -h >/dev/null 2>&1
then
go tool lefthook "$@"
elif bundle exec lefthook -h >/dev/null 2>&1
then
bundle exec lefthook "$@"
elif yarn lefthook -h >/dev/null 2>&1
then
yarn lefthook "$@"
elif pnpm lefthook -h >/dev/null 2>&1
then
pnpm lefthook "$@"
elif swift package lefthook >/dev/null 2>&1
then
swift package --build-path .build/lefthook --disable-sandbox lefthook "$@"
elif command -v mint >/dev/null 2>&1
then
mint run csjones/lefthook-plugin "$@"
elif uv run lefthook -h >/dev/null 2>&1
then
uv run lefthook "$@"
elif mise exec -- lefthook -h >/dev/null 2>&1
then
mise exec -- lefthook "$@"
elif devbox run lefthook -h >/dev/null 2>&1
then
devbox run lefthook "$@"
else
echo "Can't find lefthook in PATH"
fi
fi
}

call_lefthook run "pre-commit" "$@"
69 changes: 69 additions & 0 deletions .husky/_/prepare-commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/sh

if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then
set -x
fi

if [ "$LEFTHOOK" = "0" ]; then
exit 0
fi

call_lefthook()
{
if test -n "$LEFTHOOK_BIN"
then
"$LEFTHOOK_BIN" "$@"
elif lefthook -h >/dev/null 2>&1
then
lefthook "$@"
else
dir="$(git rev-parse --show-toplevel)"
osArch=$(uname | tr '[:upper:]' '[:lower:]')
cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/')
if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook"
then
"$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@"
elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook"
then
"$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@"
elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook"
then
"$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@"
elif test -f "$dir/node_modules/lefthook/bin/index.js"
then
"$dir/node_modules/lefthook/bin/index.js" "$@"

elif go tool lefthook -h >/dev/null 2>&1
then
go tool lefthook "$@"
elif bundle exec lefthook -h >/dev/null 2>&1
then
bundle exec lefthook "$@"
elif yarn lefthook -h >/dev/null 2>&1
then
yarn lefthook "$@"
elif pnpm lefthook -h >/dev/null 2>&1
then
pnpm lefthook "$@"
elif swift package lefthook >/dev/null 2>&1
then
swift package --build-path .build/lefthook --disable-sandbox lefthook "$@"
elif command -v mint >/dev/null 2>&1
then
mint run csjones/lefthook-plugin "$@"
elif uv run lefthook -h >/dev/null 2>&1
then
uv run lefthook "$@"
elif mise exec -- lefthook -h >/dev/null 2>&1
then
mise exec -- lefthook "$@"
elif devbox run lefthook -h >/dev/null 2>&1
then
devbox run lefthook "$@"
else
echo "Can't find lefthook in PATH"
fi
fi
}

call_lefthook run "prepare-commit-msg" "$@"
1 change: 0 additions & 1 deletion .husky/pre-commit

This file was deleted.

5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "vscode.json-language-features"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
Expand All @@ -59,5 +59,6 @@
"fileMatch": ["/examples/**/*.json"],
"url": "./src/spec-v1/csn-interop-effective.schema.json"
}
]
],
"jest.runMode": "on-demand"
}
51 changes: 50 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@ For a roadmap including expected timeline, please refer to [ROADMAP.md](./ROADMA

## [unreleased]

## [1.1.0]

### Added

- Added `cds.Binary` and `cds.LargeBinary` types
- Added new `@Semantics` annotations:
- `@Semantics.mimeType`
- `@Semantics.largeObject.acceptableMimeTypes`
- `@Semantics.largeObject.mimeType`
- `@Semantics.largeObject.fileName`

## [1.0.7]

### Added

- Added `meta.document.name` to give the overall document a machine-readable name
- Added `meta.document.namespace` to give the overall document a globally unique namespace
- Allow `cds.Decimal` to be used as a key, indicated via `key` boolean

## [1.0.6]

### Added

- Added `@ObjectModel.custom` annotation

## [1.0.5]

### Fixed

- typescript type `SpecJsonSchemaRoot` is missing in the packed NpmJS artefact

## [1.0.4]

### Changed

- BREAKING: TypeScript interface `CSNInteropRoot` renamed to `CSNInteropEffectiveDocument`
Expand All @@ -19,17 +52,33 @@ For a roadmap including expected timeline, please refer to [ROADMAP.md](./ROADMA
- Added missing property `$id` to annotation extension schemas
- Added clarification that `key: true` also implies `notNull: true`
- Added clarification that `precision` and `scale` are RECOMMENDED to be added and MUST be added if own default assumptions diverge from the specified default.
<<<<<<< HEAD
- Added explicit regexp pattern to `@EntityRelationship` Entity Type and Property Type IDs
- Added the ID constraints from ORD page more explicitly back to CSN Interop page and fixed links
- # Added explicit regexp pattern to `@ODM` Entity IDs

### Fixed

- Fixed scope of `@ObjectModel.semanticKey` to be valid on Entity, not Type level.

## [1.0.3]

> > > > > > > e51a8a9d933873be8f1269f90fdfedfe18dba9fa

### Fixed

- Fixed the "scope" of annotations that belong to type, that they also apply to type definitions
- Fixed scope of `@ObjectModel.semanticKey` to be valid on Entity, not Type level.

## [1.0.3]

### Added

- Added `@ObjectModel.tenantWideUniqueName` annotation

### Fixed

- Fixed the "scope" of annotations that belong to type, that they also apply to type definitions

## [1.0.2]

### Added
Expand Down
13 changes: 9 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ title: "Overview"

## Summary

Core Schema Notation Interoperability (short: CSN Interop) is a powerful and flexible format used to describe entity and service models in the wider SAP and BTP ecosystem. CSN files are JSON-based and provide comprehensive metadata about entities and their structure, relationships, and other aspects of the model.
Core Schema Notation Interoperability (short: CSN Interop) is a modeling format to describe entity and service models in the wider SAP and BTP ecosystem.
CSN files serialized as JSON and provide comprehensive metadata about entities and their structure, relationships, and other aspects of the model.

A CSN Interop file can look like this (extracted from [./examples/airline.json](./spec-v1/examples/airline.md)):

Expand Down Expand Up @@ -49,8 +50,6 @@ The actual specification is described mostly in the [formal interface documentat

## What is CSN Interop Effective?

For now, we describe the [CSN](#csn) [Interoperability](#interoperability) [Effective](#effective) exchange format.

### CSN

Core Schema Notation (CSN, pronounced as "Season") is a JSON based serialization format for Core Data Services (CDS) models that can be used to describe domain, data and service models (and more) on a _conceptual_ level, with rich semantics and annotations.
Expand Down Expand Up @@ -79,10 +78,16 @@ This includes:

Right now, this spec describes only the [effective](./spec-v1/csn-interop-effective) feature dimension.

Effective means that the format is "[denormalized](https://en.wikipedia.org/wiki/Denormalization)", and optimized towards easy consumption by machines, with the tradeoff of more verbosity and duplicated information.
Effective means that the format is [denormalized](https://en.wikipedia.org/wiki/Denormalization), and optimized towards easy consumption by machines, with the tradeoff of more verbosity and duplicated information.

Information reuse concepts like aspects have already been resolved, applied and cleaned up. What the consumer gets, is a document that does not require further post-processing / logic to be interpreted correctly. This is a tradeoff, prioritizing easy consumption over convenient creation.

### Serialization

A CSN Interop file is serialized as [JSON](https://www.json.org/json-en.html).

The correct media type for CSN Interop files is either `application/json` (because it is a valid JSON file) or `application/csn-interop+json` if you want to be more specific about the content type.

## Intended Audience

- Developers and Architects that either need to export or import CSN across different technology stacks.
Expand Down
82 changes: 41 additions & 41 deletions docs/mappings/abap.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,46 @@ description: "ABAP Type System"

# ABAP to CSN Interop

> <span className="feature-status-draft">DRAFT</span> This mapping definition is work in progress and may be subject to further change.
<span className="feature-status-draft">DRAFT</span> This mapping definition is work in progress and may be subject to further change.

<!-- prettier-ignore -->
| ABAP DataType | CDS Datatype | Properties | Comment | New CDS Datatype |
|-------------- | ------------ | ---------- | ------- | ----------------- |
| abap.cuky (len=5) | cds.String | length = 5 | | no change |
| abap.unit (len=3) | cds.String | length = 3 | | no change |
| abap.char (len=x) | cds.String | length = x | | no change |
| abap.varc (len=x) | cds.String | length = x | | no change |
| abap.sstring (len=x) | cds.String | length = x | | no change |
| abap.numc (len=x) | cds.String | length = x | | no change |
| abap.clnt (len=3) | cds.String | length = 2 | | no change |
| abap.lang (len=2) | cds.String | length = 2 | | no change |
| abap.accp (len=6) | cds.String | length = 6 | | no change |
| special logic | cds.Boolean | | decision is taken based on certain domains | no change |
| abap.utclong | cds.Timestamp | | | no change |
| abap.tims | cds.Time | | | no change |
| abap.timn | cds.Time | | | no change |
| abap.dats | cds.Date | | | no change |
| abap.datn | cds.Date | | | no change |
| abap.dec(precision = x, scale = y) | cds.Decimal | precision = x, scale = y | | no change |
| abap.quan(precision = x, scale = y) | cds.Decimal | precision = x, scale = y | | no change |
| abap.decfloat16(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | | cds.decimal(16,?) |
| abap.df16_dec(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | | cds.decimal(16,?) |
| abap.df16_raw(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | | cds.decimal(16,?) |
| abap.df16_scl(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | | cds.decimal(16,?) |
| abap.decfloat34(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | | cds.decimal(34,?) |
| abap.df34_dec(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | | cds.decimal(34,?) |
| abap.df34_raw(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | | cds.decimal(34,?) |
| abap.df34_scl(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | | cds.decimal(34,?) |
| abap.curr(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | | cds.decimal(34,?) |
| abap.int8 | cds.Integer64 | | | no change |
| abap.int1 | cds.Integer | | | no change |
| abap.int2 | cds.Integer | | | no change |
| abap.int4 | cds.Integer | | | no change |
| abap.prec | cds.Integer | | | no change |
| abap.raw | cds.binary | | | default: cds.String(2 \* raw-length) - later we have to discuss how to encode e.g. images or for which data types we use cds.UUID (max 36) (for a dedicated list of abap data types) - for cds.UUID use rules from OData Data Types |
| abap.fltp | cds.double | | | cds.Double |
| abap.string | cds.largestring | | | cds.String length is either given or blank |
| abap.lchr | cds.largestring | | | cds.String length is either given or blank |
| abap.lraw | cds.largebinary | | | not supported |
| abap.rawstring | cds.largebinary | | | not supported |
| abap.geom_ewkb | cds.largebinary | | | not supported |
| ABAP DataType | CDS Datatype | Properties | Spark Type | ABAP Format | Comment | | Transformer |
|-------------- | ------------ | ---------- | ---------- | ----------- | ------- | - | ----------- |
| abap.cuky (len=5) | cds.String | length = 5 | STRING(5) | | | | - |
| abap.unit (len=3) | cds.String | length = 3 | STRING(3) | | | | - |
| abap.char (len=x) | cds.String | length = x | STRING(x) | | | | - |
| abap.varc (len=x) | cds.String | length = x | STRING(x) | | | | - |
| abap.sstring (len=x) | cds.String | length = x | STRING(x) | | | | - |
| abap.string | cds.LargeString | | STRING | | | | - |
| abap.lchr | cds.LargeString | | STRING | | | | - |
| abap.rawstring | cds.LargeString | | STRING | | | | - |
| abap.geom_ewkb | cds.LargeString | | STRING | | | | - |
| abap.numc (len=x) | cds.String | length = x | STRING(x) | | | | - |
| abap.clnt (len=3) | cds.String | length = 3 | STRING(3) | | | | - |
| abap.lang (len=2) | cds.String | length = 2 | STRING(2) | | | | - |
| abap.accp (len=6) | cds.String | length = 6 | STRING(6) | | | | - |
| abap.char(1) (*"@Semantic.booleanIndicator: true"*) | cds.String | length = 1 | STRING(1) | | We can't enforce the right values - therefore we must use string | | - |
| abap.utclong | cds.Timestamp | "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS" | TIMESTAMP | | | | "castToTimestamp": \[\{ "sourceColumnName": "abap_tstmpl", "sourceFormat": \["yyyy-MM-dd'T'HH:mm:ss.SSSSSSS"\], "valueReplacements": \[\{"sourceValues": \[ "" \], "targetValue": "NULL_VALUE" \}\]\}\] |
| abap.tims | cds.Time | | STRING(6) | "HHmmss" | type time not available in spark | | - |
| abap.timn | cds.Time | | STRING(12) | "HH:mm:ss.SSS" | type time not available in spark | | - |
| abap.dats | cds.Date | | DATE | "yyyyMMdd" | | | "castToDate": \[\{ "sourceColumnName": "abap_dats", "sourceFormat": \["yyyyMMdd"\], "valueReplacements": \[\{"sourceValues": \[ "00000000", "" \], "targetValue": "NULL_VALUE" \}\]\}\] |
| abap.datn | cds.Date | | DATE | "yyyy-MM-dd" | | | "castToDate": \[\{ "sourceColumnName": "abap_dats", "sourceFormat": \["yyyy-MM-dd"\], "valueReplacements": \[\{"sourceValues": \[ "0000-00-00", "" \], "targetValue": "NULL_VALUE" \}\]\}\] |
| abap.dec(precision = p, scale = s) | cds.Decimal | precision = p, scale = s | DECIMAL(p,s) | | | | - |
| abap.quan(precision = p, scale = s) | cds.Decimal | precision = p, scale = s | DECIMAL(p,s) | | | | - |
| abap.decfloat16(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | *not supported* | | | | - |
| abap.df16_dec(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | *not supported* | | | | - |
| abap.df16_raw(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | *not supported* | | | | - |
| abap.df16_scl(precision = 16, scale = floating) | cds.Decimal | precision = 16, scale = floating | *not supported* | | | | - |
| abap.decfloat34(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | *not supported* | | | | - |
| abap.df34_dec(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | *not supported* | | | | - |
| abap.df34_raw(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | *not supported* | | | | - |
| abap.df34_scl(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = floating | *not supported* | | | | - |
| abap.curr(precision = 34, scale = floating) | cds.Decimal | precision = 34, scale = 4 | DECIMAL(34, 4) | | | | - |
| abap.int8 | cds.Integer64 | | BIGINT | | | | - |
| abap.int1 | cds.Integer | | INT | | | | - |
| abap.int2 | cds.Integer | | INT | | | | - |
| abap.int4 | cds.Integer | | INT | | | | - |
| abap.prec | cds.Integer | | INT | | | | - |
| abap.fltp | cds.Double | | DOUBLE | | | | - |
| abap.raw | *not supported* | | | *not supported* | | | - |
| abap.lraw | *not supported* | | | *not supported* | | | - |
Loading
Loading