@sap/cds 6.8.4 → 7.0.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 (217) hide show
  1. package/CHANGELOG.md +66 -4
  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 +3 -3
  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 +2 -2
  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/protocols/odata-v4.js +9 -4
  45. package/lib/srv/srv-api.js +9 -3
  46. package/lib/srv/srv-dispatch.js +12 -9
  47. package/lib/srv/srv-models.js +4 -21
  48. package/lib/srv/srv-tx.js +15 -12
  49. package/lib/utils/cds-test.js +14 -9
  50. package/lib/utils/cds-utils.js +2 -12
  51. package/lib/utils/check-version.js +17 -0
  52. package/{bin/build → lib/utils}/csv-reader.js +23 -24
  53. package/libx/_runtime/auth/index.js +27 -23
  54. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
  55. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  56. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
  57. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
  58. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
  59. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
  61. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
  62. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
  63. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
  64. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
  65. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
  66. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
  68. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
  70. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
  71. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
  72. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
  73. package/libx/_runtime/cds-services/services/Service.js +79 -107
  74. package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
  75. package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
  76. package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
  77. package/libx/_runtime/cds-services/util/assert.js +65 -2
  78. package/libx/_runtime/common/composition/data.js +1 -0
  79. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  80. package/libx/_runtime/common/generic/auth/restrict.js +5 -10
  81. package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
  82. package/libx/_runtime/common/generic/auth/utils.js +1 -2
  83. package/libx/_runtime/common/generic/crud.js +32 -16
  84. package/libx/_runtime/common/generic/etag.js +133 -104
  85. package/libx/_runtime/common/generic/input.js +6 -21
  86. package/libx/_runtime/common/generic/put.js +1 -1
  87. package/libx/_runtime/common/generic/stream.js +52 -0
  88. package/libx/_runtime/common/generic/temporal.js +25 -8
  89. package/libx/_runtime/common/i18n/messages.properties +0 -2
  90. package/libx/_runtime/common/utils/cqn.js +1 -1
  91. package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
  92. package/libx/_runtime/common/utils/csn.js +0 -51
  93. package/libx/_runtime/common/utils/etag.js +30 -0
  94. package/libx/_runtime/common/utils/keys.js +1 -1
  95. package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
  96. package/libx/_runtime/common/utils/path.js +1 -1
  97. package/libx/_runtime/common/utils/resolveView.js +2 -1
  98. package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
  99. package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
  100. package/libx/_runtime/common/utils/stream.js +140 -0
  101. package/libx/_runtime/common/utils/streamProp.js +29 -12
  102. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
  103. package/libx/_runtime/db/generic/index.js +0 -2
  104. package/libx/_runtime/db/query/delete.js +2 -2
  105. package/libx/_runtime/db/query/insert.js +2 -2
  106. package/libx/_runtime/db/query/read.js +2 -2
  107. package/libx/_runtime/db/query/run.js +2 -2
  108. package/libx/_runtime/db/query/update.js +2 -2
  109. package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
  110. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
  111. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
  112. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
  113. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
  114. package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
  115. package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
  116. package/libx/_runtime/fiori/draft.js +2 -0
  117. package/libx/_runtime/fiori/generic/activate.js +8 -9
  118. package/libx/_runtime/fiori/generic/before.js +30 -20
  119. package/libx/_runtime/fiori/generic/cancel.js +5 -3
  120. package/libx/_runtime/fiori/generic/delete.js +5 -3
  121. package/libx/_runtime/fiori/generic/edit.js +7 -7
  122. package/libx/_runtime/fiori/generic/index.js +10 -16
  123. package/libx/_runtime/fiori/generic/new.js +5 -3
  124. package/libx/_runtime/fiori/generic/patch.js +11 -8
  125. package/libx/_runtime/fiori/generic/prepare.js +13 -6
  126. package/libx/_runtime/fiori/generic/read.js +12 -6
  127. package/libx/_runtime/fiori/lean-draft.js +207 -152
  128. package/libx/_runtime/fiori/utils/delete.js +10 -5
  129. package/libx/_runtime/fiori/utils/req.js +17 -5
  130. package/libx/_runtime/fiori/utils/stream.js +36 -0
  131. package/libx/_runtime/hana/Service.js +12 -9
  132. package/libx/_runtime/hana/conversion.js +10 -15
  133. package/libx/_runtime/hana/driver.js +2 -0
  134. package/libx/_runtime/hana/execute.js +28 -6
  135. package/libx/_runtime/hana/pool.js +36 -122
  136. package/libx/_runtime/hana/search2cqn4sql.js +34 -36
  137. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
  138. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
  139. package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
  140. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  141. package/libx/_runtime/remote/Service.js +20 -1
  142. package/libx/_runtime/remote/utils/client.js +3 -5
  143. package/libx/_runtime/sqlite/Service.js +4 -6
  144. package/libx/_runtime/sqlite/conversion.js +3 -13
  145. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
  146. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
  147. package/libx/_runtime/sqlite/execute.js +5 -16
  148. package/libx/odata/afterburner.js +22 -6
  149. package/libx/odata/grammar.pegjs +6 -1
  150. package/libx/odata/parser.js +1 -1
  151. package/libx/rest/RestAdapter.js +16 -9
  152. package/libx/rest/RestRequest.js +1 -1
  153. package/libx/rest/middleware/input.js +2 -1
  154. package/libx/rest/middleware/operation.js +1 -0
  155. package/libx/rest/middleware/parse.js +3 -2
  156. package/libx/rest/middleware/payload.js +9 -8
  157. package/libx/rest/middleware/read.js +1 -0
  158. package/package.json +9 -16
  159. package/server.js +1 -1
  160. package/app/fiori/preview.js +0 -270
  161. package/app/fiori/routes.js +0 -59
  162. package/bin/build/buildTaskEngine.js +0 -360
  163. package/bin/build/buildTaskFactory.js +0 -283
  164. package/bin/build/buildTaskHandler.js +0 -241
  165. package/bin/build/buildTaskProvider.js +0 -22
  166. package/bin/build/buildTaskProviderFactory.js +0 -175
  167. package/bin/build/cds.js +0 -5
  168. package/bin/build/constants.js +0 -66
  169. package/bin/build/index.js +0 -58
  170. package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
  171. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
  172. package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
  173. package/bin/build/provider/buildTaskProviderInternal.js +0 -383
  174. package/bin/build/provider/fiori/index.js +0 -171
  175. package/bin/build/provider/hana/2migration.js +0 -179
  176. package/bin/build/provider/hana/index.js +0 -505
  177. package/bin/build/provider/hana/migrationtable.js +0 -472
  178. package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
  179. package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
  180. package/bin/build/provider/hana/template/.hdinamespace +0 -4
  181. package/bin/build/provider/hana/template/package.json +0 -12
  182. package/bin/build/provider/hana/template/undeploy.json +0 -5
  183. package/bin/build/provider/java/index.js +0 -111
  184. package/bin/build/provider/java-cf/index.js +0 -1
  185. package/bin/build/provider/mtx/index.js +0 -268
  186. package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
  187. package/bin/build/provider/mtx-extension/index.js +0 -131
  188. package/bin/build/provider/mtx-sidecar/index.js +0 -137
  189. package/bin/build/provider/node-cf/index.js +0 -1
  190. package/bin/build/provider/nodejs/index.js +0 -192
  191. package/bin/build/util.js +0 -299
  192. package/bin/cds.js +0 -125
  193. package/bin/deploy/to-hana/cfUtil.js +0 -355
  194. package/bin/deploy/to-hana/gitUtil.js +0 -57
  195. package/bin/deploy/to-hana/hana.js +0 -306
  196. package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
  197. package/bin/deploy/to-hana/index.js +0 -16
  198. package/bin/deploy/to-hana/mtaUtil.js +0 -170
  199. package/bin/mtx/in-cds.js +0 -17
  200. package/bin/plugins.js +0 -32
  201. package/bin/run.js +0 -24
  202. package/bin/utils/log.js +0 -24
  203. package/bin/version.js +0 -178
  204. package/libx/_runtime/audit/Service.js +0 -222
  205. package/libx/_runtime/audit/generic/personal/access.js +0 -61
  206. package/libx/_runtime/audit/generic/personal/index.js +0 -56
  207. package/libx/_runtime/audit/generic/personal/modification.js +0 -132
  208. package/libx/_runtime/audit/generic/personal/utils.js +0 -186
  209. package/libx/_runtime/audit/utils/log.js +0 -23
  210. package/libx/_runtime/audit/utils/v2.js +0 -176
  211. package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
  212. package/libx/_runtime/db/generic/integrity.js +0 -455
  213. package/srv/audit-log.cds +0 -87
  214. package/srv/mtx.cds +0 -2
  215. package/srv/mtx.js +0 -8
  216. /package/lib/{core → linked}/classes.js +0 -0
  217. /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