@sap/cds 7.0.2 → 7.0.3

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@
4
4
  - The format is based on [Keep a Changelog](http://keepachangelog.com/).
5
5
  - This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ ## Version 7.0.3 - 2023-07-19
8
+
9
+ ### Fixed
10
+
11
+ - Compile for lean draft: do not add draft entity for external entities
12
+ - Rollback awaited in REST adapter
13
+ - `service.on('error')` handler invoked only once
14
+ - `SELECT.one.localized`
15
+ - `COPYFILE_DISABLE=1` is now set for building `tar` archives by default
16
+ - Actions of projection target are no longer accessible in linked models
17
+ - Batch execute model-less mass inputs when on `@sap/hana-client`
18
+ - Requests to `/<path>/webapp` return 404 for absolute `@path` specifications
19
+ - `cds compile --to serviceinfo` no longer returns paths w/ Windows `\` path characters
20
+
7
21
  ## Version 7.0.2 - 2023-07-06
8
22
 
9
23
  ### Fixed
@@ -106,7 +106,7 @@ module.exports = function cds_compile_for_lean_drafts(csn) {
106
106
  }
107
107
  for (const name in csn.definitions) {
108
108
  const def = csn.definitions[name]
109
- if (!_isDraft(def)) continue
109
+ if (!_isDraft(def) || def['@cds.external']) continue
110
110
  def.elements.IsActiveEntity.virtual = true
111
111
  def.elements.HasDraftEntity.virtual = true
112
112
  def.elements.HasActiveEntity.virtual = true
@@ -62,13 +62,17 @@ module.exports = (model, options={}) => {
62
62
 
63
63
  // the URL path that is *likely* effective at runtime
64
64
  function _url4 (p) {
65
- return normalize (p.replace(/^\/+/, '') + '/') //> /foo/bar -> foo/bar/
65
+ p = p.replace(/\\/g, '/') // handle Windows
66
+ .replace(/^\/+/, '') // strip leading
67
+ .replace(/\/+$/, '') // strip trailing
68
+ p += '/' // end with /
69
+ return p
66
70
  }
67
71
 
68
72
  function _javaPath (service) {
69
73
  const d = model.definitions[service.name]
70
74
  const path = d && d['@path'] ? d['@path'].replace(/^[^/]/, c => '/'+c) : service.name
71
- return join(javaPrefix, path).replace(/\\/g, '/')
75
+ return join(javaPrefix, path)
72
76
  }
73
77
 
74
78
  function _isNodeProject(root) {
@@ -25,7 +25,8 @@ class LinkedCSN extends any {
25
25
  /* else: */ any.prototype
26
26
  )
27
27
  if (p.key && !d.key && d.kind === 'element') Object.defineProperty (d,'key',{value:undefined}) //> don't propagate .key
28
- if (p.params && !d.params && d.kind === 'entity') Object.defineProperty (d,'params',{value:undefined}) //> don't propagate .key
28
+ if (p.params && !d.params && d.kind === 'entity') Object.defineProperty (d,'params',{value:undefined}) //> don't propagate .params
29
+ if (p.actions && !d.actions && d.kind === 'entity') Object.defineProperty (d,'actions',{value:undefined}) //> don't propagate .actions
29
30
  if (d.elements && d.elements.localized) Object.defineProperty (d,'texts',{value: defs [d.elements.localized.target] })
30
31
  try { return Object.setPrototypeOf(d,p) } //> link d to resolved proto
31
32
  catch(e) { //> cyclic proto error
package/lib/ql/SELECT.js CHANGED
@@ -17,6 +17,7 @@ module.exports = class Query extends Whereable {
17
17
  one: $((...x) => new this({one:true})._select_or_from(...x),{
18
18
  columns: (..._) => new this({one:true}).columns(..._),
19
19
  from: (..._) => new this({one:true}).from(..._),
20
+ localized: (..._) => new this({one:true,localized:true}).from(..._)
20
21
  }),
21
22
  from: $((..._) => new this().from(..._), {
22
23
  localized: (..._) => new this({localized:true}).from(..._)
@@ -83,11 +83,28 @@ class ProtocolAdapter {
83
83
  app.use (`/${path}`, before, adapter, after)
84
84
  }
85
85
 
86
+ if (!cds.env.features.serve_on_root) {
87
+ // log warning for changed path if $metadata is accessed
88
+ let logged = false
89
+ app.use(`*/${path}/\\$metadata`, (req,res,next) => {
90
+ if (!logged) {
91
+ const logger = cds.log('adapters')
92
+ logger._warn && logger.warn(`With @sap/cds version 7, the service path has changed to '${srv.path}'.
93
+ If you use SAP Fiori Elements, make sure to adapt the 'dataSources.uri' paths
94
+ in 'manifest.json' files accordingly. For more information, see the release notes at
95
+ https://cap.cloud.sap/docs/releases/jun23.`)
96
+ logged = true
97
+ }
98
+ next()
99
+ })
100
+ }
101
+
86
102
  prefix = this.protocols[p.kind].path
87
103
  prefix = prefix ? (prefix.endsWith('/') ? prefix : prefix + '/') : '/'
88
104
  path = prefix + path
89
105
  }
90
106
 
107
+ app.use (`${path}/webapp/`, (_,res) => res.sendStatus(404))
91
108
  DEBUG?.('app.use(', path, ', ... )')
92
109
  app.use (path, before, adapter, after)
93
110
  // REVISIT this doesn't handle multiple protocols correctly
package/lib/utils/tar.js CHANGED
@@ -102,7 +102,7 @@ exports.create = async (dir='.', ...args) => {
102
102
  args.push('.')
103
103
  }
104
104
 
105
- c = spawn ('tar', ['c', '-C', dir, ...args])
105
+ c = spawn ('tar', ['c', '-C', dir, ...args], { env: { COPYFILE_DISABLE: 1 }})
106
106
  }
107
107
 
108
108
  return {__proto__:c, // returning a thenable + fluent ChildProcess...
@@ -169,7 +169,7 @@ exports.extract = (archive, ...args) => ({
169
169
  to (...dest) {
170
170
  if (typeof dest === 'string') dest = _resolve(...dest)
171
171
  const input = typeof archive !== 'string' || archive == '-' ? '-' : _resolve(archive)
172
- const x = spawn('tar', ['xf', win(input), '-C', win(dest), ...args], { env: { COPYFILE_DISABLE: 1 }})
172
+ const x = spawn('tar', ['xf', win(input), '-C', win(dest), ...args])
173
173
  if (archive === '-') return x.stdin
174
174
  if (Buffer.isBuffer(archive)) archive = require('stream').Readable.from (archive)
175
175
  if (typeof archive !== 'string') archive.pipe (x.stdin)
@@ -112,6 +112,7 @@ const getErrorHandler = (crashOnError = true, srv) => {
112
112
  if ('_data' in err && !('data' in req)) req.data = err._data
113
113
  }
114
114
 
115
+ // REVISIT: invoking service.on('error') handlers needs a cleanup with new protocol adapters!!!
115
116
  // invoke srv.on('error', function (err, req) { ... }) here in special situations
116
117
  // REVISIT: if for compat reasons, remove once cds^5.1
117
118
  if (srv._handlers._error.length) {
@@ -124,7 +125,7 @@ const getErrorHandler = (crashOnError = true, srv) => {
124
125
  // > error before req was dispatched
125
126
  const creq = new cds.Request({ req, res: req.res, user: req.user || new cds.User.Anonymous() })
126
127
  for (const each of srv._handlers._error) each.handler.call(srv, err, creq)
127
- } else {
128
+ } else if (ctx._tx?._done !== 'rolled back') {
128
129
  // > error after req was dispatched, e.g., serialization error in okra
129
130
  const creq = /* odataReq.req || */ new cds.Request({ req, res: req.res, user: ctx.user, tenant: ctx.tenant })
130
131
  for (const each of srv._handlers._error) each.handler.call(srv, err, creq)
@@ -18,7 +18,8 @@ const _loadStreamExtensionIfNeeded = () => {
18
18
  const streamExtension = _loadStreamExtensionIfNeeded()
19
19
 
20
20
  function hasStreamInsert(insert, model) {
21
- if (!model) return true
21
+ if (!model) return false
22
+
22
23
  const name = insert.into.ref ? insert.into.ref[0] : insert.into
23
24
  const into = model.definitions[ensureNoDraftsSuffix(name)]
24
25
  if (!into) return false
@@ -38,9 +39,7 @@ function hasStreamInsert(insert, model) {
38
39
  }
39
40
 
40
41
  function hasStreamUpdate(update, model) {
41
- if (!model) {
42
- return true
43
- }
42
+ if (!model) return false
44
43
 
45
44
  const entity = model.definitions[ensureNoDraftsSuffix((update.entity.ref && update.entity.ref[0]) || update.entity)]
46
45
  if (!entity) return false
@@ -194,12 +194,12 @@ const RestAdapter = function (srv) {
194
194
 
195
195
  // -----------------------------------------------------------------------------------------
196
196
  // error handling
197
- router.use((err, req, res, next) => {
197
+ router.use(async (err, req, res, next) => {
198
198
  // REVISIT: should not be neccessary!
199
199
  // request may fail during processing or during commit -> both caught here
200
200
 
201
201
  // REVISIT: rollback needed if error occured before commit attempted -> how to distinguish?
202
- cds.context?.tx?.rollback(err).catch(() => {}) // REVISIT: silently ?!?
202
+ await cds.context?.tx?.rollback(err).catch(() => {}) // REVISIT: silently ?!?
203
203
 
204
204
  next(err)
205
205
  })
@@ -39,13 +39,16 @@ const _log = err => {
39
39
  module.exports = (err, req, res, next) => {
40
40
  const { _srv: srv } = req
41
41
 
42
+ // REVISIT: invoking service.on('error') handlers needs a cleanup with new protocol adapters!!!
42
43
  // invoke srv.on('error', function (err, req) { ... }) here in special situations
43
44
  let ctx = cds.context
44
45
  if (!ctx) {
45
46
  // > error before req was dispatched
46
47
  ctx = new cds.Request({ req, res: req.res, user: req.user || new cds.User.Anonymous() })
48
+ for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
49
+ } else if (ctx._tx?._done !== 'rolled back') {
50
+ for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
47
51
  }
48
- for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
49
52
 
50
53
  // log the error (4xx -> warn)
51
54
  _log(err)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.0.2",
3
+ "version": "7.0.3",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [