@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
@@ -1,270 +0,0 @@
1
- const cds = require('../../lib')
2
- cds.on('served', ()=>{
3
-
4
- const { app, env, service:{providers} } = cds
5
-
6
- const mountPoint = '/$fiori-preview'
7
- const appID = 'preview-app'
8
- const _appURL = (srv, entity) => `${mountPoint}/${srv}/${entity}#${appID}`
9
- const _componentURL = (srv, entity) => `${mountPoint}/${srv}/${entity}/app`
10
-
11
- function _manifest(serviceName, entityName) {
12
- const [serviceProv, serviceInfo] = _validate(serviceName, entityName)
13
- const listPageInitialLoad = (env.preview && env.preview.fiori && env.preview.fiori.initialload !== undefined)
14
- ? env.preview.fiori.initialload
15
- : true
16
- const manifest = {
17
- _version: '1.8.0',
18
- 'sap.app': {
19
- id: 'preview',
20
- type: 'application',
21
- title: `Preview ‒ List of ${serviceProv.name}.${entityName}`,
22
- description: 'Preview Application',
23
- dataSources: {
24
- mainService: {
25
- uri: `${serviceProv.path}/`,
26
- type: 'OData',
27
- settings: {
28
- odataVersion: '4.0'
29
- }
30
- }
31
- },
32
- },
33
- 'sap.ui5': {
34
- flexEnabled: true,
35
- dependencies: {
36
- minUI5Version: '1.96.0',
37
- libs: {
38
- 'sap.ui.core': {},
39
- 'sap.fe.templates': {}
40
- }
41
- },
42
- models: {
43
- '': {
44
- dataSource: 'mainService',
45
- settings: {
46
- synchronizationMode: 'None',
47
- operationMode: 'Server',
48
- autoExpandSelect: true,
49
- earlyRequests: true,
50
- groupProperties: {
51
- default: {
52
- submit: 'Auto'
53
- }
54
- }
55
- }
56
- }
57
- },
58
- routing: {
59
- routes: [
60
- {
61
- name: `${entityName}ListRoute`,
62
- target: `${entityName}ListTarget`,
63
- pattern: ':?query:',
64
- },
65
- {
66
- name: `${entityName}DetailsRoute`,
67
- target: `${entityName}DetailsTarget`,
68
- pattern: `${entityName}({key}):?query:`,
69
- }
70
- ],
71
- targets: {
72
- [`${entityName}ListTarget`]: {
73
- type: 'Component',
74
- id: `${entityName}ListTarget`,
75
- name: 'sap.fe.templates.ListReport',
76
- options: {
77
- settings: {
78
- entitySet: `${entityName}`,
79
- initialLoad: listPageInitialLoad,
80
- navigation: {
81
- [`${entityName}`]: {
82
- detail: {
83
- route: `${entityName}DetailsRoute`
84
- }
85
- }
86
- }
87
- }
88
- }
89
- },
90
- [`${entityName}DetailsTarget`]: {
91
- type: 'Component',
92
- id: `${entityName}DetailsTarget`,
93
- name: 'sap.fe.templates.ObjectPage',
94
- options: {
95
- settings: {
96
- entitySet: `${entityName}`,
97
- navigation: {}
98
- }
99
- }
100
- }
101
- }
102
- },
103
- },
104
- contentDensities: {
105
- compact: true,
106
- cozy: true
107
- },
108
- 'sap.ui': {
109
- technology: 'UI5',
110
- fullWidth: true,
111
- deviceTypes: {
112
- desktop: true,
113
- tablet: true,
114
- phone: true
115
- }
116
- },
117
- 'sap.fiori': {
118
- registrationIds: [],
119
- archeType: 'transactional'
120
- },
121
- }
122
-
123
- const { routing } = manifest['sap.ui5']
124
- for (const {navProperty, targetEntity} of serviceInfo) {
125
- // add a route for the navigation property
126
- routing.routes.push(
127
- {
128
- name: `${navProperty}Route`,
129
- target: `${navProperty}Target`,
130
- pattern: `${entityName}({key})/${navProperty}({key2}):?query:`,
131
- }
132
- )
133
- // add a route target leading to the target entity
134
- routing.targets[`${navProperty}Target`] = {
135
- type: 'Component',
136
- id: `${navProperty}Target`,
137
- name: 'sap.fe.templates.ObjectPage',
138
- options: {
139
- settings: {
140
- entitySet: targetEntity
141
- }
142
- }
143
- }
144
- // wire the new route from the source entity's navigation (see above)
145
- routing.targets[`${entityName}DetailsTarget`].options.settings.navigation[navProperty] = {
146
- detail: {
147
- route: `${navProperty}Route`
148
- }
149
- }
150
- }
151
-
152
- return manifest
153
- }
154
-
155
- function _html(serviceName, entityName) {
156
- _validate(serviceName, entityName)
157
- let ui5Version = (env.preview && env.preview.ui5 && env.preview.ui5.version) || ''
158
- let ui5Host = (env.preview && env.preview.ui5 && env.preview.ui5.host) || `https://sapui5.hana.ondemand.com/${ui5Version}`
159
- if (!ui5Host.endsWith('/')) ui5Host += '/'
160
-
161
- // copied from UI5's test-resources/sap/ushell/shells/sandbox/fioriSandbox.html
162
- return `
163
- <!DOCTYPE html>
164
- <html>
165
- <head>
166
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
167
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
168
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
169
- <title>Preview for ${serviceName}.${entityName}</title>
170
- <script>
171
- window["sap-ushell-config"] = {
172
- defaultRenderer: "fiori2",
173
- applications: {
174
- "${appID}": {
175
- title: "Browse ${entityName}",
176
- description: "from ${serviceName}",
177
- additionalInformation: "SAPUI5.Component=app",
178
- applicationType : "URL",
179
- url: "${_componentURL(serviceName, entityName)}",
180
- navigationMode: "embedded"
181
- }
182
- }
183
- }
184
- </script>
185
- <script id="sap-ushell-bootstrap" src="${ui5Host}test-resources/sap/ushell/bootstrap/sandbox.js"></script>
186
- <script id="sap-ui-bootstrap" src="${ui5Host}resources/sap-ui-core.js"
187
- data-sap-ui-theme="sap_horizon"
188
- data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
189
- data-sap-ui-compatVersion="edge"
190
- data-sap-ui-async="true"
191
- data-sap-ui-preload="async"></script>
192
- <script>
193
- sap.ui.getCore().attachInit(function() { sap.ushell.Container.createRenderer().placeAt("content") })
194
- </script>
195
- </head>
196
- <body class="sapUiBody sapUiSizeCompact" id="content"></body>
197
- </html>
198
- `
199
- }
200
-
201
- function _componentJs(serviceName, entityName) {
202
- const manifest = _manifest(serviceName, entityName)
203
- return `sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
204
- "use strict";
205
- return AppComponent.extend("preview.Component", {
206
- metadata: { manifest: ${JSON.stringify(manifest, null, 2)} }
207
- });
208
- });`
209
- }
210
-
211
- function _validate(serviceName, entityName) {
212
- const serviceProv = providers.find (s => s.name === serviceName)
213
- if (!serviceProv) throw _badRequest (`No such service '${serviceName}'. Available: [${providers.map(p => p.name)}]`)
214
- return _serviceInfo (serviceProv, entityName)
215
- }
216
-
217
- function _serviceInfo (serviceProv, entityName) {
218
- const entities = serviceProv.model.entities(serviceProv.name)
219
- const entity = entities[entityName]
220
- if (!entity) throw _badRequest (`No such entity '${entityName}' in service '${serviceProv.name}'`)
221
- return [serviceProv, serviceProv.model.all ('Association', entity.elements)
222
- .filter (a =>
223
- !a.target.endsWith('.texts') &&
224
- !a.target.endsWith('_texts') &&
225
- !a.target.endsWith('DraftAdministrativeData') &&
226
- a.name !== 'SiblingEntity')
227
- .map (a => { return { navProperty: a.name, targetEntity: a.target.split('.')[1] } })
228
- ]
229
- }
230
-
231
- const _badRequest = (message) => { const err = new Error (message); err.statusCode = 400; return err}
232
-
233
-
234
- // fetch and instrument all OData providers
235
- const any = providers.filter (srv =>
236
- srv._adapters && srv._adapters [Object.keys(srv._adapters) .find (a => a.startsWith ('odata'))]
237
- )
238
- .map(srv => {
239
- // called from ../index.js to provide the data for the HTML link
240
- const link = linkProvider(srv)
241
- srv.$linkProviders ? srv.$linkProviders.push (link) : srv.$linkProviders = [link]
242
- return link
243
- })
244
- .length
245
-
246
-
247
- // install middlewares once
248
- if (any) {
249
- // eslint-disable-next-line cds/no-missing-dependencies
250
- const router = require('express').Router()
251
- // UI5 component
252
- router.get ('/:service/:entity/app/Component.js', ({ params }, resp) => resp.send(_componentJs(params.service, params.entity)))
253
- // html
254
- router.get ('/:service/:entity', ({ params }, resp) => resp.send(_html(params.service, params.entity)))
255
-
256
- app.use(mountPoint.replace('$','\\$'), router)
257
- }
258
-
259
- function linkProvider(service) {
260
- return (entity) => {
261
- if (!entity) return
262
- return {
263
- href: _appURL(service.name, entity),
264
- title: 'Preview in Fiori elements',
265
- name: 'Fiori preview'
266
- }
267
- }
268
- }
269
-
270
- })
@@ -1,59 +0,0 @@
1
- const cds = require('../../lib')
2
- const DEBUG = cds.debug('fiori/routes')
3
- const {dirname, relative, join} = require('path')
4
-
5
- // Only for local cds runs w/o approuter:
6
- // If there is a relative URL in UI5's manifest.json for the datasource,
7
- // like 'browse/' or 'prefix/browse/', we get called with a prefix to the
8
- // service path, like '/browse/webapp/browse/'.
9
- // Serve these requests by redirecting to the actual service URL.
10
- cds.on ('bootstrap', app => {
11
-
12
- const { env, utils:{find,fs}} = cds
13
- const v2Prefix = (env.odata.v2proxy && env.odata.v2proxy.urlpath) || '/v2'
14
- const serviceForUri = {}
15
-
16
- dataSourceURIs (env.folders.app).forEach(({appPath, dataSourceUri}) => {
17
- const uiRoutes = [
18
- join('/', appPath, dataSourceUri, '*'), // /uiApp/webapp/browse/*
19
- join('/', appPath, '*', dataSourceUri, '*') // /uiApp/webapp/*/browse/*
20
- ].map(r => r.replace(/\\/g, '/')) // handle Windows \
21
- DEBUG && DEBUG ('Register routes', uiRoutes)
22
-
23
- app.use(uiRoutes, ({originalUrl}, res, next)=> {
24
- // any of our special URLs ($fiori-, $api-docs) ? -> next
25
- if (originalUrl.startsWith('/$')) return next()
26
-
27
- // is there a service for '[prefix]/browse' ?
28
- const srv = serviceForUri[dataSourceUri] || (serviceForUri[dataSourceUri] =
29
- cds.service.providers.find (srv => ('/'+dataSourceUri).lastIndexOf(srv.path) >=0))
30
- if (srv) {
31
- let redirectUrl
32
- // odata-proxy may be in the line with its /v2 prefix. Make sure we retain it.
33
- const v2Index = originalUrl.lastIndexOf(v2Prefix+srv.path)
34
- if (v2Index >= 0) // --> /browse/webapp[/prefix]/v2/browse/ -> /v2/browse
35
- redirectUrl = originalUrl.substring(v2Index)
36
- else // --> /browse/webapp[/prefix]/browse/ -> /browse
37
- redirectUrl = originalUrl.substring(originalUrl.lastIndexOf(srv.path+'/'))
38
- if (originalUrl !== redirectUrl) {// safeguard to prevent running in loops
39
- DEBUG && DEBUG ('Redirecting', {src: originalUrl}, '~>', {target: redirectUrl})
40
- return res.redirect (308, redirectUrl)
41
- }
42
- }
43
- next()
44
- })
45
- })
46
-
47
- function dataSourceURIs (dir) {
48
- const uris = new Set()
49
- find (dir, ['*/manifest.json', '*/*/manifest.json']).forEach(file => {
50
- const appPath = relative(join(cds.root, dir), dirname(file))
51
- const {dataSources: ds} = JSON.parse(fs.readFileSync(file))['sap.app'] || {}
52
- Object.keys (ds||[])
53
- .filter (k => ds[k].uri && !ds[k].uri.startsWith('/')) // only consider relative URLs)
54
- .forEach(k => uris.add({ appPath, dataSourceUri: ds[k].uri }))
55
- })
56
- return uris
57
- }
58
-
59
- })
@@ -1,360 +0,0 @@
1
- const fs = require('fs')
2
- const path = require('path')
3
- const cds = require('./cds'), { log } = cds.exec
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')
7
- const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
8
- const BuildTaskHandlerInternal = require('./provider/buildTaskHandlerInternal')
9
-
10
- const COMPILATION_ERROR = 'CompilationError'
11
- const COMPILE_MESSAGE = 'CompileMessage'
12
-
13
- class BuildTaskEngine {
14
- constructor(logger) {
15
- this._logger = logger || cds.log(LOG_MODULE_NAMES)
16
- }
17
- get logger() {
18
- return this._logger
19
- }
20
-
21
- async processTasks(tasks, buildOptions, clean = true) {
22
- const startTime = Date.now()
23
- const messages = []
24
-
25
- if (buildOptions) {
26
- // clone as data may be stored as part of the buildOptions object
27
- buildOptions = JSON.parse(JSON.stringify(buildOptions))
28
- } else {
29
- buildOptions = {
30
- root: process.env._TEST_CWD || process.cwd()
31
- }
32
- }
33
- if (!buildOptions.outputMode) {
34
- buildOptions.outputMode = OUTPUT_MODE_DEFAULT
35
- }
36
-
37
- this.logger.log(`building project [${buildOptions.root}], clean [${clean}]`)
38
- this.logger.log(`cds [${cds.version}], compiler [${cds.compiler.version()}], home [${cds.home}]\n`)
39
-
40
- if (!buildOptions.target) {
41
- buildOptions.target = path.resolve(buildOptions.root, cds.env.build.target)
42
- }
43
-
44
- // validate required @sap namespace models - log only
45
- const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
46
- if (unresolved.length > 0) {
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
- }
49
-
50
- // create build task handlers
51
- const handlers = []
52
- tasks.forEach((task) => {
53
- if (task) {
54
- const handler = this._createHandler(task, buildOptions)
55
- handlers.push(handler)
56
- }
57
- })
58
-
59
- // use resolved tasks
60
- buildOptions.tasks = handlers.map(handler => handler.task)
61
-
62
- try {
63
- await this._executePrepare(handlers, buildOptions)
64
- await this._executeCleanBuildTasks(handlers, buildOptions, clean)
65
-
66
- // throwing Exception in case of compilation errors
67
- const buildResult = await this._executeBuildTasks(handlers, buildOptions)
68
-
69
- await this._writeGenerationLog(handlers, buildOptions)
70
- this._logBuildOutput(handlers, buildOptions)
71
- this._logMessages(buildOptions, [...BuildTaskEngine._getHandlerMessages(handlers), ...messages])
72
- this._logTimer(startTime, Date.now())
73
-
74
- return buildResult
75
- } catch (error) {
76
- this._logBuildOutput(handlers, buildOptions)
77
-
78
- // cds CLI layer logs in case of an exception if invoked from CLI
79
- if (!buildOptions.cli) {
80
- this._logMessages(buildOptions, [...BuildTaskEngine._getErrorMessages([error]), ...messages])
81
- }
82
-
83
- if (error.name === BuildError.name && error.errors.length > 0 && error.errors[0].constructor.name === COMPILATION_ERROR) {
84
- // TODO: for compatibility reasons with cds-mtx we're throwing the actual cause of type CompiliationError
85
- // Note: As a consequence we are loosing any info or warning messages issued by build task handlers
86
- throw error.errors[0]
87
- }
88
- throw error
89
- }
90
- }
91
-
92
- /**
93
- * BuildTaskHandler#prepare has been deprecated and was never part of the public API.
94
- * Currently only used by internal FioriBuildTaskHandller.
95
- * @deprecated
96
- * @param {*} handlers
97
- * @returns
98
- */
99
- async _executePrepare(handlers, buildOptions) {
100
- const handlerGroups = new Map()
101
-
102
- // group handlers by type
103
- handlers.forEach(handler => {
104
- handlerGroups.has(handler.task.for) ? handlerGroups.get(handler.task.for).push(handler) : handlerGroups.set(handler.task.for, [handler])
105
- })
106
-
107
- const promises = []
108
- for (let handlerGroup of handlerGroups.values()) {
109
- promises.push(this._doPrepare(handlerGroup, buildOptions))
110
- }
111
- return Promise.all(promises)
112
- }
113
-
114
- /**
115
- * @deprecated
116
- * @param {*} handlerGroup
117
- */
118
- async _doPrepare(handlerGroup, buildOptions) {
119
- for (let handler of handlerGroup) {
120
- // prepare has been deprecated
121
- if (handler instanceof BuildTaskHandlerInternal) {
122
- this.logger._debug && this.logger.debug(`preparing, handler [${handler.constructor.name}], src [${relativePaths(buildOptions.root, handler.task.src)}]`)
123
- const result = await handler.prepare()
124
- if (result === false) {
125
- break
126
- }
127
- }
128
- }
129
- }
130
-
131
- async _executeCleanBuildTasks(handlers, buildOptions, clean) {
132
- if (clean) {
133
- // clean entire build staging folder once
134
- if (buildOptions.target !== buildOptions.root) {
135
- this.logger._debug && this.logger.debug(`cleaning staging folder ${buildOptions.target}`)
136
- await fs.promises.rm(buildOptions.target, { force: true, recursive: true })
137
- }
138
-
139
- const results = await Promise.allSettled(handlers.map((handler) => {
140
- this.logger._debug && this.logger.debug(`cleaning, handler [${handler.constructor.name}], src [${relativePaths(buildOptions.root, handler.task.src)}]`)
141
- return handler.clean()
142
- }))
143
- // check for errors and throw exception
144
- BuildTaskEngine._resolveHandlerResponse(results, buildOptions)
145
- }
146
- }
147
-
148
- async _executeBuildTasks(handlers, buildOptions) {
149
- // sort handlers based on priority in
150
- handlers = handlers.sort((a, b) => {
151
- return a.priority === b.priority ? 0 : a.priority > b.priority ? -1 : 1
152
- })
153
- // group handlers with same priority in order to execute in parallel
154
- const buildPipeline = handlers.reduce((acc, handler) => {
155
- if (acc.length === 0) {
156
- acc.push([handler])
157
- } else {
158
- const currGroup = acc[acc.length - 1]
159
- if (currGroup[0].priority === handler.priority) {
160
- currGroup.push(handler)
161
- } else {
162
- acc.push([handler])
163
- }
164
- }
165
- return acc
166
- }, [])
167
-
168
- const results = await this._executePipeline(buildOptions, buildPipeline)
169
-
170
- // check for errors and throw exception - return results otherwise including any compiler and build status messages
171
- return BuildTaskEngine._resolveHandlerResponse(results, buildOptions, BuildTaskEngine._getHandlerMessages(handlers))
172
- }
173
-
174
- async _executePipeline(buildOptions, pipeline) {
175
- let allResults = []
176
- for (const group of pipeline) {
177
- const results = await Promise.allSettled(group.map((handler) => {
178
- this.logger._debug && this.logger.debug(`building, handler [${handler.constructor.name}], src [${relativePaths(buildOptions.root, handler.task.src)}]`)
179
- return handler.build()
180
- .then(handlerResult => {
181
- return Promise.resolve({
182
- task: handler.task,
183
- result: handlerResult,
184
- messages: BuildTaskEngine._sortMessagesUnique(buildOptions, handler.messages)
185
- })
186
- })
187
- }))
188
- allResults = allResults.concat(results)
189
- }
190
- return allResults
191
- }
192
-
193
- static _resolveHandlerResponse(results, buildOptions, handlerMessages = []) {
194
- const errors = []
195
- const resolvedResults = results.reduce((acc, r) => {
196
- if (r.status === 'fulfilled') {
197
- acc.push(r.value)
198
- }
199
- if (r.status === 'rejected' && r.reason) {
200
- errors.push(r.reason)
201
- }
202
- return acc
203
- }, [])
204
-
205
- if (errors.length > 0) {
206
- const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
207
- if (error) {
208
- // throw original error, including BuildErrors
209
- // for BuildErrors we do not merge other build messages that might exist in order to keep the original error intact
210
- throw error
211
- }
212
-
213
- // propagate existing CompilationErrors
214
- // merge all existing compilation messages into a single CompilationError
215
- // compiler warning and info messages are returned as handler messages
216
- const compileErrors = errors.filter(e => e.constructor.name === COMPILATION_ERROR)
217
- const compileMessages = handlerMessages.filter(message => message.constructor.name === COMPILE_MESSAGE)
218
- if (compileErrors.length) {
219
- throw new CompilationError(BuildTaskEngine._sortMessagesUnique(buildOptions, BuildTaskEngine._getErrorMessages(compileErrors), compileMessages))
220
- }
221
- }
222
- return resolvedResults
223
- }
224
-
225
- _createHandler(task, buildOptions) {
226
- try {
227
- const providerFactory = new BuildTaskProviderFactory(this._logger, buildOptions)
228
- const handler = providerFactory.createHandler(task)
229
- handler.init()
230
-
231
- if (!(handler instanceof BuildTaskHandlerInternal) && handler.priority !== 1) {
232
- // Custom build handlers are executed before internal handlers to ensure
233
- // that generated content cannot be overwriten by mistake
234
- throw new Error(`Illegal priority for ${handler.consructor.name} encountered for custom handler - in this version only priority value '1' is allowed`)
235
- }
236
- this._logTaskHandler(handler, buildOptions)
237
-
238
- return handler
239
- } catch (error) {
240
- log(error, { log: this.logger.log })
241
- throw error
242
- }
243
- }
244
-
245
- _logBuildOutput(handlers, buildOptions) {
246
- // log all generated files
247
- const files = BuildTaskEngine._getBuildOutput(handlers, buildOptions)
248
- if (files.length > 0) {
249
- this.logger.log(`done > wrote output to:\n ${files.join("\n ")}\n`)
250
- }
251
- }
252
-
253
- async _writeGenerationLog(handlers, buildOptions) {
254
- const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
255
- if (outputFile) {
256
- const files = BuildTaskEngine._getBuildOutput(handlers, buildOptions)
257
- this.logger.log(`writing generation log to [${outputFile}]\n`)
258
- try {
259
- await fs.promises.mkdir(path.dirname(outputFile), { recursive: true }).then(() => fs.promises.writeFile(outputFile, files.join('\n')))
260
- } catch (error) {
261
- this.logger.error(`failed to write generation log`)
262
- this.logger.error(error.stack || error)
263
- }
264
- }
265
- }
266
-
267
- static _getBuildOutput(handlers, buildOptions) {
268
- const files = handlers.reduce((acc, handler) => acc.concat(handler.files), []).sort()
269
- return files.map(file => path.relative(buildOptions.root, file))
270
- }
271
-
272
- _logTimer(start, end) {
273
- this.logger.log(`build completed in ${end - start} ms\n`)
274
- }
275
-
276
- _logTaskHandler(handler, buildOptions) {
277
- this.logger._debug && this.logger.debug(`handler ${handler.constructor.name}`)
278
- this.logger._debug && this.logger.debug(`details src [${relativePaths(buildOptions.root, handler.task.src)}], dest [${relativePaths(buildOptions.root, handler.task.dest)}], use [${handler.task.use}], options [${JSON.stringify(handler.task.options)}]`) //NOSONAR
279
- }
280
-
281
- _logMessages(buildOptions, messages) {
282
- if (messages.length > 0) {
283
- const options = {
284
- log: this.logger.log,
285
- "log-level": BuildTaskEngine._getLogLevel(buildOptions) // ensures that for tests the correct cds.env is used
286
- }
287
- deduplicateMessages(messages)
288
- log(messages, options)
289
- }
290
- }
291
-
292
- static _resolveRequiredSapServices(tasks) {
293
- const taskModelPaths = tasks.reduce((acc, task) => {
294
- const model = task.options?.model
295
- if (model) {
296
- if (Array.isArray(model)) {
297
- model.forEach(m => acc.add(m))
298
- } else {
299
- acc.add(model)
300
- }
301
- }
302
- return acc
303
- }, new Set())
304
-
305
- return resolveRequiredSapModels([...taskModelPaths])
306
- }
307
-
308
- /**
309
- * Returns a sorted and flattened list of all messages extracted from the given errors.
310
- * @param {Array<Error>} errors
311
- */
312
- static _getErrorMessages(errors) {
313
- let messages = []
314
- // flatten all compile messages in order to filter duplicates and sort later on
315
- errors.forEach(error => {
316
- if (Array.isArray(error.errors) && error.errors.length > 0) {
317
- messages = messages.concat(BuildTaskEngine._getErrorMessages(error.errors))
318
- } else {
319
- messages.push(error)
320
- }
321
- })
322
- return messages
323
- }
324
-
325
- /**
326
- * Returns compiler messages and validation messages issued by handlers.
327
- * @param {Array<BuildTaskHandler>} handlers
328
- */
329
- static _getHandlerMessages(handlers) {
330
- return handlers.reduce((acc, handler) => acc.concat(handler.messages), [])
331
- }
332
-
333
- /**
334
- * Sort and filter the given errors of type CompileMessage or BuildMessage according to their severity and location,
335
- * but leave any other errors untouched as part of the result array.<br>
336
- * The log level passed as 'buildOptions.log-level' or defined in 'cds.env' is used to filter the given messages.
337
- * @param {object} buildOptions
338
- * @param {...Error} messages
339
- */
340
- static _sortMessagesUnique(buildOptions, ...messages) {
341
- const logLevelIdx = LOG_LEVELS.indexOf(this._getLogLevel(buildOptions))
342
- // flatten
343
- messages = messages.reduce((acc, m) => acc.concat(m), [])
344
- // filter according to log-level
345
- const filteredMessages = messages.filter(message => !message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity))
346
- // remove duplicates
347
- deduplicateMessages(filteredMessages)
348
- // sort
349
- return sortMessagesSeverityAware(filteredMessages)
350
- }
351
-
352
- /**
353
- * Return user defined log level or default value 'warn'
354
- * @param {string} buildOptions
355
- */
356
- static _getLogLevel(buildOptions) {
357
- return buildOptions["log-level"] || cds.env["log-level"]
358
- }
359
- }
360
- module.exports = BuildTaskEngine