@sap/cds 6.7.2 → 6.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/README.md +1 -0
  3. package/_i18n/i18n.properties +9 -6
  4. package/_i18n/i18n_ar.properties +6 -6
  5. package/_i18n/i18n_cs.properties +6 -6
  6. package/_i18n/i18n_da.properties +6 -6
  7. package/_i18n/i18n_de.properties +6 -6
  8. package/_i18n/i18n_en.properties +6 -6
  9. package/_i18n/i18n_es.properties +6 -6
  10. package/_i18n/i18n_fi.properties +6 -6
  11. package/_i18n/i18n_fr.properties +6 -6
  12. package/_i18n/i18n_hu.properties +6 -6
  13. package/_i18n/i18n_it.properties +6 -6
  14. package/_i18n/i18n_ja.properties +6 -6
  15. package/_i18n/i18n_ko.properties +6 -6
  16. package/_i18n/i18n_ms.properties +6 -6
  17. package/_i18n/i18n_nl.properties +6 -6
  18. package/_i18n/i18n_no.properties +6 -6
  19. package/_i18n/i18n_pl.properties +6 -6
  20. package/_i18n/i18n_pt.properties +6 -6
  21. package/_i18n/i18n_ro.properties +6 -6
  22. package/_i18n/i18n_ru.properties +6 -6
  23. package/_i18n/i18n_sv.properties +6 -6
  24. package/_i18n/i18n_th.properties +6 -6
  25. package/_i18n/i18n_tr.properties +8 -8
  26. package/_i18n/i18n_zh_CN.properties +3 -3
  27. package/_i18n/i18n_zh_TW.properties +6 -6
  28. package/apis/core.d.ts +30 -31
  29. package/apis/csn.d.ts +1 -1
  30. package/apis/ql.d.ts +69 -39
  31. package/apis/serve.d.ts +4 -3
  32. package/apis/services.d.ts +20 -7
  33. package/bin/build/buildTaskEngine.js +1 -1
  34. package/bin/build/index.js +1 -1
  35. package/bin/build/provider/buildTaskProviderInternal.js +9 -6
  36. package/bin/build/provider/hana/index.js +11 -4
  37. package/bin/build/provider/mtx-extension/index.js +13 -1
  38. package/bin/build/provider/mtx-sidecar/index.js +3 -3
  39. package/bin/build/provider/nodejs/index.js +23 -0
  40. package/bin/plugins.js +2 -1
  41. package/bin/version.js +3 -2
  42. package/common.cds +3 -2
  43. package/lib/auth/index.js +3 -0
  44. package/lib/auth/mocked-users.js +13 -0
  45. package/lib/compile/etc/_localized.js +3 -0
  46. package/lib/compile/for/lean_drafts.js +0 -1
  47. package/lib/core/entities.js +7 -3
  48. package/lib/dbs/cds-deploy.js +36 -12
  49. package/lib/env/cds-env.js +47 -14
  50. package/lib/env/cds-requires.js +16 -7
  51. package/lib/env/defaults.js +2 -2
  52. package/lib/env/schemas/cds-rc.json +1 -8
  53. package/lib/index.js +1 -1
  54. package/lib/ql/STREAM.js +89 -0
  55. package/lib/ql/cds-ql.js +2 -1
  56. package/lib/req/request.js +6 -2
  57. package/lib/req/user.js +1 -1
  58. package/lib/srv/middlewares/index.js +9 -7
  59. package/lib/srv/middlewares/trace.js +6 -5
  60. package/lib/srv/srv-api.js +1 -0
  61. package/lib/utils/cds-utils.js +1 -1
  62. package/lib/utils/tar.js +30 -31
  63. package/libx/_runtime/audit/Service.js +96 -37
  64. package/libx/_runtime/audit/generic/personal/utils.js +26 -13
  65. package/libx/_runtime/audit/utils/v2.js +21 -22
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +2 -0
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -3
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
  71. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/batch/BatchProcessor.js +2 -0
  72. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -1
  73. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +10 -3
  74. package/libx/_runtime/cds-services/services/Service.js +2 -7
  75. package/libx/_runtime/cds-services/services/utils/differ.js +1 -1
  76. package/libx/_runtime/cds-services/util/assert.js +28 -5
  77. package/libx/_runtime/common/aspects/any.js +4 -1
  78. package/libx/_runtime/common/generic/auth/utils.js +30 -41
  79. package/libx/_runtime/common/generic/crud.js +1 -1
  80. package/libx/_runtime/common/i18n/messages.properties +1 -1
  81. package/libx/_runtime/common/utils/generateOnCond.js +18 -22
  82. package/libx/_runtime/db/expand/expandCQNToJoin.js +49 -41
  83. package/libx/_runtime/db/expand/rawToExpanded.js +3 -5
  84. package/libx/_runtime/db/generic/rewrite.js +3 -0
  85. package/libx/_runtime/db/utils/generateAliases.js +1 -1
  86. package/libx/_runtime/fiori/generic/activate.js +1 -1
  87. package/libx/_runtime/fiori/generic/before.js +18 -19
  88. package/libx/_runtime/fiori/generic/prepare.js +1 -1
  89. package/libx/_runtime/fiori/generic/read.js +1 -1
  90. package/libx/_runtime/fiori/lean-draft.js +87 -53
  91. package/libx/_runtime/fiori/utils/handler.js +0 -6
  92. package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +1 -1
  93. package/libx/_runtime/hana/customBuilder/CustomReferenceBuilder.js +2 -1
  94. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +0 -5
  95. package/libx/_runtime/hana/execute.js +18 -11
  96. package/libx/_runtime/hana/pool.js +26 -18
  97. package/libx/_runtime/hana/search2Contains.js +1 -1
  98. package/libx/_runtime/hana/search2cqn4sql.js +26 -18
  99. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +23 -16
  100. package/libx/_runtime/messaging/outbox/utils.js +6 -1
  101. package/libx/_runtime/remote/Service.js +83 -48
  102. package/libx/_runtime/remote/utils/client.js +17 -19
  103. package/libx/_runtime/sqlite/execute.js +2 -0
  104. package/libx/rest/middleware/read.js +2 -1
  105. package/libx/rest/middleware/update.js +1 -1
  106. package/package.json +1 -1
package/apis/core.d.ts CHANGED
@@ -2,33 +2,9 @@
2
2
  import { ReflectedModel, LinkedModel, LinkedDefinition } from './reflect'
3
3
  import { CSN as csn, Definition } from './csn'
4
4
 
5
- type UserInput = string | { id: string; attr: Record<string, string>; roles: Record<string, string> } | User
6
-
7
- export class User {
8
- constructor(obj?: UserInput)
9
- id: string
10
- /**
11
- * @deprecated Use https://cap.cloud.sap/docs/node.js/events#locale instead
12
- */
13
- locale: string
14
- /**
15
- * @deprecated Use https://cap.cloud.sap/docs/node.js/events#tenant instead
16
- */
17
- tenant: string | undefined
18
- attr: Record<string, string>
19
- roles: Record<string, string>
20
- static Privileged: typeof Privileged
21
- is(role: string): boolean
22
- }
23
- /**
24
- * Subclass for executing code with superuser privileges.
25
- */
26
- declare class Privileged extends User {
27
- constructor()
28
- is(): boolean
29
- }
30
5
 
31
6
  export = cds
7
+
32
8
  declare class cds {
33
9
  /**
34
10
  * Turns the given plain CSN model into a linked model
@@ -46,7 +22,7 @@ declare class cds {
46
22
  * Represents the user in a given context.
47
23
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/authentication#cds-user)
48
24
  */
49
- User: typeof User
25
+ User: typeof cds.User
50
26
 
51
27
  // infer (query : cqn, model : csn) : LinkedDefinition
52
28
 
@@ -87,11 +63,6 @@ declare class cds {
87
63
  */
88
64
  entity: Definition
89
65
 
90
- struct: Definition
91
- array: Definition
92
- context: Definition
93
- service: Definition
94
-
95
66
  /**
96
67
  * Add aspects to a given object, for example:
97
68
  *
@@ -134,3 +105,31 @@ declare class cds {
134
105
  lazified: <T>(target: T) => T
135
106
  }
136
107
  // & typeof import ('../lib/index')
108
+
109
+ declare namespace cds {
110
+ type UserInput = string | { id: string; attr: Record<string, string>; roles: Record<string, string> } | User
111
+
112
+ export class User {
113
+ constructor(obj?: UserInput)
114
+ id: string
115
+ /**
116
+ * @deprecated Use https://cap.cloud.sap/docs/node.js/events#locale instead
117
+ */
118
+ locale: string
119
+ /**
120
+ * @deprecated Use https://cap.cloud.sap/docs/node.js/events#tenant instead
121
+ */
122
+ tenant: string | undefined
123
+ attr: Record<string, string>
124
+ roles: Record<string, string>
125
+ static Privileged: typeof Privileged
126
+ is(role: string): boolean
127
+ }
128
+ /**
129
+ * Subclass for executing code with superuser privileges.
130
+ */
131
+ class Privileged extends User {
132
+ constructor()
133
+ is(): boolean
134
+ }
135
+ }
package/apis/csn.d.ts CHANGED
@@ -24,7 +24,7 @@ export interface DefinitionRegistry {
24
24
  type: type,
25
25
  struct: struct,
26
26
  entity: entity,
27
- Association: Association,
27
+ Association: Association
28
28
  }
29
29
  export type Definition = DefinitionRegistry[keyof DefinitionRegistry];
30
30
 
package/apis/ql.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Definition} from "./csn"
1
+ import { Definition } from "./csn"
2
2
  import * as CQN from "./cqn"
3
3
  import { Constructable, ArrayConstructable, SingularType } from "./internal/inference"
4
4
 
@@ -14,59 +14,89 @@ export class cds_ql {
14
14
 
15
15
  export type PK = number | string | object
16
16
 
17
- interface QLExtensions {
18
- as: (alias: string) => void
19
- }
20
17
 
21
- // to support path expressions: https://pages.github.tools.sap/cap/docs/java/query-api#path-expressions
22
- // `Book.title` == `Proxy<Book>.title()`
23
-
24
- /**
25
- * This is a marker interface to determine if a property supports subselecting. That is:
26
- *
27
- * [1] SELECT.from(Books, b => b.author)
28
- * means: "select all books and project each book's author"
29
- * [2] SELECT.from(Books, b => b.author(a => a.ID))
30
- * means: "select all books, subselect each book's author's ID
31
- *
32
- */
33
- type Subqueryable <T> = ((fn:((a:T)=>any)|"*") => T)
18
+ type Primitive = string | number | boolean | Date
34
19
 
20
+ // don't wrap QLExtensions in more QLExtensions (indirection to work around recursive definition)
21
+ type QLExtensions<T> = T extends QLExtensions_<any> ? T : QLExtensions_<T>
35
22
 
36
23
  /**
37
- * Wrap every attribute into a proxy that supports operations
38
- * in addition to its regular capabilities.
39
- */
40
- type ProxiedAttributes<T> = {
41
- [Key in keyof T]: Proxy<T[Key]> //& QLExtensions
24
+ * QLExtensions are properties that are attached to entities in CQL contexts.
25
+ * They are passed down to all properties recursively.
26
+ */
27
+ type QLExtensions_<T> = {
28
+ [Key in keyof T]: QLExtensions<T[Key]>
42
29
  } & {
43
30
  /**
44
- * Accesses any nested attribute based on a path:
31
+ * Alias for this attribute.
32
+ */
33
+ as: (alias: string) => void
34
+
35
+ /**
36
+ * Accesses any nested attribute based on a [path](https://cap.cloud.sap/cap/docs/java/query-api#path-expressions):
45
37
  * `X.get('a.b.c.d')`. Note that you will not receive
46
38
  * proper typing after this call.
47
39
  * To still have access to typed results, use
48
40
  * `X.a().b().c().d()` instead.
49
41
  */
50
- get: (path: string) => any // Proxy<unknown>
51
- } & QLExtensions
42
+ get: (path: string) => any
43
+
44
+ // have to exclude undefined from the type, or we'd end up with a distribution of Subqueryable
45
+ // over T and undefined, which gives us zero code completion within the callable.
46
+ } & Subqueryable<Exclude<T, undefined>>
52
47
 
53
48
  /**
54
- * Entrance type for Proxies. The conditional makes sure that we don't
55
- * wrap properties into multiple layers of proxy.
56
- * Proxy_ was required as we can't circularily reference a type during its definition.
57
- * So `Proxy<T> = T extends Proxy<any> ...` was not possible.
49
+ * Adds the ability for subqueries to structured properties.
50
+ * The final result of each subquery will be the property itself:
51
+ * `Book.title` == `Subqueryable<Book>.title()`
58
52
  */
59
- export type Proxy<T> = T extends Proxy_<any> ? T : Proxy_<T>
53
+ type Subqueryable<T> =
54
+ T extends Primitive ? {}
55
+ // composition of many/ association to many
56
+ : T extends readonly unknown[] ? {
57
+ /**
58
+ * @example
59
+ * ```js
60
+ * SELECT.from(Books, b => b.author)
61
+ * ```
62
+ * means: "select all books and project each book's author"
63
+ *
64
+ * whereas
65
+ * ```js
66
+ * SELECT.from(Books, b => b.author(a => a.ID))
67
+ * ```
68
+ * means: "select all books, subselect each book's author's ID
69
+ *
70
+ * Note that you do not need to return anything from these subqueries.
71
+ */
72
+ (fn: ((a:QLExtensions<T[number]>) => any) | '*'): T[number]
73
+ }
74
+ // composition of one/ association to one
75
+ : {
76
+ /**
77
+ * @example
78
+ * ```js
79
+ * SELECT.from(Books, b => b.author)
80
+ * ```
81
+ * means: "select all books and project each book's author"
82
+ *
83
+ * whereas
84
+ * ```js
85
+ * SELECT.from(Books, b => b.author(a => a.ID))
86
+ * ```
87
+ * means: "select all books, subselect each book's author's ID
88
+ *
89
+ * Note that you do not need to return anything from these subqueries.
90
+ */
91
+ (fn: ((a:QLExtensions<T>) => any) | '*'): T
92
+ }
93
+ ;
60
94
 
61
- export type Proxy_<T> = (T extends Subqueryable<infer U>
62
- ? (Omit<T, ""> & Subqueryable<Proxy_<U>>) // drop ((x: T) => T) in favour of (x: Proxy<T>) => Proxy<T>)
63
- : (ProxiedAttributes<T>))
64
- & QLExtensions
65
95
 
66
96
  // Alias for projections
67
97
  // https://cap.cloud.sap/docs/node.js/cds-ql?q=projection#projection-functions
68
98
  //export type Projection<T> = (e:T)=>void
69
- export type Projection<T> = (e:Proxy<T extends ArrayConstructable ? SingularType<T> : T>)=>void
99
+ export type Projection<T> = (e:QLExtensions<T extends ArrayConstructable ? SingularType<T> : T>)=>void
70
100
  // Type for query pieces that can either be chained to build more complex queries or
71
101
  // awaited to materialise the result:
72
102
  // `Awaitable<SELECT<Book>, Book> = SELECT<Book> & Promise<Book>`
@@ -161,11 +191,11 @@ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
161
191
  &
162
192
  // calling with class
163
193
  (<T extends ArrayConstructable<any>>
164
- (entityType: T, projection?: Projection<Proxy<SingularType<T>>>)
194
+ (entityType: T, projection?: Projection<QLExtensions<SingularType<T>>>)
165
195
  => Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
166
196
  &
167
197
  (<T extends ArrayConstructable<any>>
168
- (entityType: T, primaryKey : PK, projection?: Projection<Proxy<SingularType<T>>>)
198
+ (entityType: T, primaryKey : PK, projection?: Projection<QLExtensions<SingularType<T>>>)
169
199
  => Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
170
200
 
171
201
  & ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
@@ -180,12 +210,12 @@ type SELECT_from =
180
210
  &
181
211
  // calling with class
182
212
  (<T extends ArrayConstructable<any>>
183
- (entityType: T, projection?: Projection<Proxy<SingularType<T>>>)
213
+ (entityType: T, projection?: Projection<QLExtensions<SingularType<T>>>)
184
214
  => Awaitable<SELECT<T>, InstanceType<T>>)
185
215
  &
186
216
  (<T extends ArrayConstructable<any>>
187
217
  (entityType: T, primaryKey : PK, projection?: Projection<SingularType<T>>)
188
- => Awaitable<SELECT<T>, InstanceType<T>>)
218
+ => Awaitable<SELECT<SingularType<T>>, InstanceType<SingularType<T>>>) // when specifying a key, we expect a single element as result
189
219
  // calling with definition
190
220
  & ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
191
221
  // calling with concrete list
package/apis/serve.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Service, ServiceImpl } from "./services"
2
2
  import { LinkedDefinition } from "./reflect"
3
3
  import { csn } from "./csn"
4
4
  import * as http from "http"
5
+ import { Application } from "express"
5
6
 
6
7
  // export const Service : _Service
7
8
 
@@ -9,7 +10,7 @@ interface _fluent {
9
10
  from (model : string | csn) : this
10
11
  to (protocol: string) : this
11
12
  at (path: string) : this
12
- in <T = any> (app: T) : this
13
+ in (app: Application) : this
13
14
  with (impl: ServiceImpl | string) : this
14
15
  // (req,res) : void
15
16
  }
@@ -40,8 +41,8 @@ declare class cds_serve {
40
41
  * express application has been constructed but no middlewares or routes
41
42
  * added yet.
42
43
  */
43
- on <T = any> (event : 'bootstrap', listener : (app : T) => void) : this
44
- once <T = any>(event : 'bootstrap', listener : (app : T) => void) : this
44
+ on (event : 'bootstrap', listener : (app : Application) => void) : this
45
+ once (event : 'bootstrap', listener : (app : Application) => void) : this
45
46
 
46
47
  /**
47
48
  * Emitted for each service served by cds.serve().
@@ -1,9 +1,10 @@
1
1
  import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery, UPSERT } from './ql'
2
- import { Projection, Proxy, Awaitable } from './ql'
3
- import { ArrayConstructable, Unwrap, SingularType, Constructable } from './internal/inference'
2
+ import { Awaitable } from './ql'
3
+ import { ArrayConstructable, Constructable } from './internal/inference'
4
4
  import { LinkedModel, Definition, Definitions } from './reflect'
5
- import { csn, type } from './csn'
5
+ import { csn } from './csn'
6
6
  import { User } from './core'
7
+ import * as express from "express"
7
8
  import { ref } from './cqn'
8
9
  // import { Service } from './cds'
9
10
 
@@ -90,6 +91,18 @@ export class QueryAPI {
90
91
  * @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=spawn#cds-spawn)
91
92
  */
92
93
  spawn(options: Options, fn: (tx: Transaction) => {}): SpawnEventEmitter
94
+
95
+ /**
96
+ * @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=cds.context#cds-context
97
+ */
98
+ context: ContextProperties
99
+ }
100
+
101
+ type ContextProperties = {
102
+ id?: string
103
+ http?: {req: express.Request, res: express.Response}
104
+ tenant? : string,
105
+ user? : User | string
93
106
  }
94
107
 
95
108
  /**
@@ -98,11 +111,11 @@ export class QueryAPI {
98
111
  */
99
112
  export class Service extends QueryAPI {
100
113
  constructor(
101
- name: String,
114
+ name: string,
102
115
  model: csn,
103
116
  options: {
104
- kind: String
105
- impl: String | ServiceImpl
117
+ kind: string
118
+ impl: string | ServiceImpl
106
119
  }
107
120
  )
108
121
 
@@ -345,7 +358,7 @@ interface SpawnEvents {
345
358
  'done': () => void
346
359
  }
347
360
 
348
- class SpawnEventEmitter {
361
+ declare class SpawnEventEmitter {
349
362
  on<U extends keyof SpawnEvents>(
350
363
  event: U, listener: SpawnEvents[U]
351
364
  ): this;
@@ -44,7 +44,7 @@ class BuildTaskEngine {
44
44
  // validate required @sap namespace models - log only
45
45
  const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
46
46
  if (unresolved.length > 0) {
47
- messages.push(new BuildMessage(`Required CDS service models [${unresolved.join(', ')}] cannot be resolved. Make sure to install the missing npm modules.`))
47
+ messages.push(new BuildMessage(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Make sure up-to-date versions of the missing modules are installed.`))
48
48
  }
49
49
 
50
50
  // create build task handlers
@@ -29,7 +29,7 @@ async function build(options = {}) {
29
29
  cds.root = projectPath // REVISIT: not good / fragile
30
30
  }
31
31
  const logger = options.logger || cds.log(LOG_MODULE_NAMES)
32
- const buildOptions = _mergeCliOptions({ root: projectPath }, options)
32
+ const buildOptions = _mergeCliOptions({ root: cds.root }, options)
33
33
  const buildTaskFactory = new BuildTaskFactory(logger)
34
34
  const buildTaskEngine = new BuildTaskEngine(logger)
35
35
  let buildResult
@@ -178,7 +178,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
178
178
  sidecarEnv = cds.env.for("cds", sidecarPath)
179
179
  }
180
180
  if (
181
- cds.env.profiles.includes('with-mtx-sidecar') || // new presets
181
+ cds.env.profiles?.includes('with-mtx-sidecar') || // new presets
182
182
  cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
183
183
  ) {
184
184
  this.logger.debug("MTX app with sidecar")
@@ -186,17 +186,21 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
186
186
  throw new BuildError(`MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration required if the folder is named differently.`)
187
187
  }
188
188
  if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
189
+ // correct profile might have been set, but couldn't be resolved
190
+ if (sidecarEnv.profiles?.includes('mtx-sidecar')) {
191
+ throw new BuildError('MTX configuration cannot be resolved. Make sure an up-to-date version of @sap/cds-mtxs is installed.')
192
+ }
189
193
  throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
190
194
  }
191
195
  return { for: BUILD_TASK_MTX_SIDECAR }
192
196
  }
193
- if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
194
- throw new BuildError(`MTX sidecar configuration requires profile 'with-mtx-sidecar' or custom build task configuration.`)
197
+ if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
198
+ throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
195
199
  }
196
200
 
197
- if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
201
+ if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
198
202
  // cds build is executed in sidecar folder
199
- throw new BuildError(`Invalid working directory ${projectPath}. Make sure to execute 'cds build' in the CAP project root directory.`)
203
+ throw new BuildError(`Seems that you are executing 'cds build' in the 'mtx/sidecar' folder. Execute 'cds build' in the project root folder instead.`)
200
204
  }
201
205
 
202
206
  // without sidecar
@@ -208,7 +212,6 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
208
212
  }
209
213
  }
210
214
  }
211
-
212
215
  if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
213
216
  if (cds.env.requires.multitenancy) {
214
217
  this.logger.debug("Nodejs Classic MTX app without sidecar")
@@ -8,7 +8,7 @@ const { BuildError, relativePaths } = require('../../util')
8
8
  const CSV = require('../../csv-reader')
9
9
  const to_hdbmigration = require('./2migration')
10
10
  const to_hdbtabledata = require('./2tabledata')
11
- const { ERROR } = require('../../buildTaskHandler')
11
+ const { ERROR, WARNING } = require('../../buildTaskHandler')
12
12
 
13
13
  const DEFAULT_COMPILE_DEST_FOLDER = path.normalize("src/gen")
14
14
 
@@ -378,7 +378,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
378
378
  }
379
379
  }
380
380
  if (Object.keys(content['file_suffixes']).length !== plugins.size) {
381
- this.pushMessage(`'HANA plugin not found for file suffix [${Array.from(plugins).join(',')}]`)
381
+ this.pushMessage(`'HANA database plugin not found for file suffix [${Array.from(plugins).join(',')}]`)
382
382
  }
383
383
  // TODO - Be on the save side for now - go for the content use case later on if this works as expected.
384
384
  if (cds.env.hana['deploy-format'] === 'hdbtable') {
@@ -433,13 +433,20 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
433
433
  async _readTypesFromUndeployJson() {
434
434
  const result = new Set()
435
435
  const file = path.join(this.task.src, "undeploy.json")
436
+
436
437
  if (fs.existsSync(file)) {
437
438
  const undeployList = JSON.parse((await fs.promises.readFile(file)).toString(), 'utf-8')
438
439
  if (Array.isArray(undeployList)) {
440
+ const hdiconfig = await HanaModuleBuilder._readTemplateAsJson('.hdiconfig-haas')
441
+ const keys = new Set(Object.keys(hdiconfig['file_suffixes']).map(key => '.' + key))
439
442
  undeployList.forEach(entry => {
440
443
  const extName = path.extname(entry)
441
- if (extName) {
442
- result.add(extName)
444
+ if (extName && !result.has(extName)) {
445
+ if (keys.has(extName)) {
446
+ result.add(extName)
447
+ } else {
448
+ this.pushMessage(`Ignoring invalid entry '${entry}' in undeploy.json file`, WARNING)
449
+ }
443
450
  }
444
451
  })
445
452
  }
@@ -32,7 +32,7 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
32
32
  if (model) {
33
33
  // extension CSN using parsed format
34
34
  const options = { ...this.options(), flavor: 'parsed' }
35
- const extModel = await cds.load(this.resolveModel(), options)
35
+ const extModel = await cds.load(this._resolveExtensionFiles(model), options)
36
36
  if (extModel.requires) {
37
37
  extModel.requires.length = 0
38
38
  }
@@ -79,6 +79,18 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
79
79
  await new ResourcesTarBuilder(this).writeTarFile(path.join(this.task.dest, 'extension.tgz'), destExt)
80
80
  }
81
81
 
82
+ _resolveExtensionFiles(model) {
83
+ const node_modules = path.join(this.task.src, 'node_modules')
84
+ const paths = model['$sources'].reduce((acc, file) => {
85
+ if (file.startsWith(this.task.src) && !file.startsWith(node_modules)) {
86
+ acc.push(file)
87
+ }
88
+ return acc
89
+ }, [])
90
+
91
+ return paths
92
+ }
93
+
82
94
  _lintExtModel(extModel, model) {
83
95
  const linter = this._linter()
84
96
  if (!linter) {
@@ -60,7 +60,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
60
60
  async _buildMainApp(sidecarEnv) {
61
61
  let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
62
62
  if (!main) {
63
- throw new BuildError('Invalid MTX sidecar configuration - "cds/requires/cds.xt.ModelProviderService/root" missing.')
63
+ throw new BuildError(`Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured and an up-to-date version of @sap/cds-mtxs is installed.`)
64
64
  }
65
65
  const profiles = cds.env.profiles || []
66
66
 
@@ -101,7 +101,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
101
101
  const modelFilePaths = cds.resolve(modelPaths)
102
102
 
103
103
  if (!modelFilePaths || modelFilePaths.length === 0) {
104
- throw new BuildError("No CDS service models found in MTX sidecar. Make sure required npm modules are installed.")
104
+ throw new BuildError("No CDS models found in MTX sidecar. Make sure up-to-date versions of the required npm modules are installed.")
105
105
  }
106
106
  this._logger._debug && this._logger.debug(`sidecar model: ${relativePaths(this.buildOptions.root, modelFilePaths).join(", ")}`)
107
107
 
@@ -109,7 +109,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
109
109
  const unresolved = resolveRequiredSapModels(modelPaths)
110
110
  if (unresolved.length > 0) {
111
111
  // log error, but don't fail
112
- this.pushMessage(`CDS service models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure to install the missing npm modules.`, ERROR)
112
+ throw new BuildError(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure up-to-date versions of the corresponding npm modules are installed.`, ERROR)
113
113
  }
114
114
 
115
115
  // synchronous compilation
@@ -65,10 +65,33 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
65
65
  if (this.isStagingBuild() && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
66
66
  const srcSrv = this.task.src === this.buildOptions.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
67
67
  await this._copyNativeContent(this.buildOptions.root, srcSrv, destRoot, destSrv)
68
+ await this._fixCapireSamplesDeps(destRoot)
68
69
  }
69
70
  return this._result
70
71
  }
71
72
 
73
+ // REVISIT: fix for capire samples - needs to be reworked
74
+ // Instead of adding relative paths we may better delete the entire entry as relative paths
75
+ // cannot be resolved in cloud deployments anyway.
76
+ // Support for HANA native artifacts defined in sub-modules required
77
+ async _fixCapireSamplesDeps(dest) {
78
+ const packageJson = path.join(dest, 'package.json')
79
+ try {
80
+ const content = require(packageJson)
81
+ const { dependencies } = content
82
+ let changed
83
+ for (let dependency in dependencies) {
84
+ if (dependency.startsWith('@capire/')) {
85
+ dependencies[dependency] = path.relative(dest, path.dirname(require.resolve(dependency + '/package.json')))
86
+ changed = true
87
+ }
88
+ }
89
+ if (changed) {
90
+ return this.write(JSON.stringify(content, null, ' ')).to(packageJson)
91
+ }
92
+ } catch (e) {/*ignore*/ }
93
+ }
94
+
72
95
  async clean() {
73
96
  // staging build content is deleted by BuildTaskEngine
74
97
  if (this.buildOptions.target === this.buildOptions.root) {
package/bin/plugins.js CHANGED
@@ -14,7 +14,8 @@ module.exports = async function load_plugins (log = console.log) {
14
14
  }
15
15
  } else
16
16
  try {
17
- const { dependencies } = require(cds.root + '/package.json')
17
+ const pkg = require(cds.root + '/package.json')
18
+ const dependencies = { ...pkg.dependencies, ...(process.env.NODE_ENV !== 'production' && pkg.devDependencies) }
18
19
  for (let each in dependencies) {
19
20
  plugins.push(_load_plugin(each + '/cds-plugin'))
20
21
  }
package/bin/version.js CHANGED
@@ -103,7 +103,7 @@ function _markdown (versions) {
103
103
  Object.assign (pkg, require (resolve('package.json')))
104
104
  } catch (e) {/* ignored */}
105
105
  console.log ('|', pkg.name, '|', pkg.repository.url || pkg.repository, '|')
106
- console.log ('|:------------------ | ----------- |')
106
+ console.log ('|:---------------------- | ----------- |')
107
107
  if (require('../lib').env['project-nature'] === 'nodejs') {
108
108
  console.log ('|', v('Node.js'), '|')
109
109
  console.log ('|', v('@sap/cds'), '|')
@@ -113,10 +113,11 @@ function _markdown (versions) {
113
113
  }
114
114
  console.log ('|', v('@sap/cds-compiler'), '|')
115
115
  console.log ('|', v('@sap/cds-dk'), '|')
116
+ console.log ('|', v('@sap/cds-dk (global)'), '|')
116
117
  console.log ('|', v('@sap/eslint-plugin-cds'), '|')
117
118
  function v (component) {
118
119
  const version = versions [component] || '_version_'
119
- return (component + ' ').slice(0,18)
120
+ return (component + ' ').slice(0,22)
120
121
  +' | '+ (version + ' ').slice(0,11)
121
122
  }
122
123
  console.log()
package/common.cds CHANGED
@@ -56,8 +56,9 @@ context sap.common {
56
56
  * See https://cap.cloud.sap/docs/cds/common#entity-sapcommoncurrencies
57
57
  */
58
58
  entity Currencies : CodeList {
59
- key code : String(3) @(title : '{i18n>CurrencyCode}');
60
- symbol : String(5) @(title : '{i18n>CurrencySymbol}');
59
+ key code : String(3) @(title : '{i18n>CurrencyCode}');
60
+ symbol : String(5) @(title : '{i18n>CurrencySymbol}');
61
+ minorUnit : Int16 @(title : '{i18n>CurrencyMinorUnit}');
61
62
  }
62
63
 
63
64
  /**
package/lib/auth/index.js CHANGED
@@ -30,5 +30,8 @@ function auth_factory (options) {
30
30
  cds.log().info ('using auth strategy:', { kind, impl: local(impl) }, '\n')
31
31
  middleware = require(impl)
32
32
  }
33
+ if ((typeof middleware === 'function' && middleware.length === 3) || Array.isArray(middleware)) {
34
+ return middleware
35
+ }
33
36
  return middleware(o)
34
37
  }
@@ -10,6 +10,19 @@ class MockedUsers {
10
10
  if (typeof v === 'boolean') continue
11
11
  if (typeof v === 'string') v = { password:v }
12
12
  let id = _configured(v).id || k
13
+
14
+ // Only for mock users the pseudo roles are kept in the role list.
15
+ // In all other cases pseudo roles are filtered out.
16
+ if (v.roles) {
17
+ if (Array.isArray(v.roles)) {
18
+ if (v.roles.includes('system-user')) v._is_system = true
19
+ if (v.roles.includes('internal-user')) v._is_internal = true
20
+ } else {
21
+ if ('system-user' in v.roles) v._is_system = true
22
+ if ('internal-user' in v.roles) v._is_internal = true
23
+ }
24
+ } else v.roles = []
25
+
13
26
  let u = users[id] = new User ({ id, ...v })
14
27
  let fts = tenants[u.tenant]?.features
15
28
  if (fts && !u.features) u.features = fts
@@ -18,6 +18,8 @@ const _been_here = Symbol('is _localized')
18
18
  function unfold_ddl (ddl, csn, o={}) { // NOSONAR
19
19
  const _locales = _locales_4sql[o.dialect]; if (!_locales) return ddl
20
20
  const localized_views = ddl.filter (each => each.startsWith('CREATE VIEW localized_') || each.startsWith('DROP VIEW localized_'))
21
+ // REVISIT: analyze ddl statements. Problem is with schevo calling this function containing only drops
22
+ if (o.sqlDialect === 'sqlite' && o.betterSqliteSessionVariables) return ddl
21
23
  for (const localized_view of localized_views) {
22
24
  for (const locale of _locales) ddl.push (localized_view
23
25
  .replace (/localized_/g, `localized_${locale}_`)
@@ -59,6 +61,7 @@ function unfold_csn (m) { // NOSONAR
59
61
  if (_localized_entries !== false && _is_localized(d)) {
60
62
  _add_proxy4 (d,`localized.${each}`, x => pass2.push([x]))
61
63
  // if running on sqlite add additional localized.<locale>. views
64
+ if (_conf?.impl === '@cap-js/sqlite') continue
62
65
  if (_locales) for (const locale of _locales) {
63
66
  _add_proxy4 (d,`localized.${locale}.${each}`, x => pass2.push([x,locale]))
64
67
  }
@@ -128,7 +128,6 @@ module.exports = function cds_compile_for_lean_drafts(csn) {
128
128
  }
129
129
  draft.elements[each] = newEl
130
130
  }
131
- // TODO: Redirect associations to localized
132
131
  return draft
133
132
  }
134
133
  for (const name in csn.definitions) {