@sap/cds 8.2.0 → 8.2.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@
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 8.2.1 - 2024-09-04
8
+
9
+ ### Fixed
10
+
11
+ - Date validation of legacy OData protocol adapter
12
+ - Content-Length headers in multipart batch request body
13
+ - Streaming requests with virtual properties
14
+ - Bring back support for `x-correlationid`
15
+ - Validation of inlined elements
16
+ - multipart `$batch` parsing with _--_ as part of payload
17
+
7
18
  ## Version 8.2.0 - 2024-08-30
8
19
 
9
20
  ### Added
@@ -56,13 +56,15 @@ class Validation {
56
56
  class ValidationErrors extends Array {
57
57
  add (error) {
58
58
  const err = Object.create (ValidationErrors.proto)
59
- err.message = err.stack = error
59
+ err.message = error
60
60
  this.push (err)
61
61
  return err
62
62
  }
63
63
  static proto = Object.create (Error.prototype, {
64
64
  message: { writable:true, configurable:true },
65
- stack: { writable:true, configurable:true, value: '<none>' },
65
+ stack: { configurable:true, get() { return this.message },
66
+ set(v) { Object.defineProperty (this, 'stack', { value:v, writable:true, configurable:true }) },
67
+ },
66
68
  code: { value: '400', writable:true }, // REVISIT: should be 'ASSERT_'... (i.e. msg) but we need to adjust all tests, and have a code catalogue
67
69
  statusCode: { value: 400 }, // REVISIT: should go into mappings in adapter's error handlers -> requires a code catalogue // REVISIT: .statusCode vs .status?
68
70
  numericSeverity: { value: 4, enumerable: true }, // REVISIT: that is OData-specific
@@ -118,12 +120,15 @@ const $any = class any {
118
120
  }
119
121
 
120
122
  _is_mandatory (d=this) {
121
- return d.own('_mandatory', ()=> {
122
- if (d['@readonly']) return false // readonly -> not mandatory
123
- if (d['@mandatory']) return true
124
- if (d['@Common.FieldControl']?.['#'] === 'Mandatory') return true
125
- else return false
126
- })
123
+ return d.own('_mandatory', ()=> (
124
+ !d['@readonly'] // readonly -> not mandatory
125
+ && (d['@mandatory'] || d['@Common.FieldControl']?.['#'] === 'Mandatory')
126
+ && !d._is_flattened()
127
+ ))
128
+ }
129
+
130
+ _is_flattened (d=this) {
131
+ return d.parent?.query?.SELECT.columns?.some (c => c.ref?.length > 1 && d.name === (c.as || c.ref.at(-1)))
127
132
  }
128
133
 
129
134
  _is_readonly (d=this) {
@@ -1,5 +1,6 @@
1
1
  const cds = require ('../../index')
2
2
  const corr_id = 'x-correlation-id'
3
+ const crippled_corr_id = 'x-correlationid'
3
4
  const req_id = 'x-request-id'
4
5
  const vr_id = 'x-vcap-request-id'
5
6
  const { uuid } = cds.utils
@@ -8,7 +9,7 @@ const { EventContext } = cds
8
9
  module.exports = () => {
9
10
  /** @type { import('express').Handler } */
10
11
  return function cds_context (req, res, next) {
11
- const id = req.headers[corr_id] ??= req.headers[req_id] || req.headers[vr_id] || uuid()
12
+ const id = req.headers[corr_id] ??= req.headers[req_id] || req.headers[vr_id] || req.headers[crippled_corr_id] || uuid()
12
13
  const ctx = EventContext.for ({ id, http: { req, res } })
13
14
  res.set ('X-Correlation-ID', id) // Note: we use capitalized style here as that's common standard in HTTP world
14
15
  cds._context.run (ctx, next)
@@ -4,7 +4,7 @@ const { big } = require('@sap/cds-foss')
4
4
  const { isInvalidBase64string } = require('../../../../../../common/utils/binary')
5
5
  const IllegalArgumentError = require('../errors/IllegalArgumentError')
6
6
 
7
- const YEAR_RE = '(?:-?(?:(?:(?:0\\d{3})|(?:[1-9]\\d{3,}))))'
7
+ const YEAR_RE = '(?:-?(?:(?:(?:0\\d{3})|(?:[1-9]\\d{3}))))'
8
8
  const MONTH_RE = '(?:(?:0[1-9])|(?:1[012]))'
9
9
  const DAY_RE = '(?:(?:0[1-9])|(?:[12]\\d)|(?:3[01]))'
10
10
  const HOURS_RE = '(?:(?:[01]\\d)|(?:2[0-3]))'
@@ -2,8 +2,10 @@ const cds = require('../../cds')
2
2
  const { ensureNoDraftsSuffix, ensureUnlocalized } = require('./draft')
3
3
  const { isDuplicate } = require('./rewriteAsterisks')
4
4
 
5
- const _addColumn = (name, type, columns, url) => {
6
- const mediaType = typeof type === 'object' ? { ref: [type['='].replaceAll(/\./g, '_')] } : { val: type }
5
+ const _addColumn = (name, type, columns, url, target) => {
6
+ let mediaType = typeof type === 'object' && type['=']
7
+ if (mediaType && target.elements[mediaType]?.virtual) return
8
+ mediaType = mediaType ? { ref: [mediaType.replaceAll(/\./g, '_')] } : { val: type }
7
9
  const col = {
8
10
  xpr: [
9
11
  'case',
@@ -34,8 +36,8 @@ const _addColumn = (name, type, columns, url) => {
34
36
  const _addColumns = (target, columns) => {
35
37
  for (const k in target.elements) {
36
38
  const el = target.elements[k]
37
- if (el['@Core.MediaType']) {
38
- _addColumn(el.name, el['@Core.MediaType'], columns, el['@Core.IsURL'] && el.type === 'cds.String')
39
+ if (el['@Core.MediaType'] && !el.virtual) {
40
+ _addColumn(el.name, el['@Core.MediaType'], columns, el['@Core.IsURL'] && el.type === 'cds.String', target)
39
41
  }
40
42
  }
41
43
  }
@@ -58,7 +60,7 @@ const handleStreamProperties = (target, columns, model) => {
58
60
  _addColumns(target, columns)
59
61
  } else if (col.ref && (type === 'cds.LargeBinary' || (mediaType && !ignoreMediaType))) {
60
62
  if (mediaType) {
61
- _addColumn(name, mediaType, columns, element['@Core.IsURL'])
63
+ _addColumn(name, mediaType, columns, element['@Core.IsURL'], target)
62
64
  columns.splice(index, 1)
63
65
  } else if (!cds.env.features.stream_compat) {
64
66
  columns.splice(index, 1)
@@ -69,7 +69,9 @@ const parseStream = async function* (body, boundary) {
69
69
  const process = chunk => {
70
70
  let changed = chunk
71
71
  .toString()
72
- .replace(/--(.*)$/gm, (_, g) => `HEAD /${g} HTTP/1.1${g.slice(-2) === '--' ? CRLF : ''}`)
72
+ .replace(/^--(.*)$/gm, (_, g) => `HEAD /${g} HTTP/1.1${g.slice(-2) === '--' ? CRLF : ''}`)
73
+ // correct content-length for non-HEAD requests is inserted below
74
+ .replace(/content-length: \d+\r\n/gim, '')
73
75
  .replace(/ \$/g, ' /$')
74
76
 
75
77
  // HACKS!!!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "8.2.0",
3
+ "version": "8.2.1",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [