@sap/cds 5.8.4 → 5.9.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 (244) hide show
  1. package/CHANGELOG.md +174 -77
  2. package/app/fiori/preview.js +16 -11
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskFactory.js +3 -3
  5. package/bin/build/buildTaskProviderFactory.js +1 -1
  6. package/bin/build/constants.js +1 -1
  7. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  8. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  9. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  10. package/bin/build/provider/hana/2migration.js +27 -24
  11. package/bin/build/provider/hana/index.js +17 -18
  12. package/bin/build/provider/hana/migrationtable.js +9 -10
  13. package/bin/build/provider/java-cf/index.js +4 -5
  14. package/bin/build/provider/node-cf/index.js +99 -6
  15. package/bin/cds.js +17 -18
  16. package/bin/deploy/to-hana/cfUtil.js +16 -19
  17. package/bin/deploy/to-hana/hana.js +7 -24
  18. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  19. package/bin/mtx/in-cds.js +2 -2
  20. package/bin/serve.js +10 -3
  21. package/bin/utils/modules.js +7 -0
  22. package/bin/version.js +56 -3
  23. package/lib/compile/cdsc.js +26 -3
  24. package/lib/compile/etc/_localized.js +36 -25
  25. package/lib/compile/etc/csv.js +8 -8
  26. package/lib/compile/for/drafts.js +9 -0
  27. package/lib/compile/for/java.js +16 -0
  28. package/lib/compile/for/nodejs.js +12 -0
  29. package/lib/compile/for/odata.js +1 -1
  30. package/lib/compile/index.js +3 -0
  31. package/lib/compile/minify.js +16 -2
  32. package/lib/compile/parse.js +2 -2
  33. package/lib/compile/resolve.js +35 -18
  34. package/lib/compile/to/json.js +3 -1
  35. package/lib/compile/to/sql.js +2 -2
  36. package/lib/compile/to/srvinfo.js +4 -2
  37. package/lib/connect/index.js +1 -1
  38. package/lib/core/entities.js +15 -14
  39. package/lib/core/index.js +39 -36
  40. package/lib/core/reflect.js +4 -2
  41. package/lib/deploy.js +114 -127
  42. package/lib/env/defaults.js +1 -0
  43. package/lib/env/index.js +165 -165
  44. package/lib/env/presets.js +1 -0
  45. package/lib/env/requires.js +120 -49
  46. package/lib/index.js +1 -0
  47. package/lib/log/format/kibana.js +2 -2
  48. package/lib/ql/SELECT.js +10 -0
  49. package/lib/ql/parse.js +1 -0
  50. package/lib/req/cds-context.js +4 -1
  51. package/lib/req/context.js +50 -56
  52. package/lib/req/event.js +1 -6
  53. package/lib/req/locale.js +6 -5
  54. package/lib/req/request.js +2 -0
  55. package/lib/req/user.js +7 -5
  56. package/lib/serve/Service-api.js +10 -7
  57. package/lib/serve/Service-dispatch.js +9 -11
  58. package/lib/serve/Service-methods.js +30 -41
  59. package/lib/serve/Transaction.js +10 -7
  60. package/lib/serve/adapters.js +7 -5
  61. package/lib/serve/index.js +24 -12
  62. package/lib/utils/data.js +1 -1
  63. package/lib/utils/index.js +27 -30
  64. package/lib/utils/resources/index.js +101 -0
  65. package/lib/utils/resources/tar.js +71 -0
  66. package/lib/utils/resources/utils.js +11 -0
  67. package/libx/_runtime/audit/Service.js +36 -39
  68. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  69. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  70. package/libx/_runtime/audit/utils/v2.js +1 -2
  71. package/libx/_runtime/auth/index.js +126 -84
  72. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  73. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  74. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  75. package/libx/_runtime/auth/strategies/mock.js +0 -4
  76. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  77. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  78. package/libx/_runtime/auth/utils.js +22 -1
  79. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  80. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
  81. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  82. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  88. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  89. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  91. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  92. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  93. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +50 -0
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  99. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  101. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  102. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  103. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  104. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  105. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  106. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  107. package/libx/_runtime/cds-services/services/Service.js +40 -0
  108. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  109. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  110. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  111. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  112. package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
  113. package/libx/_runtime/cds-services/util/assert.js +20 -14
  114. package/libx/_runtime/cds.js +9 -1
  115. package/libx/_runtime/common/aspects/any.js +5 -0
  116. package/libx/_runtime/common/aspects/entity.js +25 -7
  117. package/libx/_runtime/common/aspects/utils.js +2 -2
  118. package/libx/_runtime/common/composition/data.js +6 -0
  119. package/libx/_runtime/common/composition/insert.js +3 -2
  120. package/libx/_runtime/common/composition/tree.js +4 -10
  121. package/libx/_runtime/common/composition/update.js +4 -4
  122. package/libx/_runtime/common/constants/draft.js +29 -26
  123. package/libx/_runtime/common/error/constants.js +2 -2
  124. package/libx/_runtime/common/error/frontend.js +7 -15
  125. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  126. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  127. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  128. package/libx/_runtime/common/generic/auth/index.js +32 -0
  129. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  130. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  131. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  132. package/libx/_runtime/common/generic/auth/restrict.js +296 -0
  133. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  134. package/libx/_runtime/common/generic/crud.js +8 -6
  135. package/libx/_runtime/common/generic/etag.js +1 -1
  136. package/libx/_runtime/common/generic/input.js +35 -35
  137. package/libx/_runtime/common/generic/sorting.js +2 -3
  138. package/libx/_runtime/common/generic/temporal.js +2 -2
  139. package/libx/_runtime/common/i18n/messages.properties +1 -1
  140. package/libx/_runtime/common/toggles/handler.js +21 -0
  141. package/libx/_runtime/common/utils/copy.js +10 -1
  142. package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
  143. package/libx/_runtime/common/utils/csn.js +63 -1
  144. package/libx/_runtime/common/utils/dollar.js +10 -1
  145. package/libx/_runtime/common/utils/draft.js +46 -7
  146. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  147. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  148. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  149. package/libx/_runtime/common/utils/generateOnCond.js +4 -1
  150. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  151. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  152. package/libx/_runtime/common/utils/resolveView.js +4 -1
  153. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  154. package/libx/_runtime/common/utils/structured.js +33 -37
  155. package/libx/_runtime/common/utils/template.js +17 -8
  156. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  157. package/libx/_runtime/db/data-conversion/post-processing.js +118 -417
  158. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  159. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  160. package/libx/_runtime/db/generic/index.js +1 -3
  161. package/libx/_runtime/db/generic/input.js +5 -10
  162. package/libx/_runtime/db/generic/rewrite.js +5 -2
  163. package/libx/_runtime/db/generic/structured.js +2 -2
  164. package/libx/_runtime/db/query/delete.js +2 -2
  165. package/libx/_runtime/db/query/insert.js +1 -1
  166. package/libx/_runtime/db/query/update.js +9 -14
  167. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  168. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  169. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  170. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  171. package/libx/_runtime/db/utils/columns.js +3 -3
  172. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  173. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  174. package/libx/_runtime/extensibility/mps/index.js +5 -0
  175. package/libx/_runtime/extensibility/mps/service.js +111 -0
  176. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  177. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  178. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  179. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  180. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  181. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  182. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  184. package/libx/_runtime/fiori/generic/activate.js +2 -2
  185. package/libx/_runtime/fiori/generic/before.js +4 -4
  186. package/libx/_runtime/fiori/generic/new.js +3 -3
  187. package/libx/_runtime/fiori/generic/patch.js +1 -1
  188. package/libx/_runtime/fiori/generic/read.js +58 -66
  189. package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
  190. package/libx/_runtime/fiori/utils/handler.js +6 -13
  191. package/libx/_runtime/fiori/utils/where.js +6 -5
  192. package/libx/_runtime/hana/Service.js +4 -10
  193. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
  194. package/libx/_runtime/hana/driver.js +2 -2
  195. package/libx/_runtime/hana/execute.js +27 -74
  196. package/libx/_runtime/hana/pool.js +1 -1
  197. package/libx/_runtime/hana/streaming.js +2 -1
  198. package/libx/_runtime/index.js +6 -6
  199. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  200. package/libx/_runtime/messaging/Outbox.js +2 -2
  201. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  202. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  203. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  204. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  205. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  206. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  207. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  208. package/libx/_runtime/messaging/file-based.js +5 -5
  209. package/libx/_runtime/messaging/message-queuing.js +14 -12
  210. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  211. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  212. package/libx/_runtime/messaging/service.js +8 -6
  213. package/libx/_runtime/remote/Service.js +44 -8
  214. package/libx/_runtime/remote/utils/client.js +20 -13
  215. package/libx/_runtime/remote/utils/data.js +11 -11
  216. package/libx/_runtime/sqlite/Service.js +6 -9
  217. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  218. package/libx/_runtime/types/api.js +10 -2
  219. package/libx/common/utils/ucsn.js +109 -0
  220. package/libx/gql/resolvers/crud/update.js +5 -0
  221. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  222. package/libx/gql/schema/typeDefMap.js +2 -2
  223. package/libx/odata/afterburner.js +110 -16
  224. package/libx/odata/grammar.pegjs +9 -1
  225. package/libx/odata/parseToCqn.js +39 -0
  226. package/libx/odata/parser.js +1 -1
  227. package/libx/rest/RestAdapter.js +9 -1
  228. package/libx/rest/middleware/input.js +54 -0
  229. package/libx/rest/middleware/operation.js +14 -1
  230. package/libx/rest/middleware/parse.js +11 -7
  231. package/package.json +1 -1
  232. package/server.js +34 -19
  233. package/srv/audit-log.cds +2 -2
  234. package/srv/flex.cds +8 -2
  235. package/srv/flex.js +1 -1
  236. package/srv/mps.cds +23 -0
  237. package/srv/mps.js +1 -0
  238. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  239. package/libx/_runtime/common/generic/auth.js +0 -874
  240. package/libx/_runtime/common/toggles/alpha.js +0 -43
  241. package/libx/_runtime/db/generic/arrayed.js +0 -33
  242. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  243. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  244. package/libx/rest/utils/data.js +0 -60
@@ -6,6 +6,7 @@ module.exports = {
6
6
  requires: require('./requires'),
7
7
 
8
8
  features: {
9
+ folders: 'fts/*', // where to find feature toggles -> switch on by default when released
9
10
  cls: major > 12 || major == 12 && minor >= 18,
10
11
  live_reload: !production,
11
12
  fiori_preview: !production,
package/lib/env/index.js CHANGED
@@ -1,9 +1,8 @@
1
- const { isfile, fs } = require('../utils')
1
+ const { isfile, fs, path } = require('../utils')
2
2
  const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults')
3
- const os_user_home = require('os').homedir()
4
3
  const compat = require('./compat')
5
4
  const presets = require('./presets')
6
- const path = require('path')
5
+
7
6
 
8
7
  /**
9
8
  * Both a config inctance as well as factory for.
@@ -27,7 +26,13 @@ class Config {
27
26
  */
28
27
  constructor (_context, _home, _defaults=true) {
29
28
  Object.assign (this, { _context, _home, _sources:[] })
30
- this._profiles = _determineProfilesFrom (process.env)
29
+
30
+ // 0. determine profiles from NODE_ENV+ CDS_ENV
31
+ const { NODE_ENV, CDS_ENV } = process.env, profiles = []
32
+ if (NODE_ENV) profiles.push (NODE_ENV)
33
+ if (CDS_ENV) profiles.push (...CDS_ENV.split(/\s*,\s*/))
34
+ if (!profiles.includes('production')) profiles.push('development')
35
+ this._profiles = new Set (profiles)
31
36
  this._profiles._defined = new Set()
32
37
 
33
38
  // 1. set compat requires default values
@@ -39,7 +44,7 @@ class Config {
39
44
  this._add_to_process_env (_home, 'default-env.json')
40
45
 
41
46
  // additional env for dev => 4.
42
- if (process.env.NODE_ENV !== 'production') {
47
+ if (!this._profiles.has('production')) {
43
48
  this._add_to_process_env (_home, '.env')
44
49
  }
45
50
 
@@ -51,10 +56,10 @@ class Config {
51
56
  }
52
57
 
53
58
  // 3. read important (!) profiles from config sources in defined order
54
- const overwriteProfiles = new Set(this.profiles.map( profile => `${profile}!` ).filter( profile => this._profiles._defined.has( profile ) ));
55
- if (overwriteProfiles.size > 0) {
59
+ const important = new Set(this.profiles.map( profile => `${profile}!` ).filter( profile => this._profiles._defined.has( profile ) ));
60
+ if (important.size > 0) {
56
61
  for (const source of sources) {
57
- this._load(source.path, source.file, source.mapper, overwriteProfiles, true)
62
+ this._load(source.path, source.file, source.mapper, important, true)
58
63
  }
59
64
  }
60
65
 
@@ -63,6 +68,7 @@ class Config {
63
68
 
64
69
  // 6. link dependant services (through kind/use)
65
70
  this._link_required_services()
71
+
66
72
  // 7. complete service configurations from VCAP
67
73
  this._add_vcap_services (process.env.VCAP_SERVICES)
68
74
 
@@ -78,7 +84,9 @@ class Config {
78
84
  presets (this)
79
85
 
80
86
  // Only if feature is enabled
81
- this._emulate_vcap_services()
87
+ if (this.features && this.features.emulate_vcap_services) {
88
+ this._emulate_vcap_services()
89
+ }
82
90
  }
83
91
 
84
92
  /**
@@ -87,9 +95,9 @@ class Config {
87
95
  * @param {string} home Project home
88
96
  * @param {string} context configuration context literal
89
97
  */
90
- static sources(home, context = 'cds', ) {
98
+ static sources (home, context = 'cds') {
91
99
  if (!home) throw new Error('Missing parameter "home".')
92
- const user_home = process.env.CDS_USER_HOME || os_user_home
100
+ const user_home = process.env.CDS_USER_HOME || require('os').homedir()
93
101
 
94
102
  let sources = [
95
103
  { name: 'USER_HOME', path: user_home, file: '.cdsrc.json' },
@@ -102,6 +110,22 @@ class Config {
102
110
  return sources
103
111
  }
104
112
 
113
+
114
+ add (conf, /*from:*/ _src, profiles = this._profiles, profiles_only = false) {
115
+ if (!conf) return this
116
+ if (_src) this._sources.push (_src)
117
+ _merge (this, conf, profiles, undefined, profiles_only)
118
+ return this
119
+ }
120
+
121
+ /**
122
+ * Retrieves the value for a config option, specified as a property path.
123
+ */
124
+ get (option) {
125
+ if (!option) return
126
+ return option.split('.').reduce ((p,n)=> p && p[n], this)
127
+ }
128
+
105
129
  /**
106
130
  * This is `this.requires` plus additional entries for all cds.required.<name>.service
107
131
  */
@@ -117,22 +141,15 @@ class Config {
117
141
  return super.required_services_or_defs = dict
118
142
  }
119
143
 
120
- set roots(v) { set (this, 'roots', v) }
121
- get roots() {
122
- return this.roots = Object.values(this.folders) .concat ([ 'schema', 'services' ])
144
+ get profiles() {
145
+ return super.profiles = Array.from (this._profiles)
123
146
  }
124
147
 
125
- get tmp() {
126
- return set (this, 'tmp', require('os').tmpdir())
148
+ get roots() {
149
+ return super.roots = Object.values(this.folders) .concat ([ 'schema', 'services' ])
127
150
  }
128
151
 
129
- /**
130
- * Retrieves the value for a config option, specified as a property path.
131
- */
132
- get (option) {
133
- if (!option) return
134
- return option.split('.').reduce ((p,n)=> p && p[n], this)
135
- }
152
+ get tmp() { return super.tmp = require('os').tmpdir() }
136
153
 
137
154
  /**
138
155
  * Provides access to system defaults for cds env.
@@ -163,24 +180,19 @@ class Config {
163
180
  return Array.from (new Set(Array.from(this._profiles._defined).map( profile => profile.endsWith("!") ? profile.slice(0, -1) : profile)))
164
181
  }
165
182
 
166
- get profiles() {
167
- return super.profiles = Array.from (this._profiles)
168
- }
169
-
170
183
 
171
- //////////////////////////////////////////////////////////////////////////
172
- //
173
- // DANGER ZONE!
174
- // The following are internal APIs which can always change!
175
- //
184
+ //////////////////////////////////////////////////////////////////////////
185
+ //
186
+ // DANGER ZONE!
187
+ // The following are internal APIs which can always change!
188
+ //
176
189
 
177
190
 
178
- /**
179
- * Load from JSON file or directory
180
- *
181
- * No profile support!
182
- */
183
- _loadFromPath (_path, _basePath) {
191
+ /**
192
+ * Load from JSON file or directory
193
+ * No profile support!
194
+ */
195
+ _loadFromPath (_path, _basePath) {
184
196
  if (_basePath && !path.isAbsolute(_path)) _path = path.join(_basePath, _path)
185
197
  const json = _readJson (_path) || _readFromDir (_path)
186
198
  if (json) this.add (json, _path, new Set())
@@ -191,13 +203,6 @@ _loadFromPath (_path, _basePath) {
191
203
  if (json) this.add (_conf (json), file, profiles, profiles_only)
192
204
  }
193
205
 
194
- add (conf, /*from:*/ _src, profiles = this._profiles, profiles_only = false) {
195
- if (!conf) return this
196
- if (_src) this._sources.push (_src)
197
- _merge (this, conf, profiles, undefined, profiles_only)
198
- return this
199
- }
200
-
201
206
  _add_to_process_env (cwd, filename) {
202
207
  const file = path.resolve (cwd,filename)
203
208
  try {
@@ -246,18 +251,32 @@ _loadFromPath (_path, _basePath) {
246
251
  }
247
252
 
248
253
  _link_required_services () {
249
- const { requires } = this, protos = requires && requires._prototypes || {}
250
- for (let each in requires) {
251
- requires[each] = _merged (each)
252
- // if we got an invalid value, remove it (would anyways cause trouble down the road)
253
- if (!requires[each]) delete requires[each]
254
+ const { requires } = this; if (!requires) return
255
+ const kinds = requires.kinds
256
+ if (kinds) {
257
+ Object.defineProperty (requires, 'kinds', { value:kinds, enumerable:false }) // for cds env
258
+ Object.setPrototypeOf (requires, kinds)
259
+ for (let each in kinds) kinds[each] = _linked(each)
254
260
  }
255
- function _merged (key) {
256
- const entry = requires[key] || protos[key]
257
- if (!entry || entry._is_merged || entry.kind === key || !(entry.kind in requires) && !(entry.kind in protos)) return entry
258
- const clone = _merge ({}, _merged (entry.kind)) // first apply inherited data
259
- _merge (clone, entry, false, false) // then apply overridden data
260
- return Object.defineProperty (clone, '_is_merged', {value:true})
261
+ for (let each of Object.keys(requires)) {
262
+ const e = requires[each] = _linked(each)
263
+ if (!e) delete requires[each] // skip invalid values
264
+ }
265
+ function _linked (key) {
266
+ const e = requires[key]; if (!e || e._is_linked || e.kind === key) return e
267
+ let linked
268
+ if (e === true) {
269
+ if (key in kinds) linked = kinds[key]
270
+ else return e
271
+ }
272
+ else if (e.kind in requires) {
273
+ linked = _merge ({}, _linked(e.kind)) // first apply inherited data
274
+ const {kind} = linked // get the kind from prototype
275
+ _merge (linked, e, false, false) // then apply overridden data
276
+ if (kind) linked.kind = kind // inherited kind wins
277
+ }
278
+ else linked = e
279
+ return Object.defineProperty (linked, '_is_linked', {value:true})
261
280
  }
262
281
  }
263
282
 
@@ -267,7 +286,7 @@ _loadFromPath (_path, _basePath) {
267
286
  if (!VCAP_SERVICES) return
268
287
  try {
269
288
  const vcaps = JSON.parse (VCAP_SERVICES)
270
- const any = _add_vcap_services_to (this, vcaps)
289
+ const any = this._add_vcap_services_to (vcaps)
271
290
  if (any) this._sources.push ('{VCAP_SERVICES}')
272
291
  } catch(e) {
273
292
  throw new Error ('[cds.env] - failed to parse VCAP_SERVICES:\n '+ e.message)
@@ -279,10 +298,26 @@ _loadFromPath (_path, _basePath) {
279
298
  * locally with credentials (hybrid mode).
280
299
  */
281
300
  _emulate_vcap_services() {
282
- if (!(this.features && this.features.emulate_vcap_services)) return
283
- process.env.VCAP_SERVICES = JSON.stringify(build_vcap_services(this))
301
+ const vcap_services = {}, names = new Set()
302
+ for (const service in this.requires) {
303
+ let { vcap, credentials, binding } = this.requires[service]
304
+ // "binding.vcap" is chosen over "vcap" because it is meta data resolved from the real service (-> cds bind)
305
+ if (binding && binding.vcap) vcap = binding.vcap
306
+ if (vcap && vcap.label && credentials && Object.keys(credentials).length > 0) {
307
+ // Only one entry for a (instance) name. Generate name from label and plan if not given.
308
+ const { label, plan } = vcap
309
+ const name = vcap.name || `instance:${label}:${plan || ""}`
310
+ if (names.has(name)) continue
311
+ names.add(name)
312
+
313
+ if (!vcap_services[label]) vcap_services[label] = []
314
+ vcap_services[label].push(Object.assign({ name }, vcap, { credentials }))
315
+ }
316
+ }
317
+ process.env.VCAP_SERVICES = JSON.stringify(vcap_services)
284
318
  }
285
319
 
320
+
286
321
  //////////////////////////////////////////////////////////////////////////
287
322
  //
288
323
  // FORBIDDEN ZONE!
@@ -295,7 +330,7 @@ _loadFromPath (_path, _basePath) {
295
330
  // FOR TESTS ONLY! --> PLEASE: tests should test public APIs (only)
296
331
  _for_tests (...conf) {
297
332
  const env = new Config('cds')
298
- this._for_tests.vcaps = (vcaps) => { _add_vcap_services_to (env, vcaps)}
333
+ this._for_tests.vcaps = (vcaps) => { env._add_vcap_services_to (vcaps)}
299
334
  // merge all configs, then resolve profiles (same as in 'for' function above)
300
335
  for (let c of [...conf].reverse()) _merge(env, c, env._profiles)
301
336
  return env
@@ -305,6 +340,47 @@ _loadFromPath (_path, _basePath) {
305
340
  _merge (this, src, this._profiles)
306
341
  return this
307
342
  }
343
+
344
+ // API for binding resolution in @sap/cds-dk
345
+ _find_credentials_for_required_service(service, conf, vcaps) {
346
+ return conf.vcap && _fetch (conf.vcap) || //> alternatives, e.g. { name:'foo', tag:'foo' }
347
+ _fetch ({ name: service }) ||
348
+ _fetch ({ tag: this._context+':'+service }) ||
349
+ _fetch ({ tag: conf.dialect || conf.kind }) || // important for hanatrial, labeled 'hanatrial', tagged 'hana'
350
+ _fetch ({ label: conf.dialect || conf.kind })
351
+
352
+ function _fetch (predicate) {
353
+ for (let k of Object.keys(predicate).reverse()) {
354
+ const v = predicate[k]; if (!v) continue
355
+ const filter = k === 'tag' ? e => _array(e,'tags').includes(v) : e => e[k] === v
356
+ for (let stype in vcaps) {
357
+ const found = _array(vcaps,stype) .find (filter)
358
+ if (found) return found
359
+ }
360
+ }
361
+ }
362
+
363
+ function _array(o,p) {
364
+ const v = o[p]
365
+ if (!v) return []
366
+ if (Array.isArray(v)) return v
367
+ throw new Error(`Expected VCAP entry '${p}' to be an array, but was: ${require('util').inspect(vcaps)}`)
368
+ }
369
+ }
370
+
371
+ _add_vcap_services_to (vcaps={}) {
372
+ let any
373
+ for (let service in this.requires) {
374
+ const conf = this.requires [service]
375
+ if (!conf) continue
376
+ const { credentials } = this._find_credentials_for_required_service(service, conf, vcaps) || {}
377
+ if (credentials) {
378
+ // Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
379
+ any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
380
+ }
381
+ }
382
+ return !!any
383
+ }
308
384
  }
309
385
 
310
386
 
@@ -349,6 +425,27 @@ function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
349
425
  return dst
350
426
  }
351
427
 
428
+ function _value4 (val) {
429
+ if (val && val[0] === '{') try { return JSON.parse(val) } catch(e) {/* ignored */}
430
+ if (val && val[0] === '[') try { return JSON.parse(val) } catch(e) {/* ignored */}
431
+ if (val === 'true') return true
432
+ if (val === 'false') return false
433
+ if (!isNaN(val)) return parseFloat(val)
434
+ return val
435
+ }
436
+
437
+ function _readJson (file) {
438
+ try {
439
+ const src = fs.readFileSync (require.resolve (file))
440
+ return JSON.parse (src)
441
+ } catch (e) {
442
+ if (e instanceof SyntaxError) console.error(`Error parsing '${file}': ${e.message}`)
443
+ else if (e.code !== 'MODULE_NOT_FOUND') console.error(e.message)
444
+ }
445
+ }
446
+
447
+
448
+
352
449
  function _readFromDir (p, isDir) {
353
450
  if (typeof isDir === "undefined") {
354
451
  try {
@@ -379,118 +476,21 @@ function _readFromDir (p, isDir) {
379
476
  } else {
380
477
  return _value4(fs.readFileSync(p, "utf-8"))
381
478
  }
382
- }
383
-
384
- function isFile(p, entry) {
385
- if (entry.isFile()) return true
386
- if (entry.isSymbolicLink()) {
387
- // Kubernetes credentials use symlinks
388
- const target = fs.realpathSync(p)
389
- const targetStat = fs.statSync(target)
390
479
 
391
- if (targetStat.isFile()) return true
392
- }
393
- return false
394
- }
480
+ function isFile(p, entry) {
481
+ if (entry.isFile()) return true
482
+ if (entry.isSymbolicLink()) {
483
+ // Kubernetes credentials use symlinks
484
+ const target = fs.realpathSync(p)
485
+ const targetStat = fs.statSync(target)
395
486
 
396
- function _value4 (val) {
397
- if (val && val[0] === '{') try { return JSON.parse(val) } catch(e) {/* ignored */}
398
- if (val && val[0] === '[') try { return JSON.parse(val) } catch(e) {/* ignored */}
399
- if (val === 'true') return true
400
- if (val === 'false') return false
401
- if (!isNaN(val)) return parseFloat(val)
402
- return val
403
- }
404
-
405
-
406
- function _add_vcap_services_to (env, vcaps={}) {
407
- let any
408
- for (let service in env.requires) {
409
- const conf = env.requires [service]
410
- if (!conf) continue
411
- const { credentials } = (
412
- conf.vcap && _fetch (conf.vcap) || //> alternatives, e.g. { name:'foo', tag:'foo' }
413
- _fetch ({ name: service }) ||
414
- _fetch ({ tag: env._context+':'+service }) ||
415
- _fetch ({ tag: conf.dialect || conf.kind }) || // important for hanatrial, labeled 'hanatrial', tagged 'hana'
416
- _fetch ({ label: conf.dialect || conf.kind }) ||
417
- {/* not found */}
418
- )
419
- // Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
420
- if (credentials) {
421
- any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
422
- }
423
- }
424
- return any
425
-
426
- function _fetch (predicate) {
427
- for (let k of Object.keys(predicate).reverse()) {
428
- const v = predicate[k]; if (!v) continue
429
- const filter = k === 'tag' ? e => _array(e,'tags').includes(v) : e => e[k] === v
430
- for (let stype in vcaps) {
431
- const found = _array(vcaps,stype) .find (filter)
432
- if (found) return found
433
- }
487
+ if (targetStat.isFile()) return true
434
488
  }
489
+ return false
435
490
  }
436
-
437
- function _array(o,p) {
438
- const v = o[p]
439
- if (!v) return []
440
- if (Array.isArray(v)) return v
441
- throw new Error(`Expected VCAP entry '${p}' to be an array, but was: ${require('util').inspect(vcaps)}`)
442
- }
443
-
444
- }
445
-
446
- function _readJson (file) {
447
- try {
448
- const src = fs.readFileSync (require.resolve (file))
449
- return JSON.parse (src)
450
- } catch (e) {
451
- if (e instanceof SyntaxError) console.error(`Error parsing '${file}': ${e.message}`)
452
- else if (e.code !== 'MODULE_NOT_FOUND') console.error(e.message)
453
- }
454
- }
455
-
456
- function _determineProfilesFrom (env = process.env) {
457
- if (env.NODE_ENV !== 'production' && !/\b(development|production)\b/.test(env.CDS_ENV)) {
458
- if (env.CDS_ENV) env.CDS_ENV += ',development'
459
- else env.CDS_ENV = 'development'
460
- }
461
- const split = (x) => env[x] ? env[x].split (/\s*,\s*/) : []
462
- const profiles = [ ...split ('NODE_ENV'), ...split ('CDS_ENV') ]
463
- return new Set (profiles)
464
- }
465
-
466
-
467
- function set (o,p,value) {
468
- Object.defineProperty (o, p, {value,configurable:true,writable:true})
469
- return value
470
491
  }
471
492
 
472
- function build_vcap_services(env) {
473
- let v = {}
474
- let names = new Set()
475
-
476
- for (const service in env.requires) {
477
- let { vcap, credentials, binding } = env.requires[service]
478
- // "binding.vcap" is chosen over "vcap" because it is meta data resolved from the real service (-> cds bind)
479
- if (binding && binding.vcap) vcap = binding.vcap
480
- if (vcap && vcap.label && credentials && Object.keys(credentials).length > 0) {
481
- // Only one entry for a (instance) name. Generate name from label and plan if not given.
482
- const { label, plan } = vcap
483
- const name = vcap.name || `instance:${label}:${plan || ""}`
484
- if (names.has(name)) continue
485
- names.add(name)
486
-
487
- if (!v[label]) v[label] = []
488
- v[label].push(Object.assign({ name }, vcap, { credentials }))
489
- }
490
- }
491
493
 
492
- return v
493
- }
494
494
 
495
495
  /** @type Config & typeof DEFAULTS */
496
496
  module.exports = Config.prototype.for('cds')
@@ -1,3 +1,4 @@
1
+ // REVISIT: Looks ver hard-coded -> move this to consumers?
1
2
  module.exports = function (conf) {
2
3
  const { features } = conf
3
4
  if (!features) return