From 68ffdaf9328e1401320ab092645c8580e636a353 Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Fri, 4 Oct 2024 12:14:39 +0200 Subject: [PATCH 1/6] feat: add static `elements` to classes to access LinkedDefinitions --- lib/components/basedefs.js | 4 ++++ lib/components/wrappers.js | 8 ++++++++ lib/visitor.js | 12 ++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/components/basedefs.js b/lib/components/basedefs.js index 763fcf3b..b71e7309 100644 --- a/lib/components/basedefs.js +++ b/lib/components/basedefs.js @@ -13,6 +13,10 @@ const timeRegex = '`${number}${number}:${number}${number}:${number}${number}`' const baseDefinitions = new SourceFile('_') // FIXME: this should be a library someday baseDefinitions.addPreamble(` +import { entity } from '@sap/cds' + +export type ElementsOf = {[name in keyof DeepRequired]: entity["elements"][string]} + export namespace Association { export type to = T; export namespace to { diff --git a/lib/components/wrappers.js b/lib/components/wrappers.js index d442e5ed..cd9626db 100644 --- a/lib/components/wrappers.js +++ b/lib/components/wrappers.js @@ -17,6 +17,13 @@ const createKey = t => `${base}.Key<${t}>` */ const createKeysOf = t => `${base}.KeysOf<${t}>` +/** + * Wraps type into ElementsOf type. + * @param {string} t - the type name. + * @returns {string} + */ +const createElementsOf = t => `${base}.ElementsOf<${t}>` + /** * Wraps type into association to scalar. * @param {string} t - the singular type name. @@ -102,6 +109,7 @@ module.exports = { createArrayOf, createKey, createKeysOf, + createElementsOf, createObjectOf, createPromiseOf, createUnionOf, diff --git a/lib/visitor.js b/lib/visitor.js index 3ffde294..c379db3c 100644 --- a/lib/visitor.js +++ b/lib/visitor.js @@ -8,7 +8,7 @@ const { SourceFile, FileRepository, Buffer, Path } = require('./file') const { FlatInlineDeclarationResolver, StructuredInlineDeclarationResolver } = require('./components/inline') const { Resolver } = require('./resolution/resolver') const { LOG } = require('./logging') -const { docify, createPromiseOf, createUnionOf, createKeysOf } = require('./components/wrappers') +const { docify, createPromiseOf, createUnionOf, createKeysOf, createElementsOf } = require('./components/wrappers') const { csnToEnumPairs, propertyToInlineEnumName, isInlineEnumType, stringifyEnumType } = require('./components/enum') const { isReferenceType } = require('./components/reference') const { empty } = require('./components/typescript') @@ -162,6 +162,14 @@ class Visitor { buffer.add(`declare static readonly keys: ${createKeysOf(clean)}`) } + /** + * @param {Buffer} buffer - the buffer to write the elements into + * @param {string} clean - the clean name of the entity + */ + #printStaticElements(buffer, clean) { + buffer.add(`declare static readonly elements:${createElementsOf(clean)}`) + } + /** * Transforms an entity or CDS aspect into a JS aspect (aka mixin). * That is, for an element A we get: @@ -275,7 +283,7 @@ class Visitor { } this.#printStaticActions(entity, buffer, ancestorInfos, file) this.#printStaticKeys(buffer, clean) - + this.#printStaticElements(buffer, clean) }) }, '};') // end of generated class }, '}') // end of aspect From b61787f1793236c076da4c8eb19d336e6943348d Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Fri, 4 Oct 2024 12:14:53 +0200 Subject: [PATCH 2/6] test: add test for new 'elements' property --- test/unit/files/elements/model.cds | 19 +++++++++++++++++++ test/unit/files/elements/model.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 test/unit/files/elements/model.cds create mode 100644 test/unit/files/elements/model.ts diff --git a/test/unit/files/elements/model.cds b/test/unit/files/elements/model.cds new file mode 100644 index 00000000..f3861866 --- /dev/null +++ b/test/unit/files/elements/model.cds @@ -0,0 +1,19 @@ +namespace elements_test; + +aspect A1 { + A_f1 : String; + A_f2 : String; +} + +entity E1 { + key ID: Integer; + a : String; + b : Integer; +} + +entity E2 : A1 { + key ID: String; + d: Integer; +} + +entity P1 as projection on E2; \ No newline at end of file diff --git a/test/unit/files/elements/model.ts b/test/unit/files/elements/model.ts new file mode 100644 index 00000000..4cbe575d --- /dev/null +++ b/test/unit/files/elements/model.ts @@ -0,0 +1,26 @@ +import cds from '@sap/cds' +import { E1, E2, P1 } from '#cds-models/elements_test' + +E1.elements +E1.elements.ID +E1.elements.a +E1.elements.b +// @ts-expect-error +E1.elements.nonExistent + +E2.elements +E2.elements.d // from entity E1 +E2.elements.A_f1 // from aspect A1 + +// check projection +P1.elements +P1.elements.ID +P1.elements.d +P1.elements.A_f1 +P1.elements.A_f2 + +// check LinkedDefinitions types +E1.elements.a.type +E1.elements.a.kind +E1.elements.a.items +E1.elements.a instanceof cds.builtin.classes.String \ No newline at end of file From 6555d5d4d6ca2e326f879ab425552294b2b5aad8 Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Fri, 4 Oct 2024 12:15:01 +0200 Subject: [PATCH 3/6] chore: adjust changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08243a9e..72e8f51e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). ## Version 0.28.0 - TBD +### Added +- Added a static `elements` property to all entities, which allows access to the `LinkedDefinitions` instance of an entity's elements ## Version 0.27.0 - 2024-10-02 ### Changed From 90a3d572e8ae2cc86f993eb6e7de9b11d685fdf2 Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Fri, 4 Oct 2024 12:33:09 +0200 Subject: [PATCH 4/6] refactor: use 'Required' instead of 'DeepRequired' --- lib/components/basedefs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/basedefs.js b/lib/components/basedefs.js index b71e7309..c7100792 100644 --- a/lib/components/basedefs.js +++ b/lib/components/basedefs.js @@ -15,7 +15,7 @@ const baseDefinitions = new SourceFile('_') baseDefinitions.addPreamble(` import { entity } from '@sap/cds' -export type ElementsOf = {[name in keyof DeepRequired]: entity["elements"][string]} +export type ElementsOf = {[name in keyof Required]: entity["elements"][string]} export namespace Association { export type to = T; From 301390f87192b6e27c171cc0471377867192a930 Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Fri, 4 Oct 2024 12:38:56 +0200 Subject: [PATCH 5/6] style: add missing whitespace --- lib/visitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/visitor.js b/lib/visitor.js index c379db3c..684f1899 100644 --- a/lib/visitor.js +++ b/lib/visitor.js @@ -167,7 +167,7 @@ class Visitor { * @param {string} clean - the clean name of the entity */ #printStaticElements(buffer, clean) { - buffer.add(`declare static readonly elements:${createElementsOf(clean)}`) + buffer.add(`declare static readonly elements: ${createElementsOf(clean)}`) } /** From f5cef92ee75978815371728f44f36f2039c4cfe6 Mon Sep 17 00:00:00 2001 From: Ludwig Stockbauer-Muhr Date: Mon, 14 Oct 2024 11:42:18 +0200 Subject: [PATCH 6/6] refactor: simplify used type for elements --- lib/components/basedefs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/basedefs.js b/lib/components/basedefs.js index c7100792..482baa72 100644 --- a/lib/components/basedefs.js +++ b/lib/components/basedefs.js @@ -13,9 +13,9 @@ const timeRegex = '`${number}${number}:${number}${number}:${number}${number}`' const baseDefinitions = new SourceFile('_') // FIXME: this should be a library someday baseDefinitions.addPreamble(` -import { entity } from '@sap/cds' +import { type } from '@sap/cds' -export type ElementsOf = {[name in keyof Required]: entity["elements"][string]} +export type ElementsOf = {[name in keyof Required]: type } export namespace Association { export type to = T;