@sap/cds-dk 6.0.4 → 6.1.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 (97) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/bin/add.js +29 -16
  3. package/bin/compile/index.js +6 -1
  4. package/bin/fix-redirects.js +4 -2
  5. package/bin/init.js +5 -0
  6. package/bin/login.js +8 -2
  7. package/bin/pull.js +58 -0
  8. package/bin/push.js +52 -0
  9. package/bin/subscribe.js +45 -0
  10. package/lib/bind/bindingUtil.js +1 -2
  11. package/lib/client/activate.js +1 -1
  12. package/lib/client/auth_manager.js +7 -3
  13. package/lib/client/extend.js +1 -1
  14. package/lib/client/helper/cf_helper.js +1 -0
  15. package/lib/client/helper/url_helper.js +7 -3
  16. package/lib/client/index.js +1 -1
  17. package/lib/client/params.js +4 -2
  18. package/lib/client/pull.js +51 -0
  19. package/lib/client/push.js +49 -0
  20. package/lib/client/settings_manager.js +74 -21
  21. package/lib/compile/openapi/csdl2openapi.js +135 -163
  22. package/lib/compile/openapi/index.js +0 -1
  23. package/lib/import/common.js +9 -1
  24. package/lib/import/odata/metadataconverter/MetadataConverter.js +17 -4
  25. package/lib/import/odata/v2parser.js +0 -1
  26. package/lib/import/odata/v4parser.js +103 -29
  27. package/lib/import/openapi/importOpenAPI.js +3 -3
  28. package/lib/init/constants.js +9 -3
  29. package/lib/init/template/_merging/registry-mta.js +290 -0
  30. package/lib/init/template/approuter/index.js +22 -32
  31. package/lib/init/template/auditlog/index.js +7 -9
  32. package/lib/init/template/extensibility/files/cds.package.json +7 -0
  33. package/lib/init/template/extensibility/files/cds.package.json.hbs +9 -0
  34. package/lib/init/template/extensibility/files/xs-security.json.hbs +26 -0
  35. package/lib/init/template/extensibility/index.js +48 -0
  36. package/lib/init/template/hana/files/java/cdsrc.json.hbs +1 -1
  37. package/lib/init/template/hana/files/java/profile.cdsrc.json.hbs +2 -2
  38. package/lib/init/template/hana/files/mta.yaml.hbs +10 -0
  39. package/lib/init/template/hana/files/nodejs/cds.package.json.hbs +1 -1
  40. package/lib/init/template/hana/files/nodejs/cds.profile.package.json.hbs +2 -2
  41. package/lib/init/template/hana/index.js +74 -61
  42. package/lib/init/template/kibana-logging/index.js +31 -37
  43. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +14 -14
  44. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  45. package/lib/init/template/mta/index.js +13 -7
  46. package/lib/init/template/mtx/index.js +2 -142
  47. package/lib/init/template/multitenancy/files/cds.package.json +7 -0
  48. package/lib/init/template/multitenancy/files/cds.package.json.hbs +9 -0
  49. package/lib/init/template/{mtx → multitenancy}/files/dependencies.package.json +1 -1
  50. package/lib/init/template/multitenancy/files/java/.cdsrc.json +19 -0
  51. package/lib/init/template/multitenancy/files/java/package.json +31 -0
  52. package/lib/init/template/{mtx → multitenancy}/files/mta.yaml.hbs +13 -6
  53. package/lib/init/template/multitenancy/files/xs-security.json.hbs +18 -0
  54. package/lib/init/template/multitenancy/index.js +109 -0
  55. package/lib/init/template/nodejs/files/package.json.hbs +3 -0
  56. package/lib/init/template/templateBase.js +2 -1
  57. package/lib/init/template/toggles/files/cds.package.json +7 -0
  58. package/lib/init/template/toggles/files/cds.package.json.hbs +9 -0
  59. package/lib/init/template/toggles/index.js +24 -0
  60. package/lib/init/template/xsuaa/files/mta.yaml.hbs +10 -0
  61. package/lib/init/template/xsuaa/index.js +44 -33
  62. package/lib/init/util/projectReader.js +12 -7
  63. package/lib/init/util/templateUtil.js +112 -110
  64. package/lib/k8s/ChartAdder.js +11 -3
  65. package/lib/k8s/ChartBuilder.js +6 -0
  66. package/lib/k8s/buildModel.js +1 -3
  67. package/lib/k8s/connectivity/index.js +5 -5
  68. package/lib/k8s/deployment/index.js +2 -4
  69. package/lib/k8s/deployment/subchart/Chart.yaml +1 -1
  70. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +22 -11
  71. package/lib/k8s/deployment/subchart/templates/deployment.yaml +12 -5
  72. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +3 -1
  73. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +1 -1
  74. package/lib/k8s/deployment/subchart/values.md +812 -0
  75. package/lib/k8s/deployment/subchart/values.schema.json +125 -30
  76. package/lib/k8s/deployment/subchart/values.yaml +7 -3
  77. package/lib/k8s/hana-deployer/index.js +10 -0
  78. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +8 -0
  79. package/lib/k8s/html5-apps-deployer/index.js +10 -0
  80. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +8 -0
  81. package/lib/k8s/index.js +2 -2
  82. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +1 -159
  83. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +0 -33
  84. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +1 -1
  85. package/lib/k8s/resources/helm/chart/values.schema.json +449 -0
  86. package/lib/lint/Linter.js +155 -158
  87. package/lib/lint/io.js +13 -5
  88. package/npm-shrinkwrap.json +510 -86
  89. package/package.json +5 -4
  90. package/lib/init/template/_merging/existences.js +0 -297
  91. package/lib/init/template/mtx/files/cds.package.json +0 -12
  92. package/lib/init/template/mtx/files/cds.package.json.hbs +0 -14
  93. package/lib/init/template/mtx/files/java/.cdsrc.json +0 -17
  94. package/lib/init/template/mtx/files/java/package.json +0 -43
  95. package/lib/init/template/mtx/files/java/server.js +0 -30
  96. package/lib/init/template/mtx/files/xs-security.json.hbs +0 -48
  97. package/lib/lint/.eslintrc.js +0 -3
@@ -63,6 +63,7 @@ module.exports = class ProjectReader {
63
63
  }
64
64
 
65
65
  async _getCapDescriptor(env, options) {
66
+ const projectPath = this.projectPath
66
67
  let capDescriptor = {
67
68
  for: options && options.for,
68
69
  pLanguage: DEFAULT_P_LANGUAGE,
@@ -70,12 +71,17 @@ module.exports = class ProjectReader {
70
71
  db: [],
71
72
  requires: [],
72
73
  get isMultitenant() {
73
- return options && options.add && options.add.has('mtx') ||
74
+ return options?.add?.has('mtx') || options?.add?.has('multitenancy') ||
74
75
  env.requires.multitenancy ||
75
- env.requires.db && env.requires.db.multiTenant ||
76
- this.isJava && fs.existsSync('mtx-sidecar') ||
76
+ env.requires.db?.multiTenant ||
77
+ this.isJava && fs.existsSync(path.join('mtx', 'sidecar')) ||
77
78
  false
78
79
  },
80
+ get isExtensible() {
81
+ const sidecarPackage = path.join('mtx', 'sidecar', 'package.json')
82
+ return options?.add?.has('mtx') || options?.add?.has('extensibility') || env.requires.extensibility ||
83
+ fs.existsSync(sidecarPackage) && JSON.parse(fs.readFileSync(sidecarPackage, 'utf8')).cds.requires.extensibility === true
84
+ },
79
85
  get isNodejs() {
80
86
  return this.pLanguage === P_LANGUAGE_NODEJS
81
87
  },
@@ -99,6 +105,9 @@ module.exports = class ProjectReader {
99
105
  },
100
106
  get hasApprouter() {
101
107
  return !!env.requires.approuter
108
+ },
109
+ get hasMtaDeployment() {
110
+ return fs.existsSync(path.join(projectPath, 'mta.yaml'))
102
111
  }
103
112
  }
104
113
 
@@ -392,7 +401,6 @@ module.exports = class ProjectReader {
392
401
  // use different service name to avoid conflicts when switching from single tenant to multitenant
393
402
  // otherwise we would enforce that users have to delete the db container first
394
403
  if (resource.isMultitenant) {
395
- //resource.name += '-mt'
396
404
  resource.service = 'service-manager'
397
405
  resource.vcap.plan = 'container'
398
406
  } else {
@@ -402,9 +410,6 @@ module.exports = class ProjectReader {
402
410
  break
403
411
 
404
412
  case 'xsuaa':
405
- // if (capDescriptor.isMultitenant) {
406
- // resource.name += '-mt'
407
- // }
408
413
  resource.service = reqEntry.kind
409
414
  resource.vcap.plan = 'application'
410
415
  break
@@ -68,31 +68,29 @@ module.exports = class TemplateUtil {
68
68
  just maps where you can do a key-value replacement.
69
69
  */
70
70
  _deepMerge(target, source) {
71
+ const unique = array => [...new Set(array.map(JSON.stringify))].map(JSON.parse)
71
72
  if (this._isObject(target) && this._isObject(source)) {
72
73
  for (const key in source) {
73
74
  if (this._isObject(source[key])) {
74
- if (!target[key]) Object.assign(target, { [key]: source[key] });
75
- if ('#overwrite' in source[key]) Object.assign(target, { [key]: source[key]["#overwrite"] })
76
- else this._deepMerge(target[key], source[key]);
75
+ if (!target[key]) Object.assign(target, { [key]: source[key] })
76
+ else this._deepMerge(target[key], source[key])
77
77
  } else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
78
- const unique = array => [...new Set(array.map(JSON.stringify))].map(JSON.parse);
79
78
  target[key] = unique([...source[key], ...target[key]])
80
79
  } else {
81
- Object.assign(target, { [key]: target[key] || source[key] });
80
+ Object.assign(target, { [key]: target[key] || source[key] })
82
81
  }
83
82
  }
84
83
  } else if (Array.isArray(target) && Array.isArray(source)) {
85
- const unique = array => [...new Set(array.map(JSON.stringify))].map(JSON.parse);
86
84
  target = unique([...source, ...target])
87
85
  }
88
- return target || source;
86
+ return target || source
89
87
  }
90
88
 
91
89
  async mergeJSON(targetPath, from, projectDescriptor, semantics) {
92
90
  const source = typeof from === 'string' ? await this.fsUtil.readJSON(from, projectDescriptor) : from
93
91
 
94
92
  if (! await this.fsUtil.pathExists(targetPath)) {
95
- await this.fsUtil.writeJSON(targetPath, source);
93
+ await this.fsUtil.writeJSON(targetPath, source)
96
94
  } else if (semantics) {
97
95
  const target = await this.fsUtil.readJSON(targetPath)
98
96
 
@@ -104,11 +102,11 @@ module.exports = class TemplateUtil {
104
102
  const result = YAML.parse(YAML.stringify(resultYAML))
105
103
  await this.fsUtil.writeJSON(targetPath, result)
106
104
  } else {
107
- const target = await this.fsUtil.readJSON(targetPath);
108
- const sourceFile = await this._processTemplateFile(from, projectDescriptor);
109
- const source = JSON.parse(sourceFile);
110
- const result = target && source ? this._deepMerge(target, source) : target || source;
111
- await this.fsUtil.writeJSON(targetPath, result);
105
+ const target = await this.fsUtil.readJSON(targetPath)
106
+ const sourceFile = await this._processTemplateFile(from, projectDescriptor)
107
+ const source = JSON.parse(sourceFile)
108
+ const result = target && source ? this._deepMerge(target, source) : target || source
109
+ await this.fsUtil.writeJSON(targetPath, result)
112
110
  }
113
111
  }
114
112
 
@@ -121,98 +119,95 @@ module.exports = class TemplateUtil {
121
119
  const existenceMap = new Map()
122
120
  const templateExistenceMap = new Map()
123
121
 
124
- const existences = semantics.existences && semantics.existences.filter(e => e)
125
-
126
- existences && existences.forEach(existence => {
122
+ const { additions, overwrites, deletions, relationships } = semantics
123
+ additions?.forEach(existence => {
127
124
  existenceMap.set(existence.ref, undefined)
128
125
  templateExistenceMap.set(existence.ref, undefined)
129
- });
130
-
131
- const { deletions } = semantics
132
- deletions && deletions.forEach(deletion => {
133
- existenceMap.set(deletion.existence.ref, undefined)
134
126
  })
135
-
136
- const missingRelationshipsMap = new Map()
137
-
138
- const relationships = semantics.relationships && semantics.relationships.filter(r => r)
139
- relationships && relationships.forEach(relationship =>
140
- missingRelationshipsMap.set(relationship.in.ref, undefined)
141
- )
127
+ deletions?.forEach(deletion => {
128
+ existenceMap.set(deletion.item.ref, undefined)
129
+ })
130
+ relationships?.forEach(relationship => {
131
+ const [existence] = relationship.into
132
+ const [insertExistence] = relationship.insert
133
+ const ref = insertExistence.ref + ' -> ' + existence.ref
134
+ existenceMap.set(ref, undefined)
135
+ })
142
136
 
143
137
  let collectionStack = [target.contents]
144
138
 
145
- const _getProperty = (object, keyPath) => keyPath.reduce((p, n) => p && p[n], object)
146
- const _getYAMLProperty = (object, keyPath) => keyPath.reduce((p, n) => p && p.get(n), object)
147
-
148
- const _validateConstraints = (node, dict, index) => {
149
- const _addToMap = existences => existences && existences
150
- .filter(existence => {
151
- if (!existence.in) return true
152
- const neededParent = existence.in && dict.get(existence.in.ref) && dict.get(existence.in.ref).node
153
- return collectionStack.includes(neededParent)
154
- })
155
- .forEach(existence => {
156
- const json = JSON.parse(String(node));
157
- const constraintsFulfilled = existence.constraints.every(constraint =>
158
- constraint.value === _getProperty(json, constraint.comparisonKeyPath)
159
- )
160
- if (constraintsFulfilled) {
161
- const [collection] = collectionStack
162
- dict.set(existence.ref, { json, node, index, collection })
163
- }
139
+ const _getProperty = (object, keyPath) => keyPath.split('.').reduce((p, k) => p && p[k], object)
140
+ const _getYAMLProperty = (object, keyPath) => keyPath.split('.').reduce((p, k) => p && p.get(k), object)
141
+
142
+ const _validateWhere = (node, dict, index) => {
143
+ const _addToMap = existences => existences?.filter(existence => {
144
+ let where = existence.in
145
+ if (typeof where === 'string') return true // just a key path on the document root
146
+ else where = where[0] // a more complex list of constraints, a sequence/array is involved
147
+ const neededParent = dict.get(where.ref)?.node
148
+ return collectionStack.includes(neededParent) // lookbehind in parent collection stack
149
+ // REVISIT: Only look behind until sequence is reached?
150
+ }).forEach(({ where, ref }) => {
151
+ const json = JSON.parse(String(node))
152
+ const whereFulfilled = where.every(constraint =>
153
+ constraint.isEqualTo === _getProperty(json, constraint.property)
154
+ )
155
+ if (whereFulfilled) {
156
+ const [collection] = collectionStack
157
+ dict.set(ref, { json, node, index, collection })
158
+ }
164
159
  })
165
- _addToMap(existences)
166
- deletions && _addToMap(deletions.map(deletion => deletion.existence))
160
+ if (additions) _addToMap(additions)
161
+ if (deletions) _addToMap(deletions.map(deletion => deletion.item))
162
+ if (relationships) _addToMap(relationships?.map(({into: [existence]}) => existence).filter(e => e))
167
163
  }
168
164
 
169
165
  function _traverseYAMLNode(node, index, actions, templateNode) {
170
166
  let shifted = false
171
167
  if (YAML.isMap(node)) {
172
- actions && actions.visitMap && actions.visitMap(node, index)
168
+ actions?.visitMap && actions.visitMap(node, index)
173
169
  const [collection] = collectionStack
174
170
  if (YAML.isSeq(collection)) {
175
171
  collectionStack.unshift(node)
176
172
  shifted = true
177
173
  }
178
- } else if (YAML.isPair(node) && node.value && node.value.items) {
174
+ } else if (YAML.isPair(node) && node.value?.items) {
179
175
  collectionStack.unshift(node.value)
180
176
  } else if (YAML.isScalar(node) && semantics.forceOverwrite && templateNode) {
181
177
  node.value = templateNode.value
182
178
  }
183
179
 
184
180
  if (node.items) {
185
- if (YAML.isSeq(node) && templateNode && templateNode.items) {
186
- actions.mergeCollection && actions.mergeCollection(node, templateNode)
181
+ if (YAML.isSeq(node) && templateNode?.items) {
182
+ actions?.mergeCollection && actions.mergeCollection(node, templateNode)
187
183
  _traverseYAMLCollection(node, actions, templateNode)
188
184
  } else {
189
185
  _traverseYAMLCollection(node, actions, templateNode)
190
186
  }
191
- } else if (node.value && node.value.items || node.value && semantics.forceOverwrite) {
187
+ } else if (node.value?.items || node.value && semantics.forceOverwrite) {
192
188
  _traverseYAMLNode(node.value, index, actions, templateNode && templateNode.value)
193
189
  }
194
190
 
195
- if (node.value && node.value.items || shifted) {
191
+ if (node.value?.items || shifted) {
196
192
  collectionStack.shift()
197
193
  }
198
194
  }
199
195
  _traverseYAMLNode.bind(this)
200
196
 
201
- const _traverseYAMLCollection = (collection, actions, templateCollection) => {
197
+ function _traverseYAMLCollection (collection, actions, templateCollection) {
202
198
  if (!collection) return
203
199
 
204
- const keyIndexMap = new Map()
205
- const templateIndexMap = new Map()
200
+ const keyToIndex = new Map, templateIndexToIndex = new Map
206
201
 
207
202
  // Map collection items to their semantic counterpart(s)
208
203
  collection.items.forEach((node, i) => {
209
- if (node.key) keyIndexMap.set(node.key.value, i)
210
- const existence = [...existenceMap.entries()].find(([,value]) => value && value.node === node)
204
+ if (node.key) keyToIndex.set(node.key.value, i)
205
+ const existence = [...existenceMap.entries()].find(([,value]) => value?.node === node)
211
206
  if (existence) {
212
207
  const [existenceKey] = existence
213
208
  const templateExistence = existenceKey && templateExistenceMap.get(existenceKey)
214
- const templateIndex = templateExistence && templateExistence.index
215
- if (templateIndex !== undefined) templateIndexMap.set(templateIndex, i)
209
+ const templateIndex = templateExistence?.index
210
+ if (templateIndex !== undefined) templateIndexToIndex.set(templateIndex, i)
216
211
  }
217
212
  if (!templateCollection) _traverseYAMLNode(node, i, actions)
218
213
  })
@@ -220,8 +215,12 @@ module.exports = class TemplateUtil {
220
215
  if (!templateCollection) return
221
216
 
222
217
  templateCollection.items.forEach((templateNode, templateIndex) => {
223
- if (YAML.isPair(templateNode)) {
224
- const i = keyIndexMap.get(templateNode.key.value)
218
+ if (YAML.isScalar(templateNode)) {
219
+ if (!collection.items.map(item => item.value).includes(templateNode.value)) {
220
+ collection.add(templateNode)
221
+ }
222
+ } else if (YAML.isPair(templateNode)) {
223
+ const i = keyToIndex.get(templateNode.key.value)
225
224
  const [collection] = collectionStack
226
225
  const targetNode = collection.items && collection.items[i]
227
226
  if (targetNode) {
@@ -230,10 +229,10 @@ module.exports = class TemplateUtil {
230
229
  actions.mergePair(collection, templateNode)
231
230
  }
232
231
  } else if (YAML.isMap(templateNode)) {
233
- const targetNode = collection.items[templateIndexMap.get(templateIndex)]
232
+ const targetNode = collection.items[templateIndexToIndex.get(templateIndex)]
234
233
  if (actions.mergeCollection) actions.mergeCollection(targetNode, templateNode)
235
234
  if (targetNode) {
236
- _traverseYAMLNode(targetNode, templateIndex, actions, templateNode);
235
+ _traverseYAMLNode(targetNode, templateIndex, actions, templateNode)
237
236
  }
238
237
  }
239
238
  })
@@ -243,42 +242,43 @@ module.exports = class TemplateUtil {
243
242
  // 1. Validate the existences in the template
244
243
  _traverseYAMLNode(source.contents, null, {
245
244
  visitMap: (node, index) => {
246
- _validateConstraints(node, templateExistenceMap, index)
245
+ _validateWhere(node, templateExistenceMap, index)
247
246
  }
248
247
  })
249
248
 
250
249
  // 2. Validate the existences in the project
251
250
  _traverseYAMLNode(target.contents, null, {
252
251
  visitMap: (node, index) => {
253
- _validateConstraints(node, existenceMap, index)
252
+ _validateWhere(node, existenceMap, index)
254
253
  }
255
254
  })
256
255
 
257
256
  // 3. Apply overwrites to already found existences
258
- semantics.overwrites && semantics.overwrites.forEach(({ in: inExistence, keyPath, replacement }) => {
259
- const existence = existenceMap.get(inExistence.ref)
260
- if (existence) {
261
- _getYAMLProperty(existence.node, keyPath.slice(0, keyPath.length - 1))
262
- .set(keyPath[keyPath.length - 1], replacement)
263
- }
257
+ overwrites?.forEach(({ item, withValue }) => {
258
+ const keyPath = Array.isArray(item) && typeof item[0] === 'string' ? item[0] : typeof item === 'string' ? item : item[1]
259
+ const inExistence = Array.isArray(item) && typeof item[0] === 'object' ? item[0] : typeof item === 'object' ? item : item[1]
260
+ const node = inExistence && existenceMap.get(inExistence.ref) ? existenceMap.get(inExistence.ref).node : collectionStack[collectionStack.length - 1]
261
+ const keys = keyPath.split('.')
262
+ if (!node.getIn(keys)) return
263
+ _getYAMLProperty(node, keys.slice(0, keys.length - 1).join('.'))
264
+ .set(keys[keys.length - 1], withValue)
264
265
  })
265
266
 
266
267
  // 4. Delete existences from the project (e.g. separate deployer module when adding mtx)
267
- semantics.deletions && semantics.deletions.forEach(({ existence: { ref }, relationships }) => {
268
+ deletions?.forEach(({ item: { ref }, relationships }) => {
268
269
  const existence = existenceMap.get(ref)
269
270
  if (!existence) return
270
271
  existence.collection.delete(existence.index)
271
272
 
272
- relationships && relationships.forEach(relationship => {
273
- const parent = _getYAMLProperty(target, relationship.allWithinKeyPath)
273
+ relationships?.forEach(relationship => {
274
+ const [allWithinKeyPath, inKeyPath, into] = relationship.allWithin
275
+ const parent = _getYAMLProperty(target, allWithinKeyPath)
274
276
  for (const child of parent.items) {
275
- const c = _getYAMLProperty(child, relationship.inKeyPath)
276
- const i = c.items.findIndex(node =>
277
- node.get(relationship.into) === _getProperty(existence.json, relationship.existenceKeyPath)
278
- )
279
- if (i > -1) {
280
- c.delete(i)
281
- }
277
+ const grandchild = _getYAMLProperty(child, inKeyPath)
278
+ const i = grandchild?.items?.findIndex(node =>
279
+ node.get(into) === _getProperty(existence.json, relationship.removeProperty)
280
+ ) ?? - 1
281
+ if (i > -1) grandchild.delete(i)
282
282
  }
283
283
  })
284
284
  })
@@ -286,33 +286,32 @@ module.exports = class TemplateUtil {
286
286
  // 5. Create missing existences and pairs
287
287
  _traverseYAMLNode(target.contents, null, {
288
288
  mergePair: (collection, templateNode) => {
289
- const childIsArray = false//templateNode.value.type !== 'MAP' && !!templateNode.value.items
290
- if (YAML.isMap(collection) && !childIsArray) {
289
+ if (YAML.isMap(collection)) {
291
290
  collection.add(templateNode)
292
291
  }
293
292
  },
294
293
  mergeCollection: (targetNode, templateNode) => {
295
294
  if (YAML.isSeq(templateNode)) {
296
295
  let [,parent] = collectionStack
297
- const missingExistences = semantics.existences
298
- .filter(e => e)
299
- .filter(existence => {
300
- const requiredParent = existence.in &&
301
- existenceMap.get(existence.in.ref) &&
302
- existenceMap.get(existence.in.ref).node
303
- return !existence.in || requiredParent === parent
304
-
296
+ additions?.filter(existence => {
297
+ if (typeof existence.in === 'string' || Array.isArray(existence.in) && typeof existence.in[0] === 'string') return true
298
+ const inExistence = Array.isArray(existence.in) ? existenceMap.get(existence.in[0].ref) : existenceMap.get(existence.in.ref)
299
+ return inExistence?.node === parent
305
300
  })
306
- .filter(({keyPath}) => {
307
- if (keyPath.length > collectionStack.length + 1) return
308
- if (keyPath.length > 1) parent = collectionStack[keyPath.length]
301
+ .filter(existence => {
302
+ const keyPath = typeof existence.in === 'string' ? existence.in : Array.isArray(existence.in) && typeof existence.in[0] === 'string' ? existence.in[0] : existence.in[1]
303
+ const keys = keyPath.split('.')
304
+ if (keys.length > collectionStack.length + 1) return
305
+ if (keys.length > 1) parent = collectionStack[keys.length]
309
306
  return _getYAMLProperty(parent, keyPath) === targetNode
310
307
  })
311
- .filter(({ref}) => !existenceMap.get(ref))
312
- missingExistences.forEach(existence => {
313
- const templateNode = templateExistenceMap.get(existence.ref).node
314
- targetNode.add(templateNode)
315
- })
308
+ .filter(({ ref }) => {
309
+ return !existenceMap.get(ref)
310
+ })
311
+ .forEach(({ ref }) => {
312
+ const templateNode = templateExistenceMap.get(ref).node
313
+ targetNode.add(templateNode)
314
+ })
316
315
  }
317
316
  },
318
317
  }, source.contents)
@@ -320,7 +319,7 @@ module.exports = class TemplateUtil {
320
319
  // 6. Re-validate the existences in the project
321
320
  _traverseYAMLNode(target.contents, null, {
322
321
  visitMap: (node, index) => {
323
- _validateConstraints(node, existenceMap, index)
322
+ _validateWhere(node, existenceMap, index)
324
323
  }
325
324
  })
326
325
 
@@ -329,23 +328,26 @@ module.exports = class TemplateUtil {
329
328
  mergeCollection: (targetNode, templateNode) => {
330
329
  if (YAML.isSeq(templateNode)) {
331
330
  const targetJSON = YAML.parse(String(targetNode))
332
- const relationship = semantics.relationships && semantics.relationships
333
- .filter(r => r)
331
+ const relationship = semantics.relationships?.filter(r => r)
334
332
  .find(relationship => {
335
- const existingNode = _getYAMLProperty(existenceMap.get(relationship.in.ref).node, relationship.inKeyPath)
333
+ const [existence, keyPath] = relationship.into
334
+ if (!existenceMap.get(existence.ref)) return false
335
+ const existingNode = _getYAMLProperty(existenceMap.get(existence.ref).node, keyPath)
336
336
  return targetNode === existingNode
337
337
  })
338
338
  if (!relationship) return
339
339
 
340
- const missingPairs = relationship.existences
340
+ const intoKey = relationship.into[relationship.into.length - 1]
341
+ const [existence, existenceKeyPath] = relationship.insert
342
+ const missingPairs = [existence]
341
343
  .filter(({ref}) =>
342
344
  !targetJSON.some(item =>
343
- _getProperty(existenceMap.get(ref).json, relationship.existenceKeyPath) === item[relationship.into]
345
+ _getProperty(existenceMap.get(ref).json, existenceKeyPath) === item[intoKey]
344
346
  )
345
347
  )
346
- .map(({ref}) => existenceMap.get(ref).node.get(relationship.into))
348
+ .map(({ref}) => existenceMap.get(ref).node.get(intoKey))
347
349
  missingPairs.forEach(pair => {
348
- targetNode.add({ [relationship.into]: pair })
350
+ targetNode.add({ [intoKey]: pair })
349
351
  })
350
352
  }
351
353
  }
@@ -1,6 +1,7 @@
1
1
 
2
2
 
3
3
  const { yaml } = require('./modules');
4
+ const path = require('path');
4
5
  const fs = require('fs').promises;
5
6
  const FsUtil = require('../../lib/init/util/fsUtil')
6
7
  const TemplateUtil = require('../../lib/init/util/templateUtil')
@@ -10,9 +11,9 @@ class ChartAdder {
10
11
  *
11
12
  * @param {{ builder: import('./ChartBuilder') }} param0
12
13
  */
13
- constructor({ builder, template, chartValues, chartDependencies, app, mergeSemantics }) {
14
+ constructor({ builder, resources, chartValues, chartDependencies, app, mergeSemantics }) {
14
15
  this.builder = builder;
15
- this.template = template;
16
+ this.resources = resources;
16
17
  this.chartValues = chartValues;
17
18
  this.app = app;
18
19
  this.mergeSemantics = mergeSemantics;
@@ -22,12 +23,19 @@ class ChartAdder {
22
23
 
23
24
  async execute() {
24
25
  await this.copyTemplates();
26
+ await this.copySchema();
25
27
  await this.addChartValues();
26
28
  await this.addChartFile();
27
29
  }
28
30
 
29
31
  async copyTemplates() {
30
- await this.builder.addAllTemplates(this.template);
32
+ await this.builder.addAllTemplates(this.resources);
33
+ }
34
+
35
+ async copySchema() {
36
+ let schema = JSON.parse(await fs.readFile(path.join(this.resources, 'values.schema.json'), { encoding: 'utf8' }))
37
+
38
+ await this.builder.writeChartFile('values.schema.json', JSON.stringify(schema, null, 4));
31
39
  }
32
40
 
33
41
  async addChartValues() {
@@ -51,6 +51,12 @@ class ChartBuilder {
51
51
 
52
52
  await this.templateUtil.mergeYAML(filePath, newYamlContent, null, mergeSemantics);
53
53
  } else {
54
+
55
+ const header = `
56
+ # yaml-language-server: $schema=./values.schema.json
57
+
58
+ `
59
+ data = header + data;
54
60
  await this.writeChartFile(fileName, data);
55
61
  }
56
62
 
@@ -34,9 +34,7 @@ async function buildModel(context) {
34
34
  return {
35
35
  global: {
36
36
  domain: null,
37
- imagePullSecret: {
38
- name: null
39
- }
37
+ imagePullSecret: {}
40
38
  }
41
39
  }
42
40
  };
@@ -50,12 +50,12 @@ module.exports = wrapFeature('connectivity', {
50
50
  }
51
51
  );
52
52
 
53
- mergeSemantics.existences.push({
53
+ mergeSemantics.additions.push({
54
54
  ref: `{name}-additional-volumes`,
55
- keyPath: [name, "additionalVolumes"],
56
- constraints: [{
57
- comparisonKeyPath: ["name"],
58
- value: "connectivity-secret"
55
+ in: `${name}.additionalVolumes`,
56
+ where: [{
57
+ property: 'name',
58
+ isEqualTo: 'connectivity-secret'
59
59
  }]
60
60
  });
61
61
  },
@@ -19,7 +19,7 @@ class Deployment {
19
19
 
20
20
  const language = this.language || model.language;
21
21
  const memory = language === 'Java' ? '1G' : '500M';
22
- const cpu = language === 'Java' ? '700m' : '100m';
22
+ const cpu = language === 'Java' ? '1000m' : '500m';
23
23
  const env = language === 'Java' ? {SPRING_PROFILES_ACTIVE: "cloud"} : undefined;
24
24
 
25
25
  return {
@@ -32,12 +32,11 @@ class Deployment {
32
32
  env,
33
33
  resources: {
34
34
  limits: {
35
- cpu,
36
35
  'ephemeral-storage': '1G',
37
36
  memory
38
37
  },
39
38
  requests: {
40
- cpu: '100m',
39
+ cpu,
41
40
  'ephemeral-storage': '1G',
42
41
  memory
43
42
  }
@@ -64,7 +63,6 @@ class Deployment {
64
63
 
65
64
  async addToChart(builder) {
66
65
  await builder.addSubchartFiles(webApplicationChartPath, 'web-application');
67
- await builder.addChartFile(Path.join(__dirname, 'subchart-override', 'templates', '_web_application_helpers.tpl'), {fileName: Path.join('charts', 'web-application', 'templates', '_web_application_helpers.tpl')});
68
66
  }
69
67
  }
70
68
 
@@ -1,6 +1,6 @@
1
1
  apiVersion: v2
2
2
  name: web-application
3
3
  description: A Helm chart for web app
4
-
4
+ kubeVersion: ">= 1.21"
5
5
  type: application
6
6
  version: 0.1.0
@@ -81,13 +81,20 @@ Define Host for the APIRule
81
81
  {{- end }}
82
82
  {{- end }}
83
83
 
84
+ {{/*
85
+ Define the application uri which will be used for the VCAP_APPLICATION env variable
86
+ */}}
87
+ {{- define "web-application.applicationUri" -}}
88
+ {{- include "web-application.fullname" . }}
89
+ {{- end }}
90
+
84
91
  {{/*
85
92
  Service Binding secret mounts
86
93
  */}}
87
94
  {{- define "web-application.serviceMounts" -}}
88
95
  {{- range $name, $params := .Values.bindings }}
89
96
  - mountPath: /bindings/{{ $name }}/
90
- name: "{{ $name }}-binding"
97
+ name: "{{ $name }}"
91
98
  readOnly: true
92
99
  {{- end }}
93
100
  {{- end }}
@@ -103,18 +110,9 @@ Service Binding secret volumes
103
110
  {{- else if $params.secretName }}
104
111
  {{- $secretName = $params.secretName }}
105
112
  {{- end }}
106
- - name: {{ $name }}-binding
113
+ - name: {{ $name }}
107
114
  secret:
108
115
  secretName: {{ $secretName }}
109
- items:
110
- - key: label
111
- path: metadata/label
112
- - key: plan
113
- path: metadata/plan
114
- - key: tags
115
- path: metadata/tags
116
- - key: credentials
117
- path: credentials
118
116
  {{- end }}
119
117
  {{- end }}
120
118
 
@@ -132,6 +130,19 @@ Name of the imagePullSecret
132
130
  {{- end }}
133
131
  {{- end }}
134
132
 
133
+ {{/*
134
+ Calculate the final image name
135
+ */}}
136
+ {{- define "web-application.imageName" -}}
137
+ {{- $tag := .Values.image.tag | default .Values.global.image.tag | default "latest" }}
138
+ {{- $registry := .Values.image.registry | default .Values.global.image.registry }}
139
+ {{- if $registry }}
140
+ {{- $registry | trimSuffix "/" }}/{{ .Values.image.repository }}:{{ $tag }}
141
+ {{- else }}
142
+ {{- .Values.image.repository }}:{{ $tag }}
143
+ {{- end }}
144
+ {{- end }}
145
+
135
146
  {{/*
136
147
  Create the name of a service instance.
137
148
  We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).