@sap/cds 6.2.3 → 6.3.1

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 (74) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/apis/connect.d.ts +1 -1
  3. package/apis/cqn.d.ts +1 -1
  4. package/apis/internal/inference.d.ts +14 -0
  5. package/apis/ql.d.ts +40 -36
  6. package/apis/services.d.ts +23 -6
  7. package/bin/build/buildTaskEngine.js +15 -12
  8. package/bin/build/buildTaskHandler.js +3 -3
  9. package/bin/build/constants.js +2 -0
  10. package/bin/build/provider/buildTaskHandlerEdmx.js +1 -1
  11. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +4 -3
  12. package/bin/build/provider/buildTaskHandlerInternal.js +2 -2
  13. package/bin/build/provider/java/index.js +2 -1
  14. package/bin/build/provider/mtx/index.js +2 -1
  15. package/bin/build/provider/mtx/resourcesTarBuilder.js +3 -2
  16. package/bin/build/provider/mtx-extension/index.js +2 -1
  17. package/bin/build/provider/mtx-sidecar/index.js +3 -1
  18. package/bin/build/util.js +2 -2
  19. package/bin/deploy/to-hana/cfUtil.js +46 -62
  20. package/lib/auth/index.js +2 -1
  21. package/lib/auth/jwt-auth.js +64 -3
  22. package/lib/auth/xsuaa-auth.js +2 -3
  23. package/lib/compile/cdsc.js +1 -0
  24. package/lib/compile/etc/_localized.js +1 -0
  25. package/lib/dbs/cds-deploy.js +2 -1
  26. package/lib/env/cds-env.js +14 -49
  27. package/lib/env/cds-requires.js +13 -7
  28. package/lib/env/defaults.js +4 -0
  29. package/lib/i18n/localize.js +11 -8
  30. package/lib/index.js +1 -1
  31. package/lib/log/cds-log.js +2 -2
  32. package/lib/log/format/cf.js +16 -0
  33. package/lib/log/format/kibana.js +15 -2
  34. package/lib/ql/INSERT.js +12 -11
  35. package/lib/ql/Query.js +14 -7
  36. package/lib/ql/UPSERT.js +1 -0
  37. package/lib/ql/Whereable.js +6 -2
  38. package/lib/ql/cds-ql.js +2 -4
  39. package/lib/req/request.js +2 -0
  40. package/lib/srv/bindings.js +1 -0
  41. package/lib/srv/middlewares/cds-context.js +1 -1
  42. package/lib/srv/srv-dispatch.js +1 -0
  43. package/lib/srv/srv-tx.js +3 -3
  44. package/lib/utils/cds-utils.js +75 -30
  45. package/lib/utils/inflect.js +24 -0
  46. package/libx/_runtime/auth/strategies/ias-auth.js +1 -1
  47. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +9 -1
  48. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +23 -6
  49. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -0
  50. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/validator/ValueValidator.js +27 -15
  51. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -1
  52. package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -10
  53. package/libx/_runtime/cds-services/services/utils/differ.js +6 -4
  54. package/libx/_runtime/common/composition/data.js +29 -40
  55. package/libx/_runtime/common/composition/update.js +6 -19
  56. package/libx/_runtime/common/generic/paging.js +1 -1
  57. package/libx/_runtime/common/utils/resolveView.js +7 -13
  58. package/libx/_runtime/db/utils/generateAliases.js +1 -0
  59. package/libx/_runtime/fiori/generic/before.js +5 -2
  60. package/libx/_runtime/fiori/generic/read.js +11 -4
  61. package/libx/_runtime/hana/execute.js +2 -2
  62. package/libx/_runtime/hana/search2Contains.js +3 -1
  63. package/libx/_runtime/hana/search2cqn4sql.js +1 -0
  64. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
  65. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +5 -2
  66. package/libx/_runtime/messaging/enterprise-messaging.js +7 -1
  67. package/libx/_runtime/messaging/file-based.js +1 -1
  68. package/libx/_runtime/messaging/message-queuing.js +5 -2
  69. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  70. package/libx/_runtime/messaging/service.js +5 -3
  71. package/libx/odata/cqn2odata.js +4 -1
  72. package/libx/odata/utils.js +8 -7
  73. package/libx/rest/RestAdapter.js +1 -4
  74. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,61 @@
4
4
  - The format is based on [Keep a Changelog](http://keepachangelog.com/).
5
5
  - This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ ## Version 6.3.1 - 2022-11-04
8
+
9
+ ### Fixed
10
+ - `cds build` no longer reports false positive validation errors for built-in MTX models like `@sap/cds/srv/mtx` or `@sap/cds-mtxs/srv/bootstrap`
11
+ - `cds deploy` handles empty result from `cf` call correctly
12
+ - `$search` fails on columns composed by a CQL expression that uses the SAP HANA `coalesce` predicate
13
+ - Draft ownership was erroneously checked for bound actions on active instances
14
+ - `cds watch/run/serve --with-mocks` no longer start randomly with missing mocked services. This could happen if previous runs crashed with errors and left bad state in the local service registry.
15
+
16
+ ## Version 6.3.0 - 2022-10-28
17
+
18
+ ### Added
19
+
20
+ - Additional type signatures for service methods in the query API
21
+ - In case of error in a batch request, the @Core.ContentID is added to the details of the error message
22
+ - Extensibility: Use i18n files from extensions in edmx calculation
23
+ - In messaging, you can listen to all messages in a queue by subscribing to `'*'`
24
+ - Improved Log formatting for Cloud Foundry
25
+ - In remote services: Correct OData type conversion when using an imported csn
26
+ - Types for `SELECT.forUpdate({wait})`
27
+
28
+ - `cds.ql` now provides a dedicated method `.alias()` to choose table aliases, e.g.:
29
+ ```js
30
+ SELECT.from(Authors).alias(a)
31
+ ```
32
+ > Note: unfortunately we can't use method `.as()` instead of `.alias()` for compatibility reasons
33
+
34
+ - `cds.ql` now supports constructing queries with `where exists` clauses, e.g.:
35
+ ```js
36
+ SELECT.from(Authors).where({exists:'books'})
37
+ SELECT.from(Authors).where({'not exists':'books'})
38
+ SELECT.from(Authors).alias('a').where({ exists: // or 'not exists'
39
+ SELECT.from(Books).where({author_ID:{ref:['a','ID']}})
40
+ })
41
+ ```
42
+ > Note: last query is equivalent to first
43
+ - `cds compile` and `cds deploy` now also support dialect `h2`
44
+ - New (easier) `jwt` and `xsuaa` authentication middleware for pluggable middlewares
45
+
46
+ ### Changed
47
+
48
+ - In `enterprise-messaging`, emitting CloudEvents messages sets the HTTP header `Content-Type: application/cloudevents+json`
49
+
50
+ ### Fixed
51
+
52
+ - Change signature of cqn `SELECT.limit.offset` and `SELECT.limit.rows` to `val` instead of `number`
53
+ - Parsing of store procedure SQL calls including the schema name. For example, `CALL "SCHEMA"."PROC"(?)` and `CALL SCHEMA.PROC(?)`
54
+ - Add property name in the error message on validation of the value
55
+ - Kibana and Cloud Foundry formatter: do not log cookie header value
56
+ - Missing SQL aliases for `$search` queries combined with `$orderBy` query option
57
+ - The return value of `cds.connect` is now correctly typed as a `Promise`
58
+ - `req.data` is no longer modified for remote services in the case of `odata-v2` inserts
59
+ - `cds.localize` no longer ignores i18n files defined within CDS model scope and outside project scope
60
+ - Don't modify query in `fioriGenericRead` handler
61
+
7
62
  ## Version 6.2.3 - 2022-10-21
8
63
 
9
64
  ### Fixed
@@ -88,6 +143,8 @@
88
143
  - `cds deploy` and `cds run/serve/watch` no longer print terminal escape sequences (`x1b...`) if they run non-interactively.
89
144
  - Some fields in entities like `path` generated invalid sql
90
145
 
146
+ ### Removed
147
+
91
148
  ## Version 6.1.3 - 2022-09-13
92
149
 
93
150
  ### Added
package/apis/connect.d.ts CHANGED
@@ -15,7 +15,7 @@ declare class cds {
15
15
  * Connects the primary datasource.
16
16
  * @see [capire](https://cap.cloud.sap/docs/node.js/api#cds-connect)
17
17
  */
18
- (options?: string | ConnectOptions) : typeof cds //> cds.connect(<options>)
18
+ (options?: string | ConnectOptions) : Promise<typeof cds> //> cds.connect(<options>)
19
19
  }
20
20
 
21
21
  /**
package/apis/cqn.d.ts CHANGED
@@ -11,7 +11,7 @@ export type SELECT = {SELECT:{
11
11
  having? : predicate
12
12
  groupBy? : expr[]
13
13
  orderBy? : ordering_term[]
14
- limit?: { rows:number, offset:number }
14
+ limit?: { rows:val, offset:val }
15
15
  }}
16
16
 
17
17
  export type INSERT = {INSERT:{
@@ -0,0 +1,14 @@
1
+ // Types in this file are not part of the API.
2
+ // They are merely meant as definitions that are used
3
+ // in several places within the API.
4
+
5
+ // any class (not value) of array to represent plural types used in cds-typer.
6
+ // Mainly used as pattern match for SingularType
7
+ //type ArrayConstructable = Constructable<Array<unknown>>
8
+ export interface ArrayConstructable<T = any[]> {
9
+ new(...args: any[]): T[]
10
+ }
11
+
12
+ // concrete singular type.
13
+ // `SingularType<Books>` == `Book`.
14
+ export type SingularType<T extends ArrayConstructable<T>> = InstanceType<T>[number]
package/apis/ql.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {Definition} from "./csn"
2
2
  import * as CQN from "./cqn"
3
+ import { ArrayConstructable, SingularType } from "./internal/inference"
3
4
 
4
5
  export type Query = CQN.Query
5
6
 
@@ -12,7 +13,7 @@ interface Constructable<T> {
12
13
  }
13
14
 
14
15
  export class cds_ql {
15
- ql:QL & ((context:object) => QL)
16
+ ql:QL<any> & ((context:object) => QL<any>)
16
17
  }
17
18
 
18
19
  export type PK = number | string | object
@@ -48,25 +49,14 @@ type Proxy_<T> = {
48
49
  get: (path: string) => any // Proxy<unknown>
49
50
  } & QLExtensions
50
51
 
51
- type Proxy<T> = (T extends Subqueryable<infer U>
52
+ export type Proxy<T> = (T extends Subqueryable<infer U>
52
53
  ? (Omit<T, ""> & Subqueryable<Proxy<U>>) // drop ((x: T) => T) in favour of (x: Proxy<T>) => Proxy<T>)
53
54
  : (Proxy_<T>))
54
55
  & QLExtensions
55
56
 
56
- // any class (not value) of array to represent plural types used in cds-typer.
57
- // Mainly used as pattern match for SingularType
58
- //type ArrayConstructable = Constructable<Array<unknown>>
59
- interface ArrayConstructable<T> {
60
- new(...args: any[]): T
61
- }
62
-
63
- // concrete singular type.
64
- // `SingularType<Books>` == `Book`.
65
- type SingularType<T extends ArrayConstructable<T>> = InstanceType<T>[number]
66
-
67
57
  // Alias for projections
68
58
  // https://cap.cloud.sap/docs/node.js/cds-ql?q=projection#projection-functions
69
- type Projection<T> = (e:T)=>void
59
+ export type Projection<T> = (e:T)=>void
70
60
 
71
61
  // Type for query pieces that can either be chained to build more complex queries or
72
62
  // awaited to materialise the result:
@@ -80,7 +70,7 @@ type Projection<T> = (e:T)=>void
80
70
  // `Awaitable<T> = T extends unknown<infer I> ? (T & Promise<I>) : never`
81
71
  // (at the time of writing, infering the first generic parameter of ANY type
82
72
  // does not seem to be possible.)
83
- type Awaitable<T, I> = T & Promise<I>
73
+ export type Awaitable<T, I> = T & Promise<I>
84
74
 
85
75
  // all the functionality of an instance of SELECT, but directly callable:
86
76
  // new SELECT(...).(...) == SELECT(...)
@@ -91,7 +81,7 @@ export type StaticSELECT<T> = typeof SELECT
91
81
  & SELECT_one // as it is not directly quantified, ...
92
82
  & SELECT_from // ...we should expect both a scalar and a list
93
83
 
94
- declare class QL {
84
+ declare class QL<T> {
95
85
  SELECT : StaticSELECT<T>
96
86
  INSERT : typeof INSERT
97
87
  & ((...entries:object[]) => INSERT<any>) & ((entries:object[]) => INSERT<any>)
@@ -107,7 +97,7 @@ declare class QL {
107
97
  // when run in strict mode.
108
98
  // This signature has to be added to a method as intersection type.
109
99
  // Defining overloads with it will override preceding signatures and the other way around.
110
- type TaggedTemplateQueryPart<T> = (strings: TemplateStringsArray, ...params?: unknown[]) => T
100
+ type TaggedTemplateQueryPart<T> = (strings: TemplateStringsArray, ...params: unknown[]) => T
111
101
 
112
102
  export class SELECT<T> extends ConstructedQuery {
113
103
  static one : SELECT_one & { from: SELECT_one }
@@ -134,19 +124,32 @@ export class SELECT<T> extends ConstructedQuery {
134
124
  & ((...expr : string[]) => this)
135
125
  limit: TaggedTemplateQueryPart<this>
136
126
  & ((rows : number, offset? : number) => this)
137
- forSharedLock () : this
138
- forUpdate () : this
127
+ forShareLock () : this
128
+ forUpdate ({wait}? : {wait?: number}) : this
129
+
130
+ // Not yet public
131
+ // fullJoin (other: string, as: string) : this
132
+ // leftJoin (other: string, as: string) : this
133
+ // rightJoin (other: string, as: string) : this
134
+ // innerJoin (other: string, as: string) : this
135
+ // join (other: string, as: string, kind?: string) : this
136
+ // on : TaggedTemplateQueryPart<this>
137
+ // & ((...expr : string[]) => this)
138
+ // & ((predicate:object) => this)
139
+
139
140
  SELECT : CQN.SELECT
140
141
  }
141
142
 
142
143
 
143
144
  type SELECT_one =
145
+ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
146
+ &
144
147
  // calling with class
145
- (<T extends ArrayConstructable>
148
+ (<T extends ArrayConstructable<any>>
146
149
  (entityType: T, projection?: Projection<Proxy<SingularType<T>>>)
147
150
  => Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
148
151
  &
149
- (<T extends ArrayConstructable>
152
+ (<T extends ArrayConstructable<any>>
150
153
  (entityType: T, primaryKey : PK, projection?: Projection<Proxy<SingularType<T>>>)
151
154
  => Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
152
155
 
@@ -158,14 +161,14 @@ type SELECT_one =
158
161
 
159
162
  type SELECT_from =
160
163
  // tagged template
161
- TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<unknown>>>
164
+ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
162
165
  &
163
166
  // calling with class
164
- (<T extends ArrayConstructable>
167
+ (<T extends ArrayConstructable<any>>
165
168
  (entityType: T, projection?: Projection<Proxy<SingularType<T>>>)
166
169
  => Awaitable<SELECT<T>, InstanceType<T>>)
167
170
  &
168
- (<T extends ArrayConstructable>
171
+ (<T extends ArrayConstructable<any>>
169
172
  (entityType: T, primaryKey : PK, projection?: Projection<SingularType<T>>)
170
173
  => Awaitable<SELECT<T>, InstanceType<T>>)
171
174
  // calling with definition
@@ -176,16 +179,15 @@ type SELECT_from =
176
179
 
177
180
 
178
181
  export class INSERT<T> extends ConstructedQuery {
179
- // cds-typer plural
180
- static into <T extends ArrayConstructable> (entity:T, entries? : object | object[]) : INSERT<SingularType<T>>
181
- into <T extends ArrayConstructable> (entity:T) : this
182
-
183
- static into: TaggedTemplateQueryPart<INSERT<T>>
184
- static into (entity : Definition | string, entries? : object | object[]) : INSERT<any>
185
- static into <T> (entity:Constructable<T>, entries? : object | object[]) : INSERT<T>
186
- static into <T> (entity:T, entries? : T | object | object[]) : INSERT<T>
187
- into: TaggedTemplateQueryPart<this>
188
- into (entity : Definition | string) : this
182
+ static into : (<T extends ArrayConstructable<any>> (entity:T, entries? : object | object[]) => INSERT<SingularType<T>>)
183
+ & (TaggedTemplateQueryPart<INSERT<unknown>>)
184
+ & ((entity : Definition | string, entries? : object | object[]) => INSERT<any>)
185
+ & (<T> (entity:Constructable<T>, entries? : object | object[]) => INSERT<T>)
186
+ & (<T> (entity:T, entries? : T | object | object[]) => INSERT<T>)
187
+
188
+ into: (<T extends ArrayConstructable> (entity:T) => this)
189
+ & TaggedTemplateQueryPart<this>
190
+ & ((entity : Definition | string) => this)
189
191
  data (block : (e:T)=>void) : this
190
192
  entries (...entries : object[]) : this
191
193
  columns (...col: string[]) : this
@@ -195,7 +197,9 @@ export class INSERT<T> extends ConstructedQuery {
195
197
  }
196
198
 
197
199
  export class DELETE<T> extends ConstructedQuery {
198
- static from (entity : Definition | string | ArrayConstructable, primaryKey? : PK) : DELETE<any>
200
+ static from:
201
+ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
202
+ & ((entity : Definition | string | ArrayConstructable, primaryKey? : PK) => DELETE<any>)
199
203
  byKey (primaryKey? : PK) : this
200
204
  where (predicate:object) : this
201
205
  where (...expr : any[]) : this
@@ -206,7 +210,7 @@ export class DELETE<T> extends ConstructedQuery {
206
210
 
207
211
  export class UPDATE<T> extends ConstructedQuery {
208
212
  // cds-typer plural
209
- static entity <T extends ArrayConstructable> (entity:T, primaryKey? : PK) : UPDATE<SingularType<T>>
213
+ static entity <T extends ArrayConstructable<any>> (entity:T, primaryKey? : PK) : UPDATE<SingularType<T>>
210
214
 
211
215
  static entity (entity : Definition | string, primaryKey? : PK) : UPDATE<any>
212
216
  static entity <T> (entity:Constructable<T>, primaryKey? : PK) : UPDATE<T>
@@ -1,34 +1,50 @@
1
1
  import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery } from './ql'
2
+ import { Projection, Proxy, Awaitable } from './ql'
3
+ import { SingularType, ArrayConstructable } from './internal/inference'
2
4
  import { LinkedModel, Definition, Definitions } from './reflect'
3
5
  import { csn, type } from "./csn"
4
6
  // import { Service } from './cds'
5
7
 
6
-
7
8
  export class QueryAPI {
8
9
 
9
10
  /**
10
11
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
11
12
  */
12
- read <T>(entity : Definition | string, key?: any) : SELECT<T>
13
+ read: {
14
+ <T extends ArrayConstructable<any>>(entity : T, key?: any) : Awaitable<SELECT<T>, InstanceType<T>>
15
+ <T>(entity : Definition | string, key?: any) : SELECT<T>
16
+ }
13
17
 
14
18
  /**
15
19
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
16
20
  */
17
- create <T>(entity : Definition | string, key?: any) : INSERT<T>
21
+ create: {
22
+ <T extends ArrayConstructable<any>>(entity : T, key?: any) : INSERT<T>
23
+ <T>(entity : Definition | string, key?: any) : INSERT<T>
24
+ }
18
25
 
19
26
  /**
20
27
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
21
28
  */
22
- insert <T>(data : object | object[]) : INSERT<T>
29
+ insert: {
30
+ <T extends ArrayConstructable<any>>(data : T) : INSERT<T>
31
+ <T>(data : object | object[]) : INSERT<T>
32
+ }
23
33
 
24
34
  /**
25
35
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
26
36
  */
27
- update <T>(entity : Definition | string, key?: any) : UPDATE<T>
37
+ update: {
38
+ <T extends ArrayConstructable<any>>(entity : T, key?: any) : UPDATE<T>
39
+ <T>(entity : Definition | string, key?: any) : UPDATE<T>
40
+ }
28
41
 
29
42
  /**
30
43
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
31
44
  */
45
+ // as delete is the only one of the CRUD methods from QueryAPI
46
+ // that is extended in Service, we have to add the second signature down there
47
+ // (TS error 2425)
32
48
  delete <T>(entity : Definition | string, key?: any) : DELETE<T>
33
49
 
34
50
  /**
@@ -44,7 +60,7 @@ export class QueryAPI {
44
60
  /**
45
61
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run-sql)
46
62
  */
47
- run (query : string, args? : any[]|object) : Promise<ResultSet | any>
63
+ run (query : string, args? : any[]|object) : Promise<ResultSet | any>
48
64
 
49
65
  /**
50
66
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
@@ -184,6 +200,7 @@ export class Service extends QueryAPI {
184
200
  /**
185
201
  * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
186
202
  */
203
+ delete <T extends ArrayConstructable<any>>(entity : T, key?: any): DELETE<T>
187
204
  delete <T>(entity : Definition | string, key?: any) : DELETE<T>
188
205
 
189
206
  // The central method to dispatch events
@@ -2,8 +2,8 @@ const fs = require('fs')
2
2
  const path = require('path')
3
3
  const _cds = require('./cds'), { log } = _cds.exec
4
4
  const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = require('@sap/cds-compiler')
5
- const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
6
- const { OUTPUT_MODE_DEFAULT, SEVERITIES, LOG_LEVELS, LOG_MODULE_NAMES } = require('./constants')
5
+ const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels, hasJavaNature } = require('./util')
6
+ const { OUTPUT_MODE_DEFAULT, SEVERITIES, LOG_LEVELS, LOG_MODULE_NAMES, CDS_MODEL_EXCLUDE_LIST } = require('./constants')
7
7
  const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
8
8
  const BuildTaskHandlerInternal = require('./provider/buildTaskHandlerInternal')
9
9
 
@@ -28,6 +28,7 @@ class BuildTaskEngine {
28
28
 
29
29
  async processTasks(tasks, buildOptions, clean = true) {
30
30
  const startTime = Date.now()
31
+ const messages = []
31
32
 
32
33
  if (buildOptions) {
33
34
  // clone as data may be stored as part of the buildOptions object
@@ -48,14 +49,16 @@ class BuildTaskEngine {
48
49
  buildOptions.target = path.resolve(buildOptions.root, this.env.build.target)
49
50
  }
50
51
 
51
- // validate required @sap namespace models - log only
52
- const { unresolved, missing } = this._resolveRequiredSapServices(tasks)
53
- const messages = []
54
- if (unresolved.length > 0) {
55
- messages.push(new BuildMessage(`Required CDS service models [${unresolved.join(', ')}] cannot be resolved. Make sure to install the missing npm modules.`))
56
- }
57
- if (missing.length > 0) {
58
- messages.push(new BuildMessage(`Required CDS service models [${missing.join(', ')}] are missing in custom build tasks. Make sure to add the missing models.`))
52
+ // Java projects don't have node modules installed on project root level
53
+ if (!hasJavaNature([buildOptions.root])) {
54
+ // validate required @sap namespace models - log only
55
+ const { unresolved, missing } = this._resolveRequiredSapServices(tasks)
56
+ if (unresolved.length > 0) {
57
+ messages.push(new BuildMessage(`Required CDS service models [${unresolved.join(', ')}] cannot be resolved. Make sure to install the missing npm modules.`))
58
+ }
59
+ if (missing.length > 0) {
60
+ messages.push(new BuildMessage(`Required CDS service models [${missing.join(', ')}] are missing in custom build tasks. Make sure to add the missing models.`))
61
+ }
59
62
  }
60
63
 
61
64
  // create build task handlers
@@ -85,7 +88,7 @@ class BuildTaskEngine {
85
88
  return buildResult
86
89
  } catch (error) {
87
90
  this._logBuildOutput(handlers, buildOptions)
88
-
91
+
89
92
  // cds CLI layer logs in case of an exception if invoked from CLI
90
93
  if (!buildOptions.cli) {
91
94
  this._logMessages(buildOptions, [...BuildTaskEngine._getErrorMessages([error]), ...messages])
@@ -336,7 +339,7 @@ class BuildTaskEngine {
336
339
 
337
340
  const unresolved = resolveRequiredSapModels(this.cds, [...taskModelPaths])
338
341
  // are the required service models contained in the task's options.model
339
- const missing = srvModelPaths.filter(m => m.startsWith('@sap/') && !taskModelPaths.has(m) && !unresolved.find(u => u === m))
342
+ const missing = srvModelPaths.filter(m => m.startsWith('@sap/') && !CDS_MODEL_EXCLUDE_LIST.includes(m) && !taskModelPaths.has(m) && !unresolved.find(u => u === m))
340
343
 
341
344
  return { unresolved, missing }
342
345
  }
@@ -117,7 +117,7 @@ class BuildTaskHandler {
117
117
  // relative to build task's destination path
118
118
  dest = path.resolve(this.task.dest, dest)
119
119
  }
120
- this._pushFile(dest)
120
+ this.pushFile(dest)
121
121
  if (this._hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_DEFAULT)) {
122
122
  await fs.mkdir(path.dirname(dest), { recursive: true })
123
123
  await fs.writeFile(dest, typeof data === "object" && !Buffer.isBuffer(data) ? JSON.stringify(data, null, 2) : data)
@@ -151,7 +151,7 @@ class BuildTaskHandler {
151
151
  // relative to build task's destination path
152
152
  dest = path.resolve(this.task.dest, dest)
153
153
  }
154
- this._pushFile(dest)
154
+ this.pushFile(dest)
155
155
  if (this._hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_DEFAULT)) {
156
156
  if (fs.cp) { // Node.js >= 16.7
157
157
  return fs.cp(src, dest, { recursive: true })
@@ -205,7 +205,7 @@ class BuildTaskHandler {
205
205
  * Adds the given fully qualified file path to the list of files that are written by this build task.
206
206
  * @param {string} filePath
207
207
  */
208
- _pushFile(filePath) {
208
+ pushFile(filePath) {
209
209
  this._written.add(filePath)
210
210
  }
211
211
  /**
@@ -45,6 +45,8 @@ exports.FOLDER_GEN = "gen"
45
45
  exports.FILE_EXT_CDS = ".cds"
46
46
  exports.MTX_SIDECAR_FOLDER = "mtx/sidecar" // default name of the mtx sidecar folder
47
47
  exports.DEFAULT_CSN_FILE_NAME = "csn.json"
48
+ // REVISIT: the models are not required if a custom server.js file is used for MTX bootstrap
49
+ exports.CDS_MODEL_EXCLUDE_LIST = ['@sap/cds/srv/mtx', '@sap/cds-mtxs/srv/bootstrap']
48
50
 
49
51
  exports.CDS_CONFIG_PATH_SEP = "/"
50
52
  exports.SKIP_ASSERT_COMPILER_V2 = "skip-assert-compiler-v2"
@@ -26,7 +26,7 @@ class BuildTaskHandlerEdmx extends BuildTaskHandlerFeatureToggles {
26
26
  const result = this.cds.compile.to.edmx(model, options)
27
27
 
28
28
  if (result) {
29
- let langs = this.task.options.lang || this.cds.env.i18n.languages
29
+ let langs = this.task.options.lang || this.env.i18n.languages
30
30
  if (langs.split) { // string to array
31
31
  langs = langs.split(',')
32
32
  }
@@ -43,8 +43,9 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
43
43
  }
44
44
 
45
45
  async collectAllLanguageBundles(dictionary, paths, destBase, destFts) {
46
+ const i18nFolder = this.env.i18n.folders?.[0] || 'i18n'
46
47
  // create language bundle for base model
47
- const i18n = await this.collectLanguageBundles(dictionary.base, destBase)
48
+ const i18n = await this.collectLanguageBundles(dictionary.base, path.join(destBase, i18nFolder))
48
49
  if (i18n) {
49
50
  this._result.languageBundles = i18n.bundles
50
51
  }
@@ -54,7 +55,7 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
54
55
  for (const ftName in dictionary.features) {
55
56
  // attach the sources information for i18n location reference
56
57
  dictionary.features[ftName]['$sources'] = paths.features[ftName]
57
- await this.collectLanguageBundles(dictionary.features[ftName], path.join(destFts, this.ftsName, ftName))
58
+ await this.collectLanguageBundles(dictionary.features[ftName], path.join(destFts, this.ftsName, ftName, i18nFolder))
58
59
  }
59
60
  }
60
61
  }
@@ -108,7 +109,7 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
108
109
 
109
110
  // replace require paths by base model path to ensure precedence of feature annotations
110
111
  // see https://pages.github.tools.sap/cap/docs/cds/compiler-messages#anno-duplicate-unrelated-layer
111
- ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g,'/')]
112
+ ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g, '/')]
112
113
 
113
114
  await this.compileToJson(ftCsn, path.join(ftPath, DEFAULT_CSN_FILE_NAME))
114
115
  await this._validateFeature(ftPath)
@@ -204,8 +204,8 @@ class BuildTaskHandlerInternal extends BuildTaskHandler {
204
204
  bundles = {}
205
205
  }
206
206
  // copied from ../compile/i18n.js
207
- const { folders = ['i18n'], file: base = 'i18n' } = this.env.i18n
208
- const file = path.join(bundleDest, folders[0], base + '.json')
207
+ const { file: base = 'i18n' } = this.env.i18n
208
+ const file = path.join(bundleDest, base + '.json')
209
209
 
210
210
  // bundleDest might be null
211
211
  if (bundleDest && Object.keys(bundles).length > 0) {
@@ -48,7 +48,8 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
48
48
 
49
49
  if (this.hasBuildOption(CONTENT_LANGUAGE_BUNDLES, true)) {
50
50
  // collect and write language bundles into single i18n.json file
51
- const i18n = await this.collectLanguageBundles(model, this.task.dest)
51
+ const i18nFolder = this.env.i18n.folders?.[0] || 'i18n'
52
+ const i18n = await this.collectLanguageBundles(model, path.join(this.task.dest, i18nFolder))
52
53
  if (i18n) {
53
54
  this._result.languageBundles = i18n.bundles
54
55
  }
@@ -96,7 +96,8 @@ class ClassicMtxBuilder {
96
96
  }))
97
97
 
98
98
  // collect and write language bundles into single i18n.json file
99
- const i18n = await this._handler.collectLanguageBundles(model, destSdc)
99
+ const i18nFolder = this._handler.env.i18n.folders?.[0] || 'i18n'
100
+ const i18n = await this._handler.collectLanguageBundles(model, path.join(destSdc, i18nFolder))
100
101
  if (i18n) {
101
102
  this._handler._result.languageBundles = i18n.bundles
102
103
  }
@@ -15,16 +15,17 @@ class ResourcesTarBuilder {
15
15
  async createTar(dest, model) {
16
16
  const { root, files } = await this._getResources(model)
17
17
  if (files.length === 0) {
18
- // packTarArchive failes otherwise
18
+ // packTarArchive fails otherwise
19
19
  this.handler.pushMessage("No deployment resources found - skip resources.tgz", WARNING)
20
20
  return
21
21
  }
22
22
  await this.writeTarFile(files, root, path.join(dest, DEFAULT_TAR_NAME))
23
23
  }
24
24
 
25
- async writeTarFile(files, root, tarFile) { // REVISIT: eliminate this anti-pattern helper
25
+ async writeTarFile(files, root, tarFile) {
26
26
  const { tar } = require('../../../../lib').utils
27
27
  await tar.czfd (tarFile, root, files) // REVISIT: tar.czfd was created for this case only -> it ensures the target's dir exists
28
+ this.handler.pushFile(tarFile)
28
29
  }
29
30
 
30
31
  async _getResources(model) {
@@ -33,7 +33,8 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
33
33
  await this.compileToJson(extCsn, csnFile)
34
34
  allFiles.push(csnFile)
35
35
 
36
- const i18n = await this.collectLanguageBundles(extCsn, destExt)
36
+ // static i18n folder name as runtime does not use the CDS config of the extension project
37
+ const i18n = await this.collectLanguageBundles(extCsn, path.join(destExt, 'i18n'))
37
38
  if (i18n) {
38
39
  allFiles.push(i18n.file)
39
40
  }
@@ -44,9 +44,11 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
44
44
  async _buildNodeApp(sidecarEnv) {
45
45
  const destSidecar = this.task.dest
46
46
  const destSidecarSrc = path.join(destSidecar, this.env.folders.srv)
47
+ const i18nFolder = this.env.i18n.folders?.[0] || 'i18n'
47
48
  const model = this._compileSidecarSync(sidecarEnv)
48
49
  await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
49
- await this.collectLanguageBundles(model, destSidecarSrc)
50
+
51
+ await this.collectLanguageBundles(model, path.join(destSidecarSrc, i18nFolder))
50
52
  await this.copyProjectRootContent(this.task.src, destSidecar)
51
53
  }
52
54
 
package/bin/build/util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
- const { SEVERITY_ERROR } = require('./constants')
3
+ const { SEVERITY_ERROR, CDS_MODEL_EXCLUDE_LIST } = require('./constants')
4
4
 
5
5
  function getProperty(src, segments) {
6
6
  segments = Array.isArray(segments) ? segments : segments.split('.')
@@ -142,7 +142,7 @@ function isStreamlinedMtx(cds) {
142
142
  */
143
143
  function resolveRequiredSapModels(cds, modelPaths) {
144
144
  return modelPaths.filter(p => {
145
- if (p.startsWith('@sap/')) {
145
+ if (p.startsWith('@sap/') && !CDS_MODEL_EXCLUDE_LIST.includes(p)) {
146
146
  const files = cds.resolve(p)
147
147
  return !files || files.length === 0
148
148
  }