@sap/cds 6.8.4 → 7.0.0

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 (214) hide show
  1. package/CHANGELOG.md +58 -5
  2. package/README.md +0 -1
  3. package/bin/cds-serve.js +50 -3
  4. package/bin/deploy/to-hana.js +1 -0
  5. package/bin/serve.js +16 -20
  6. package/lib/auth/basic-auth.js +6 -4
  7. package/lib/auth/index.js +4 -3
  8. package/lib/auth/jwt-auth.js +2 -5
  9. package/lib/compile/cds-compile.js +34 -89
  10. package/lib/compile/cdsc.js +11 -0
  11. package/lib/compile/etc/properties.js +2 -2
  12. package/lib/compile/for/lean_drafts.js +36 -69
  13. package/lib/compile/for/nodejs.js +2 -1
  14. package/lib/compile/load.js +1 -1
  15. package/lib/compile/minify.js +2 -0
  16. package/lib/compile/to/csn.js +74 -0
  17. package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
  18. package/lib/compile/to/json.js +1 -1
  19. package/lib/compile/to/sql.js +8 -6
  20. package/lib/dbs/cds-deploy.js +174 -114
  21. package/lib/env/cds-env.js +64 -79
  22. package/lib/env/cds-requires.js +11 -28
  23. package/lib/env/defaults.js +13 -3
  24. package/lib/env/plugins.js +1 -12
  25. package/lib/env/presets.js +25 -21
  26. package/lib/index.js +121 -147
  27. package/lib/{core/reflect.js → linked/models.js} +2 -2
  28. package/lib/{core/infer.js → linked/queries.js} +2 -0
  29. package/lib/{core/index.js → linked/types.js} +2 -1
  30. package/lib/log/cds-error.js +13 -7
  31. package/lib/log/format/cf.js +1 -1
  32. package/lib/plugins.js +49 -0
  33. package/lib/ql/Query.js +0 -9
  34. package/lib/ql/STREAM.js +0 -1
  35. package/lib/req/context.js +2 -7
  36. package/lib/req/request.js +6 -2
  37. package/lib/req/response.js +23 -10
  38. package/lib/srv/middlewares/ctx-model.js +1 -1
  39. package/lib/srv/middlewares/errors.js +1 -1
  40. package/lib/srv/protocols/_legacy.js +1 -0
  41. package/lib/srv/protocols/graphql.js +7 -16
  42. package/lib/srv/protocols/index.js +59 -45
  43. package/lib/srv/protocols/odata-v2-proxy.js +2 -70
  44. package/lib/srv/srv-api.js +9 -3
  45. package/lib/srv/srv-dispatch.js +12 -9
  46. package/lib/srv/srv-models.js +4 -21
  47. package/lib/srv/srv-tx.js +15 -12
  48. package/lib/utils/cds-test.js +14 -9
  49. package/lib/utils/cds-utils.js +2 -12
  50. package/lib/utils/check-version.js +17 -0
  51. package/{bin/build → lib/utils}/csv-reader.js +23 -24
  52. package/libx/_runtime/auth/index.js +27 -23
  53. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
  54. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  55. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
  56. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
  57. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
  58. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
  59. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
  61. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
  62. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
  63. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
  64. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
  65. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
  66. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
  68. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
  70. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
  71. package/libx/_runtime/cds-services/services/Service.js +79 -107
  72. package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
  73. package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
  74. package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
  75. package/libx/_runtime/cds-services/util/assert.js +65 -2
  76. package/libx/_runtime/common/composition/data.js +1 -0
  77. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  78. package/libx/_runtime/common/generic/auth/restrict.js +5 -10
  79. package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
  80. package/libx/_runtime/common/generic/auth/utils.js +1 -2
  81. package/libx/_runtime/common/generic/crud.js +32 -16
  82. package/libx/_runtime/common/generic/etag.js +133 -104
  83. package/libx/_runtime/common/generic/input.js +6 -21
  84. package/libx/_runtime/common/generic/put.js +1 -1
  85. package/libx/_runtime/common/generic/stream.js +52 -0
  86. package/libx/_runtime/common/generic/temporal.js +25 -8
  87. package/libx/_runtime/common/i18n/messages.properties +0 -2
  88. package/libx/_runtime/common/utils/cqn.js +1 -1
  89. package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
  90. package/libx/_runtime/common/utils/csn.js +0 -51
  91. package/libx/_runtime/common/utils/etag.js +30 -0
  92. package/libx/_runtime/common/utils/keys.js +1 -1
  93. package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
  94. package/libx/_runtime/common/utils/path.js +1 -1
  95. package/libx/_runtime/common/utils/resolveView.js +2 -1
  96. package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
  97. package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
  98. package/libx/_runtime/common/utils/stream.js +140 -0
  99. package/libx/_runtime/common/utils/streamProp.js +29 -12
  100. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
  101. package/libx/_runtime/db/generic/index.js +0 -2
  102. package/libx/_runtime/db/query/delete.js +2 -2
  103. package/libx/_runtime/db/query/insert.js +2 -2
  104. package/libx/_runtime/db/query/read.js +2 -2
  105. package/libx/_runtime/db/query/run.js +2 -2
  106. package/libx/_runtime/db/query/update.js +2 -2
  107. package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
  108. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
  109. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
  110. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
  111. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
  112. package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
  113. package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
  114. package/libx/_runtime/fiori/draft.js +2 -0
  115. package/libx/_runtime/fiori/generic/activate.js +8 -9
  116. package/libx/_runtime/fiori/generic/before.js +30 -20
  117. package/libx/_runtime/fiori/generic/cancel.js +5 -3
  118. package/libx/_runtime/fiori/generic/delete.js +5 -3
  119. package/libx/_runtime/fiori/generic/edit.js +7 -7
  120. package/libx/_runtime/fiori/generic/index.js +10 -16
  121. package/libx/_runtime/fiori/generic/new.js +5 -3
  122. package/libx/_runtime/fiori/generic/patch.js +11 -8
  123. package/libx/_runtime/fiori/generic/prepare.js +13 -6
  124. package/libx/_runtime/fiori/generic/read.js +12 -6
  125. package/libx/_runtime/fiori/lean-draft.js +207 -152
  126. package/libx/_runtime/fiori/utils/delete.js +10 -5
  127. package/libx/_runtime/fiori/utils/req.js +17 -5
  128. package/libx/_runtime/fiori/utils/stream.js +36 -0
  129. package/libx/_runtime/hana/Service.js +12 -9
  130. package/libx/_runtime/hana/conversion.js +10 -15
  131. package/libx/_runtime/hana/driver.js +2 -0
  132. package/libx/_runtime/hana/execute.js +28 -6
  133. package/libx/_runtime/hana/pool.js +36 -122
  134. package/libx/_runtime/hana/search2cqn4sql.js +34 -36
  135. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
  136. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
  137. package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
  138. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  139. package/libx/_runtime/remote/Service.js +20 -1
  140. package/libx/_runtime/remote/utils/client.js +3 -5
  141. package/libx/_runtime/sqlite/Service.js +4 -6
  142. package/libx/_runtime/sqlite/conversion.js +3 -13
  143. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
  144. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
  145. package/libx/_runtime/sqlite/execute.js +5 -16
  146. package/libx/odata/afterburner.js +22 -6
  147. package/libx/odata/grammar.pegjs +6 -1
  148. package/libx/odata/parser.js +1 -1
  149. package/libx/rest/RestAdapter.js +16 -9
  150. package/libx/rest/RestRequest.js +1 -1
  151. package/libx/rest/middleware/input.js +2 -1
  152. package/libx/rest/middleware/operation.js +1 -0
  153. package/libx/rest/middleware/parse.js +3 -2
  154. package/libx/rest/middleware/payload.js +9 -8
  155. package/libx/rest/middleware/read.js +1 -0
  156. package/package.json +9 -16
  157. package/app/fiori/preview.js +0 -270
  158. package/app/fiori/routes.js +0 -59
  159. package/bin/build/buildTaskEngine.js +0 -360
  160. package/bin/build/buildTaskFactory.js +0 -283
  161. package/bin/build/buildTaskHandler.js +0 -241
  162. package/bin/build/buildTaskProvider.js +0 -22
  163. package/bin/build/buildTaskProviderFactory.js +0 -175
  164. package/bin/build/cds.js +0 -5
  165. package/bin/build/constants.js +0 -66
  166. package/bin/build/index.js +0 -58
  167. package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
  168. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
  169. package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
  170. package/bin/build/provider/buildTaskProviderInternal.js +0 -383
  171. package/bin/build/provider/fiori/index.js +0 -171
  172. package/bin/build/provider/hana/2migration.js +0 -179
  173. package/bin/build/provider/hana/index.js +0 -505
  174. package/bin/build/provider/hana/migrationtable.js +0 -472
  175. package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
  176. package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
  177. package/bin/build/provider/hana/template/.hdinamespace +0 -4
  178. package/bin/build/provider/hana/template/package.json +0 -12
  179. package/bin/build/provider/hana/template/undeploy.json +0 -5
  180. package/bin/build/provider/java/index.js +0 -111
  181. package/bin/build/provider/java-cf/index.js +0 -1
  182. package/bin/build/provider/mtx/index.js +0 -268
  183. package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
  184. package/bin/build/provider/mtx-extension/index.js +0 -131
  185. package/bin/build/provider/mtx-sidecar/index.js +0 -137
  186. package/bin/build/provider/node-cf/index.js +0 -1
  187. package/bin/build/provider/nodejs/index.js +0 -192
  188. package/bin/build/util.js +0 -299
  189. package/bin/cds.js +0 -125
  190. package/bin/deploy/to-hana/cfUtil.js +0 -355
  191. package/bin/deploy/to-hana/gitUtil.js +0 -57
  192. package/bin/deploy/to-hana/hana.js +0 -306
  193. package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
  194. package/bin/deploy/to-hana/index.js +0 -16
  195. package/bin/deploy/to-hana/mtaUtil.js +0 -170
  196. package/bin/mtx/in-cds.js +0 -17
  197. package/bin/plugins.js +0 -32
  198. package/bin/run.js +0 -24
  199. package/bin/utils/log.js +0 -24
  200. package/bin/version.js +0 -178
  201. package/libx/_runtime/audit/Service.js +0 -222
  202. package/libx/_runtime/audit/generic/personal/access.js +0 -61
  203. package/libx/_runtime/audit/generic/personal/index.js +0 -56
  204. package/libx/_runtime/audit/generic/personal/modification.js +0 -132
  205. package/libx/_runtime/audit/generic/personal/utils.js +0 -186
  206. package/libx/_runtime/audit/utils/log.js +0 -23
  207. package/libx/_runtime/audit/utils/v2.js +0 -176
  208. package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
  209. package/libx/_runtime/db/generic/integrity.js +0 -455
  210. package/srv/audit-log.cds +0 -87
  211. package/srv/mtx.cds +0 -2
  212. package/srv/mtx.js +0 -8
  213. /package/lib/{core → linked}/classes.js +0 -0
  214. /package/lib/{core → linked}/entities.js +0 -0
@@ -27,13 +27,10 @@ const _checkAppURL = appURL => {
27
27
  )
28
28
  }
29
29
 
30
- const _oldMtx = () => cds.mtx
31
- const _multitenancyEnabled = () => cds.requires.multitenancy || _oldMtx()
32
-
33
30
  // REVISIT: It's bad to have to rely on the subdomain.
34
31
  // For all interactions where we perform the token exchange ourselves,
35
32
  // we will be able to use the zoneId instead of the subdomain.
36
- const _subdomainFromContext = context => context?.http.req?.authInfo?.getSubdomain()
33
+ const _subdomainFromContext = context => context?.http.req?.authInfo?.getSubdomain?.()
37
34
 
38
35
  class EnterpriseMessaging extends AMQPWebhookMessaging {
39
36
  init() {
@@ -41,61 +38,17 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
41
38
  return super.init()
42
39
  }
43
40
 
44
- // Needs to be run after `served` event, otherwise `ProvisioningService` might not be available.
45
- // REVISIT: We should register the handlers before, otherwise a tenant subscription
46
- // immediately after app start won't trigger those handlers.
47
- async addMTXHandlers() {
48
- const provisioning = await cds.connect.to('ProvisioningService')
49
- const tenantPersistence = await cds.connect.to('TenantPersistenceService')
50
- tenantPersistence.impl(() => {
51
- tenantPersistence.on('createTenant', async (req, next) => {
52
- const res = await next()
53
- const subdomain = req.data.subscriptionData.subscribedSubdomain
54
- const management = await this.getManagement(subdomain).waitUntilReady()
55
- await management.deploy()
56
- return res
57
- })
58
- })
59
- provisioning.impl(() => {
60
- provisioning.on('DELETE', 'tenant', async (req, next) => {
61
- const subdomain = req.data.subscribedSubdomain
62
- try {
63
- const management = await this.getManagement(subdomain).waitUntilReady()
64
- await management.undeploy()
65
- } catch (error) {
66
- this.LOG.error('Failed to delete messaging artifacts for subdomain', subdomain, '(', error, ')')
67
- }
68
- return next()
69
- })
70
- provisioning.on('dependencies', async (req, next) => {
71
- this.LOG._info && this.LOG.info('Include Enterprise-Messaging as SaaS dependency')
72
- const res = await next()
73
- const xsappname = this.options.credentials && this.options.credentials.xsappname
74
- if (xsappname) {
75
- const exists = res.some(d => d.xsappname === xsappname)
76
- if (!exists) res.push({ xsappname })
77
- }
78
- return res
79
- })
80
- })
81
- }
82
-
83
41
  // New mtx based on @sap/cds-mtxs
84
42
  async addMTXSHandlers() {
85
43
  // REVISIT: Is that tested with MTX services in sidecar?
86
44
  const deploymentSrv = await cds.connect.to('cds.xt.DeploymentService')
87
45
  const provisioningSrv = await cds.connect.to('cds.xt.SaasProvisioningService')
88
46
  deploymentSrv.impl(() => {
89
- deploymentSrv.after('subscribe', async (res, req) => {
47
+ deploymentSrv.after('subscribe', async (_res, req) => {
90
48
  const { tenant } = req.data
91
49
  let subdomain
92
- try {
93
- const tenantInfo = await getTenantInfo(tenant) // @sap/cds-mtxs must provide that info
94
- subdomain = tenantInfo.subdomain
95
- } catch (e) {
96
- this.LOG.error("'subscribe' is not yet implemented for @sap/cds-mtxs")
97
- throw e
98
- }
50
+ const tenantInfo = await getTenantInfo(tenant)
51
+ subdomain = tenantInfo.subdomain
99
52
  const management = await this.getManagement(subdomain).waitUntilReady()
100
53
  await management.deploy()
101
54
  })
@@ -103,10 +56,10 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
103
56
  const { tenant } = req.data
104
57
  let subdomain
105
58
  try {
106
- const tenantInfo = await getTenantInfo(tenant) // @sap/cds-mtxs must provide that info
59
+ const tenantInfo = await getTenantInfo(tenant)
107
60
  subdomain = tenantInfo.subdomain
108
61
  } catch (e) {
109
- this.LOG.error("'unsubscribe' is not yet implemented for @sap/cds-mtxs")
62
+ if (e.status === 404) return // idempotent
110
63
  throw e
111
64
  }
112
65
  try {
@@ -118,7 +71,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
118
71
  })
119
72
  })
120
73
  provisioningSrv.impl(() => {
121
- provisioningSrv.on('dependencies', async (req, next) => {
74
+ provisioningSrv.on('dependencies', async (_req, next) => {
122
75
  this.LOG._info && this.LOG.info('Include Enterprise-Messaging as SaaS dependency')
123
76
  const res = (await next()) || []
124
77
  const xsappname = this.options.credentials?.xsappname
@@ -132,7 +85,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
132
85
  }
133
86
 
134
87
  startListening() {
135
- const doNotDeploy = _multitenancyEnabled() && !this.options.deployForProvider
88
+ const doNotDeploy = cds.requires.multitenancy && !this.options.deployForProvider
136
89
  if (doNotDeploy) this.LOG._info && this.LOG.info('Skipping deployment of messaging artifacts for provider account')
137
90
  super.startListening({ doNotDeploy })
138
91
  if (!doNotDeploy && (this._listenToAll || this.subscribedTopics.size)) {
@@ -151,9 +104,8 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
151
104
  async listenToClient(cb) {
152
105
  _checkAppURL(this.optionsApp.appURL)
153
106
  registerWebhookEndpoints(BASE_PATH, this.queueName, this.LOG, cb)
154
- if (_multitenancyEnabled()) {
155
- if (_oldMtx()) await this.addMTXHandlers()
156
- else await this.addMTXSHandlers()
107
+ if (cds.requires.multitenancy) {
108
+ await this.addMTXSHandlers()
157
109
  registerDeployEndpoints(BASE_PATH, this.queueName, async (tenantInfo, options) => {
158
110
  const result = { queue: this.queueName, succeeded: [], failed: [] }
159
111
  await Promise.all(
@@ -37,7 +37,7 @@ const hasPersistentOutbox = (srv, tenant) => {
37
37
  if (!cds.requires.outbox || cds.requires.outbox.kind !== 'persistent-outbox') return false
38
38
  if (srv.options && srv.options.outbox && srv.options.outbox.kind && srv.options.outbox.kind !== 'persistent-outbox')
39
39
  return false
40
- if ((cds.mtx || cds.requires.multitenancy) && tenant && _isProviderTenant(tenant)) return false // no persistence for provider account
40
+ if (cds.requires.multitenancy && tenant && _isProviderTenant(tenant)) return false // no persistence for provider account
41
41
  return true
42
42
  }
43
43
 
@@ -7,13 +7,19 @@ const { getKind, run, getDestination, getAdditionalOptions, getReqOptions } = re
7
7
  const { formatVal } = require('../../odata/utils')
8
8
  const { hasAliasedColumns } = require('./utils/data')
9
9
 
10
- let _cloudSdkConnectivity
10
+ let _cloudSdkConnectivity, _cloudSdkResilience
11
11
  const cloudSdkConnectivity = () => {
12
12
  if (_cloudSdkConnectivity) return _cloudSdkConnectivity
13
13
  // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
14
14
  _cloudSdkConnectivity = require('@sap-cloud-sdk/connectivity')
15
15
  return _cloudSdkConnectivity
16
16
  }
17
+ const cloudSdkResilience = () => {
18
+ if (_cloudSdkResilience !== undefined) return _cloudSdkResilience
19
+ // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
20
+ _cloudSdkResilience = require('@sap-cloud-sdk/resilience')
21
+ return _cloudSdkResilience
22
+ }
17
23
 
18
24
  const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
19
25
 
@@ -200,9 +206,15 @@ class RemoteService extends cds.Service {
200
206
  getDestination(this.definition?.name ?? this.datasource, this.options.credentials)
201
207
  this.path = this.options.credentials.path
202
208
 
209
+ // `requestTimeout` API is kept as it was public
203
210
  this.requestTimeout = this.options.credentials.requestTimeout
204
211
  if (this.requestTimeout == null) this.requestTimeout = 60000
205
212
 
213
+ // we're using this as an object to allow remote services without the need for Cloud SDK
214
+ // required for BAS creating remote services only for events
215
+ // at first request the middlewares are created
216
+ this._resilienceMiddlewares = {}
217
+
206
218
  // REVISIT: remove cds.env.features.fetch_csrf in next major ^7
207
219
  this.csrf = cds.env.features.fetch_csrf ?? this.options.csrf
208
220
  this.csrfInBatch = this.options.csrfInBatch
@@ -250,6 +262,13 @@ class RemoteService extends cds.Service {
250
262
  }
251
263
 
252
264
  this.on('*', async (req, next) => {
265
+ // early validation on first request for use case without remote API
266
+ // ideally, that's done on bootstrap of the remote service
267
+ if (typeof this.destination === 'object' && !this.destination.url)
268
+ throw new Error(`"url" or "destination" property must be configured in "credentials" of "${this.name}".`)
269
+ if (this._resilienceMiddlewares && !this._resilienceMiddlewares.timeout)
270
+ this._resilienceMiddlewares.timeout = cloudSdkResilience().timeout(this.requestTimeout)
271
+
253
272
  const { query } = req
254
273
  if (!query && !(typeof req.path === 'string')) return next()
255
274
 
@@ -77,10 +77,6 @@ const cloudSdk = () => {
77
77
  }
78
78
 
79
79
  const getDestination = (name, credentials) => {
80
- if (!credentials.url) {
81
- throw new Error(`"url" or "destination" property must be configured in "credentials" of "${name}".`)
82
- }
83
-
84
80
  // Cloud SDK wants property "queryParameters" but we have documented "queries"
85
81
  if (credentials.queries && !credentials.queryParameters) {
86
82
  credentials.queryParameters = credentials.queries
@@ -466,7 +462,9 @@ const getReqOptions = (req, query, service) => {
466
462
  }
467
463
 
468
464
  reqOptions.headers = { accept: 'application/json,text/plain' }
469
- reqOptions.timeout = service.requestTimeout
465
+
466
+ // add resilience middlewares for Cloud SDK
467
+ reqOptions.middleware = [service._resilienceMiddlewares.timeout]
470
468
 
471
469
  if (!_hasHeader(req.headers, 'accept-language')) {
472
470
  // Forward the locale properties from the original request (including region variants or weight factors),
@@ -56,12 +56,12 @@ module.exports = class SQLiteDatabase extends DatabaseService {
56
56
  * tx
57
57
  */
58
58
  this.on(['BEGIN', 'COMMIT', 'ROLLBACK'], function (req) {
59
+ // REVISIT: better?
60
+ this.dbc._closed = req.event !== 'BEGIN'
61
+
59
62
  return this._run(this.model, this.dbc, req.event)
60
63
  })
61
64
 
62
- // REVISIT: register only if needed?
63
- this.before('COMMIT', this._integrity.performCheck)
64
-
65
65
  /*
66
66
  * generic
67
67
  */
@@ -79,9 +79,6 @@ module.exports = class SQLiteDatabase extends DatabaseService {
79
79
  this.before('READ', '*', convertAssocToOneManaged)
80
80
  this.before('READ', '*', localized) // > has to run after rewrite
81
81
  this.before('READ', '*', this._virtual)
82
-
83
- // REVISIT: get data to be deleted for integrity check
84
- this.before('DELETE', '*', this._integrity.beforeDelete)
85
82
  }
86
83
 
87
84
  _registerOnHandlers() {
@@ -104,6 +101,7 @@ module.exports = class SQLiteDatabase extends DatabaseService {
104
101
  getDbUrl(tenant) {
105
102
  return this.url4(tenant)
106
103
  }
104
+
107
105
  url4(tenant) {
108
106
  const credentials = this.options.credentials || this.options || {}
109
107
  let dbUrl = credentials.database || credentials.url || credentials.host || ':memory:'
@@ -1,4 +1,4 @@
1
- const cds = require('../cds')
1
+ const normalizeTimestamp = require('../common/utils/normalizeTimestamp')
2
2
 
3
3
  const convertToBoolean = boolean => {
4
4
  if (boolean === null) return null
@@ -21,9 +21,9 @@ const convertInt64ToString = int64 => {
21
21
  const convertToISOTime = value => {
22
22
  if (value === null) return value
23
23
 
24
- if (!value) value = 0
24
+ if (!value) return normalizeTimestamp(new Date(0))
25
25
 
26
- return new Date(value).toISOString()
26
+ return normalizeTimestamp(value)
27
27
  }
28
28
 
29
29
  const convertToISONoMillis = element => {
@@ -44,14 +44,4 @@ const SQLITE_TYPE_CONVERSION_MAP = new Map([
44
44
  ['cds.Timestamp', convertToISOTime]
45
45
  ])
46
46
 
47
- if (cds.env.features.bigjs) {
48
- // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
49
- const Big = require('big.js')
50
- const convertToBig = value => new Big(value)
51
-
52
- SQLITE_TYPE_CONVERSION_MAP.set('cds.Integer64', convertToBig)
53
- SQLITE_TYPE_CONVERSION_MAP.set('cds.Int64', convertToBig)
54
- SQLITE_TYPE_CONVERSION_MAP.set('cds.Decimal', convertToBig)
55
- }
56
-
57
47
  module.exports = { SQLITE_TYPE_CONVERSION_MAP }
@@ -46,13 +46,16 @@ class CustomFunctionBuilder extends FunctionBuilder {
46
46
  default:
47
47
  break
48
48
  }
49
-
50
- this._outputObj.sql.push(functionName, '(')
51
- if (typeof args === 'string') {
52
- this._outputObj.sql.push(args, ')')
49
+ if (functionName === 'INSTR') {
50
+ super._handleIndexof(args, functionName)
53
51
  } else {
54
- this._addFunctionArgs(args)
55
- this._outputObj.sql.push(')')
52
+ this._outputObj.sql.push(functionName, '(')
53
+ if (typeof args === 'string') {
54
+ this._outputObj.sql.push(args, ')')
55
+ } else {
56
+ this._addFunctionArgs(args)
57
+ this._outputObj.sql.push(')')
58
+ }
56
59
  }
57
60
  }
58
61
 
@@ -2,6 +2,11 @@ const InsertBuilder = require('../../db/sql-builder').InsertBuilder
2
2
  const getAnnotatedColumns = require('../../db/sql-builder/annotations')
3
3
 
4
4
  class CustomUpsertBuilder extends InsertBuilder {
5
+ constructor(obj, options, csn) {
6
+ super(obj, options, csn)
7
+ this._UPSERT = true
8
+ }
9
+
5
10
  annotatedColumns(entityName, csn) {
6
11
  const { updateAnnotatedColumns } = getAnnotatedColumns(entityName, csn)
7
12
 
@@ -18,7 +23,7 @@ class CustomUpsertBuilder extends InsertBuilder {
18
23
  super.build()
19
24
  const csnKeys =
20
25
  (this._obj._target ? this._obj._target.keys : this._csn.definitions[this._obj.INSERT.into].keys) || {}
21
- const keys = Object.keys(csnKeys).filter(k => !csnKeys[k].isAssociation)
26
+ const keys = Object.keys(csnKeys).filter(k => !csnKeys[k].isAssociation && k !== 'IsActiveEntity')
22
27
  const updates = []
23
28
  const columns = this._obj.INSERT.columns || Object.keys(this._obj.INSERT.entries[0])
24
29
  if (this.managedCols) {
@@ -71,23 +71,9 @@ function executeSelectSQL(dbc, sql, values, isOne, postMapper) {
71
71
  return new Promise((resolve, reject) => {
72
72
  _exec(dbc, isOne ? 'get' : 'all', sql, values, (err, result) => {
73
73
  if (err) return reject(err)
74
-
75
- // REVISIT
76
- // .get returns undefined if nothing in db
77
- // our coding expects the result to be null if isOne does not return anything
78
- // REVISIT: -> we should definitely fix that coding which expects null
79
- if (isOne && result === undefined) {
80
- result = null
81
- }
82
-
83
- try {
84
- result = postProcess(result, postMapper)
85
- resolve(result)
86
- } catch (e) {
87
- reject(e)
88
- }
74
+ resolve(result)
89
75
  })
90
- })
76
+ }).then(result => postProcess(result, postMapper))
91
77
  }
92
78
 
93
79
  function _processExpand(model, dbc, cqn, user, locale, txTimestamp) {
@@ -162,6 +148,9 @@ const _executeBulkInsertSQL = (dbc, sql, values) =>
162
148
  const stmt = _exec(dbc, 'prepare', sql, err => {
163
149
  if (err) return reject(err)
164
150
 
151
+ // REVISIT: better?
152
+ if (dbc._closed) return reject(new Error('Transaction is already closed'))
153
+
165
154
  if (!Array.isArray(values[0])) values = [values]
166
155
 
167
156
  // guarantee order through counters in closure
@@ -2,6 +2,7 @@ const cds = require('../_runtime/cds')
2
2
 
3
3
  const { where2obj, resolveFromSelect } = require('../_runtime/common/utils/cqn')
4
4
  const { findCsnTargetFor } = require('../_runtime/common/utils/csn')
5
+ const normalizeTimestamp = require('../_runtime/common/utils/normalizeTimestamp')
5
6
 
6
7
  const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
7
8
  for (const keyName in keys) {
@@ -184,6 +185,9 @@ function _convertVal(element, value) {
184
185
  case 'cds.Boolean':
185
186
  return typeof value === 'string' ? value === 'true' : value
186
187
 
188
+ case 'cds.Timestamp':
189
+ return normalizeTimestamp(value)
190
+
187
191
  default:
188
192
  return value
189
193
  }
@@ -395,20 +399,32 @@ function _addKeys(columns, target) {
395
399
  }
396
400
  }
397
401
 
402
+ // remove duplicate * in expand (e.g. expand=*,*)
403
+ function _removeDuplicateAsterix(columns) {
404
+ let hasExpandStar = false
405
+ for (let i = columns.length - 1; i > 0; i--) {
406
+ const column = columns[i]
407
+ if (!hasExpandStar && !column.ref && column?.expand?.[0] === '*') hasExpandStar = true
408
+ else if (hasExpandStar && !column.ref && column?.expand[0] === '*') {
409
+ columns.splice(i, 1)
410
+ }
411
+ }
412
+ }
413
+
398
414
  function _processColumns(cqn, target) {
399
415
  if (cqn.SELECT.from.SELECT) _processColumns(cqn.SELECT.from, target)
400
416
 
401
- const columns = cqn.SELECT.columns
417
+ let columns = cqn.SELECT.columns
402
418
 
403
419
  // REVISIT Keys should be added only in case of odata, not e.g. rest.
404
420
  // Currently odata is detected via odata_new_parser flag -> find a better indicator.
405
- if (columns && !cqn.SELECT.groupBy && cds.env.features.odata_new_parser) {
421
+ if (columns && !cqn.SELECT.groupBy) {
406
422
  let entity
407
423
  if (target.kind === 'entity') entity = target
408
424
  else if (target.kind === 'action' && target.returns?.kind === 'entity') entity = target.returns
409
425
  if (!entity) return
410
-
411
- _addKeys(columns, entity)
426
+ _removeDuplicateAsterix(columns)
427
+ if (cds.env.features.odata_new_parser) _addKeys(columns, entity)
412
428
  }
413
429
 
414
430
  if (!Array.isArray(columns)) return
@@ -514,7 +530,7 @@ function _4service(service) {
514
530
 
515
531
  // REVISIT: better
516
532
  // set target (csn definition) for later retrieval
517
- cqn.__target = current
533
+ cqn.__target = current.parent?.kind === 'entity' ? `${current.parent.name}:$:${current.name}` : current.name
518
534
 
519
535
  // target <=> endpoint entity, all navigation refs must be resolvable accordingly
520
536
  if (cds.env.effective.odata.structs) _resolveAliasesInNavigation(cqn, target)
@@ -522,7 +538,7 @@ function _4service(service) {
522
538
  /*
523
539
  * add default aggregation function (and alias)
524
540
  */
525
- _processColumns(cqn, cqn.__target)
541
+ _processColumns(cqn, current)
526
542
 
527
543
  return cqn
528
544
  }
@@ -656,6 +656,8 @@
656
656
 
657
657
  val
658
658
  = val:(bool / date) {return {val}}
659
+ / val:time {return {val}}
660
+ / val:date {return {val}}
659
661
  / val:guid {return {val}}
660
662
  / val:number {return typeof val === 'number' ? {val} : { val, literal:'number' }}
661
663
  / val:string {return {val}}
@@ -846,6 +848,9 @@
846
848
  word "a string"
847
849
  = $([^ \t\n()"&;]+)
848
850
 
851
+ time "a time"
852
+ = $([0-9][0-9]":"[0-9][0-9]":"[0-9][0-9])
853
+
849
854
  date "a date"
850
855
  = s:$( [0-9]+"-"[0-9][0-9]"-"[0-9][0-9] // date
851
856
  ( "T"[0-9][0-9]":"[0-9][0-9](":"[0-9][0-9]("."[0-9]+)?)? // time
@@ -867,7 +872,7 @@
867
872
  = s:$( [+-]? [0-9]+ ) { return parseInt(s) }
868
873
 
869
874
  identifier "an identifier"
870
- = !bool !guid s:$([_a-zA-Z][_a-zA-Z0-9"."]*) { return s }
875
+ = !bool !guid s:$([_a-zA-Z][_a-zA-Z0-9"."\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]*) { return s }
871
876
 
872
877
  guid "a guid"
873
878
  = $( hex16 hex16 "-"? hex16 "-"? hex16 "-"? hex16 "-"? hex16 hex16 hex16 )