@sap/cds-dk 6.5.2 → 6.6.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 (122) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/bin/activate.js +7 -0
  3. package/bin/add.js +1 -2
  4. package/bin/build/all.js +14 -9
  5. package/bin/cds.js +11 -8
  6. package/bin/compile/index.js +13 -3
  7. package/bin/env.js +16 -25
  8. package/bin/extend.js +8 -0
  9. package/bin/init.js +0 -3
  10. package/bin/login.js +3 -7
  11. package/bin/logout.js +2 -7
  12. package/bin/watch.js +4 -5
  13. package/lib/client/activate.js +1 -1
  14. package/lib/client/helper/errors.js +2 -1
  15. package/lib/client/params.js +4 -3
  16. package/lib/client/pull.js +31 -5
  17. package/lib/client/push.js +3 -3
  18. package/lib/client/settings_manager.js +6 -6
  19. package/lib/client/subscription.js +1 -1
  20. package/lib/compile/asyncapi/channels.js +22 -0
  21. package/lib/compile/asyncapi/components/messageTraits/index.js +89 -0
  22. package/lib/compile/asyncapi/components/messages/index.js +150 -0
  23. package/lib/compile/asyncapi/components/schemas/annotations.js +13 -0
  24. package/lib/compile/asyncapi/components/schemas/array.js +8 -0
  25. package/lib/compile/asyncapi/components/schemas/cache.js +14 -0
  26. package/lib/compile/asyncapi/components/schemas/definition.js +187 -0
  27. package/lib/compile/asyncapi/components/schemas/enum.js +16 -0
  28. package/lib/compile/asyncapi/components/schemas/examples.js +10 -0
  29. package/lib/compile/asyncapi/components/schemas/index.js +14 -0
  30. package/lib/compile/asyncapi/components/schemas/localized.js +29 -0
  31. package/lib/compile/asyncapi/components/schemas/primitiveElement.js +41 -0
  32. package/lib/compile/asyncapi/components/schemas/ref.js +45 -0
  33. package/lib/compile/asyncapi/components/schemas/utils.js +108 -0
  34. package/lib/compile/asyncapi/index.js +226 -0
  35. package/lib/compile/asyncapi/message.js +13 -0
  36. package/lib/compile/asyncapi/utils.js +27 -0
  37. package/lib/compile/index.js +1 -0
  38. package/lib/compile/openapi/csdl2openapi.js +40 -18
  39. package/lib/import/message.js +3 -1
  40. package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -1
  41. package/lib/import/odata/v2parser.js +131 -50
  42. package/lib/import/odata/v4parser.js +160 -68
  43. package/lib/import/openapi/importOpenAPI.js +14 -14
  44. package/lib/init/constants.js +1 -1
  45. package/lib/init/index.js +11 -21
  46. package/lib/init/template/_merging/registry-mta.js +15 -26
  47. package/lib/init/template/approuter/files/package.json +2 -2
  48. package/lib/init/template/approuter/index.js +6 -12
  49. package/lib/init/template/auditlog/files/auditlog.yaml +4 -1
  50. package/lib/init/template/auditlog/index.js +23 -8
  51. package/lib/init/template/cf-manifest/index.js +4 -14
  52. package/lib/init/template/connectivity/files/connectivity-proxy-info.yaml +2 -6
  53. package/lib/init/template/connectivity/files/values.yaml.hbs +5 -8
  54. package/lib/init/template/connectivity/index.js +2 -9
  55. package/lib/init/template/data/index.js +0 -4
  56. package/lib/init/template/destinations/files/destination.yaml +4 -1
  57. package/lib/init/template/destinations/index.js +25 -10
  58. package/lib/init/template/enterprise-messaging/files/event-mesh.yaml +4 -1
  59. package/lib/init/template/enterprise-messaging/files/values.yaml +1 -2
  60. package/lib/init/template/enterprise-messaging/index.js +28 -20
  61. package/lib/init/template/extensibility/index.js +23 -31
  62. package/lib/init/template/hana/files/hana.yaml +4 -1
  63. package/lib/init/template/hana/index.js +44 -24
  64. package/lib/init/template/helm/chart/templates/_helpers.tpl +12 -0
  65. package/lib/init/template/helm/chart/values.root.schema.json +40 -0
  66. package/lib/init/template/helm/chart/values.schema.json +32 -69
  67. package/lib/init/template/helm/index.js +26 -16
  68. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +13 -26
  69. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +3 -3
  70. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +2 -0
  71. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +3 -3
  72. package/lib/init/template/helm/subcharts/content-deployment/templating.md +37 -0
  73. package/lib/init/template/helm/subcharts/content-deployment/values.md +182 -185
  74. package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +18 -22
  75. package/lib/init/template/helm/subcharts/content-deployment/values.yaml +2 -0
  76. package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +5 -0
  77. package/lib/init/template/helm/subcharts/service-instance/templates/_helpers.tpl +65 -0
  78. package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +23 -0
  79. package/lib/init/template/helm/subcharts/service-instance/templating.md +18 -0
  80. package/lib/init/template/helm/subcharts/service-instance/values-lint.yaml +2 -0
  81. package/lib/init/template/helm/subcharts/service-instance/values.md +186 -0
  82. package/lib/init/template/helm/subcharts/service-instance/values.schema.json +102 -0
  83. package/lib/init/template/helm/subcharts/service-instance/values.yaml +15 -0
  84. package/lib/init/template/html5-repo/files/html5-apps-repo-host.yaml +4 -1
  85. package/lib/init/template/html5-repo/index.js +30 -21
  86. package/lib/init/template/java/index.js +2 -5
  87. package/lib/init/template/kibana-logging/index.js +7 -10
  88. package/lib/init/template/lint/index.js +2 -3
  89. package/lib/init/template/mta/files/mta.yaml.hbs +4 -2
  90. package/lib/init/template/mta/index.js +7 -11
  91. package/lib/init/template/mtx/index.js +0 -5
  92. package/lib/init/template/multitenancy/files/java/package.json +16 -7
  93. package/lib/init/template/multitenancy/files/mta.yaml.hbs +11 -3
  94. package/lib/init/template/multitenancy/files/saas-registry-secret.yaml +9 -0
  95. package/lib/init/template/multitenancy/files/saas-registry.yaml +4 -17
  96. package/lib/init/template/multitenancy/files/service-manager.yaml +4 -1
  97. package/lib/init/template/multitenancy/files/values.yaml.hbs +15 -3
  98. package/lib/init/template/multitenancy/index.js +56 -31
  99. package/lib/init/template/nodejs/index.js +4 -7
  100. package/lib/init/template/notebook/index.js +1 -3
  101. package/lib/init/template/pipeline/index.js +2 -5
  102. package/lib/init/template/samples/index.js +6 -11
  103. package/lib/init/template/templateBase.js +10 -17
  104. package/lib/init/template/toggles/index.js +2 -6
  105. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -1
  106. package/lib/init/template/xsuaa/files/xsuaa.yaml +4 -1
  107. package/lib/init/template/xsuaa/index.js +27 -23
  108. package/lib/init/types.d.ts +0 -81
  109. package/lib/init/util/merge.js +23 -25
  110. package/lib/init/util/projectReader.js +27 -24
  111. package/lib/lint/Linter.js +31 -13
  112. package/lib/watch/livereload-server.js +21 -14
  113. package/lib/watch/node-watch.js +51 -49
  114. package/npm-shrinkwrap.json +551 -458
  115. package/package.json +2 -2
  116. package/readme.md +5 -0
  117. package/lib/init/template/connectivity/files/connectivity-binding.yaml +0 -9
  118. package/lib/init/template/connectivity/files/connectivity.yaml +0 -1
  119. package/lib/init/template/multitenancy/files/xsuaa.yaml +0 -1
  120. /package/lib/init/template/hana/files/{chart.yaml → hana-deployer.yaml} +0 -0
  121. /package/lib/init/template/html5-repo/files/{chart.yaml → html5-apps-deployer.yaml} +0 -0
  122. /package/lib/init/template/multitenancy/files/{chart.yaml → sidecar.yaml} +0 -0
@@ -6,12 +6,10 @@ const term = require('../../../util/term');
6
6
  const { PROJECT_TYPE, OPTION_JAVA, URLS } = require('../../constants');
7
7
 
8
8
  module.exports = class SamplesTemplate extends require('../templateBase') {
9
- constructor(projectPath, generator) {
10
- super(projectPath, generator, __dirname);
11
- }
9
+
12
10
 
13
11
  async run() {
14
- switch (await this.getProjectType()) {
12
+ switch (this.getProjectType()) {
15
13
  case PROJECT_TYPE.java:
16
14
  return await this._addJavaSamples();
17
15
 
@@ -49,16 +47,13 @@ module.exports = class SamplesTemplate extends require('../templateBase') {
49
47
  }
50
48
 
51
49
  async _addNodejsSamples() {
52
- const folders = (await this.getEnv()).folders;
53
- const dbFolder = join(this.projectPath, folders.db);
50
+ const { db, srv } = (await this.getEnv()).folders;
51
+ const dbFolder = join(this.projectPath, db);
54
52
  await copyFiles(join(__dirname, 'files', 'nodejs', 'db'), dbFolder, {}, this.options.force);
55
53
 
56
- const srvFolder = join(this.projectPath, folders.srv);
54
+ const srvFolder = join(this.projectPath, srv);
57
55
  await copyFiles(join(__dirname, 'files', 'nodejs', 'srv'), srvFolder, {
58
- dbFolder: folders.db.replace(/[\\/]+$/, '')
56
+ dbFolder: db.replace(/[\\/]+$/, '')
59
57
  }, this.options.force);
60
58
  }
61
-
62
- async finalize() {
63
- }
64
59
  }
@@ -8,19 +8,18 @@ module.exports = class TemplateBase {
8
8
  /**
9
9
  * @constructor
10
10
  */
11
- constructor(projectPath, generator, dirName) {
12
- if (generator.options && generator.options.for && typeof generator.options.for !== 'string') {
11
+ constructor(generator) {
12
+ if (generator.options?.for && typeof generator.options?.for !== 'string') {
13
13
  throw new Error('The --for argument must not be empty.')
14
14
  }
15
- this.projectPath = path.resolve(projectPath);
15
+ this.projectPath = cds.root;
16
16
  this.options = generator.options || {};
17
- this.projectPath = path.resolve(projectPath);
18
17
  this.generator = generator;
18
+ this.name = this.constructor.name.replace(/template/i, '').replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
19
19
 
20
20
  this.cwd = this.options.cwd || process.cwd();
21
- this.name = (dirName ? path.basename(dirName) : this.constructor.name.replace(/template/i, '').toLowerCase());
22
21
 
23
- this.projectName = path.basename(this.projectPath);
22
+ this.projectName = path.basename(cds.root);
24
23
  }
25
24
 
26
25
  /**
@@ -40,7 +39,7 @@ module.exports = class TemplateBase {
40
39
  }
41
40
 
42
41
  // eslint-disable-next-line no-unused-vars
43
- static hasFacet(env, projectPath, options) {
42
+ static hasFacet(env, options) {
44
43
  return true;
45
44
  }
46
45
 
@@ -52,7 +51,7 @@ module.exports = class TemplateBase {
52
51
  const cdsEnvVar = process.env.CDS_ENV
53
52
  process.env.CDS_ENV = profile
54
53
  try {
55
- return cds.env.for('cds', exists(this.projectPath) ? this.projectPath : this.cwd);
54
+ return cds.env.for('cds');
56
55
  } finally {
57
56
  cdsEnvVar ? process.env.CDS_ENV = cdsEnvVar : delete process.env.CDS_ENV
58
57
  }
@@ -93,15 +92,9 @@ module.exports = class TemplateBase {
93
92
  * @see PROJECT_TYPE
94
93
  * @returns PROJECT_TYPE for current project
95
94
  */
96
- async getProjectType() {
97
- if (exists(path.join(this.projectPath, 'pom.xml'))) {
98
- return PROJECT_TYPE.java;
99
- }
100
-
101
- if (exists(path.join(this.projectPath, 'package.json'))) {
102
- return PROJECT_TYPE.nodejs;
103
- }
104
-
95
+ getProjectType() {
96
+ if (exists('pom.xml')) return PROJECT_TYPE.java;
97
+ if (exists('package.json')) return PROJECT_TYPE.nodejs;
105
98
  return PROJECT_TYPE.unknown;
106
99
  }
107
100
  }
@@ -1,19 +1,15 @@
1
1
  const { join } = require('path')
2
- const ProjectReader = require('../../util/projectReader')
2
+ const { readProject } = require('../../util/projectReader')
3
3
  const { mergeJSON, sortDependencies } = require('../../util/merge')
4
4
 
5
5
  module.exports = class TogglesTemplate extends require('../templateBase') {
6
- constructor(projectPath, generator) {
7
- super(projectPath, generator, __dirname)
8
- this.projectReader = new ProjectReader(projectPath)
9
- }
10
6
 
11
7
  static hasFacet(env) {
12
8
  return !!env.requires?.toggles
13
9
  }
14
10
 
15
11
  async run() {
16
- const projectDescriptor = await this.projectReader.read(this.options)
12
+ const projectDescriptor = await readProject(this.options)
17
13
  const { for: forProfile, isNodejs, isJava } = projectDescriptor.cap
18
14
  if (isNodejs) {
19
15
  const packageJSONPath = join(this.projectPath, 'package.json')
@@ -28,4 +28,3 @@ xsuaa:
28
28
  redirect-uris:
29
29
  - https://*.<your-cluster-domain>/**
30
30
  {{/cap.hasApprouter}}
31
- config: xs-security.json
@@ -1 +1,4 @@
1
- {{- include "cap.service-instance" . }}
1
+ dependencies:
2
+ - name: service-instance
3
+ alias: xsuaa
4
+ version: 0.1.0
@@ -1,15 +1,11 @@
1
1
  const { join } = require('path')
2
- const ProjectReader = require('../../util/projectReader')
2
+ const { readProject } = require('../../util/projectReader')
3
3
  const { mergeJSON, mergeYAML } = require('../../util/merge')
4
- const { copyAndTrack, parseMd5File, writeMd5File } = require('../../util/md5Tracking')
4
+ const { parseMd5File, writeMd5File } = require('../../util/md5Tracking')
5
5
  const { srvNode, srvJava, xsuaa, mtxSidecar } = require('../_merging/registry-mta')
6
6
  const { PROJECT_TYPE } = require('../../constants')
7
7
 
8
8
  module.exports = class XsuaaTemplate extends require('../templateBase') {
9
- constructor(projectPath, generator) {
10
- super(projectPath, generator, __dirname)
11
- this.projectReader = new ProjectReader(projectPath)
12
- }
13
9
 
14
10
  static hasFacet(env) {
15
11
  return env.requires?.auth?.kind === 'xsuaa'
@@ -18,8 +14,8 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
18
14
  async run() {
19
15
  const { projectPath } = this
20
16
  const templatePath = join(__dirname, 'files')
21
- const projectDescriptor = await this.projectReader.read(this.options)
22
- const projectType = await this.getProjectType();
17
+ const projectDescriptor = await readProject(this.options)
18
+ const projectType = this.getProjectType();
23
19
  const { for: forProfile } = projectDescriptor.cap
24
20
 
25
21
  if (projectType === PROJECT_TYPE.java) {
@@ -40,7 +36,7 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
40
36
  }
41
37
 
42
38
  async runDependentMerging( context = {}) {
43
- const projectDescriptor = await this.projectReader.read(this.options)
39
+ const projectDescriptor = await readProject(this.options)
44
40
  const { isMultitenant, isExtensible, hasMta, hasHelm, isNodejs, isJava } = projectDescriptor.cap
45
41
 
46
42
  const cds = require('../../../../lib/compile')
@@ -50,7 +46,6 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
50
46
  } catch (error) { /* ignore */ }
51
47
  })() || { scopes: [], attributes: [], 'role-templates': [], 'authorities-inheritance': false }
52
48
  const mergingSemantics = { additions: xsSecurity.scopes.map(scope => ({
53
- ref: scope.name,
54
49
  in: 'scopes',
55
50
  where: [{
56
51
  property: 'name',
@@ -77,7 +72,7 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
77
72
 
78
73
  await mergeYAML(
79
74
  mtaYAMLPath,
80
- `${__dirname}/files/mta.yaml.hbs`,
75
+ join(__dirname, 'files', 'mta.yaml.hbs'),
81
76
  projectDescriptor,
82
77
  {
83
78
  additions,
@@ -94,22 +89,31 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
94
89
  context.projectPath = this.projectPath;
95
90
  context.oldTrackingData = context.newTrackingData = await parseMd5File(join(this.projectPath, 'chart', '.cds-add-helm-files.md5'));
96
91
  }
97
- await copyAndTrack(
92
+
93
+ // copy service-instance chart
94
+ const HelmTemplate = require(`../helm`);
95
+ const helmTemplate = new HelmTemplate(this.generator, context.oldTrackingData, context.newTrackingData);
96
+ isIndependentCommand = isIndependentCommand | await helmTemplate.addSubChart('service-instance');
97
+
98
+ // add dependency entry in chart.yaml
99
+ await mergeYAML(
100
+ join(this.projectPath, 'chart', 'Chart.yaml'),
98
101
  join(__dirname, 'files', 'xsuaa.yaml'),
99
- join(this.projectPath, 'chart', 'templates', 'xsuaa.yaml'),
100
- context
101
- )
102
+ projectDescriptor,
103
+ { additions: [{
104
+ in: `dependencies`,
105
+ where: [{
106
+ property: 'alias',
107
+ isEqualTo: 'xsuaa'
108
+ }]
109
+ }]}
110
+ );
111
+
102
112
  await mergeYAML(
103
113
  join(this.projectPath, 'chart', 'values.yaml'),
104
114
  join(__dirname, 'files', 'values.yaml.hbs'),
105
115
  projectDescriptor
106
116
  )
107
- // REVISIT: Shared xs-security.json location for Helm and MTA?
108
- await copyAndTrack(
109
- join(this.projectPath, 'xs-security.json'),
110
- join(this.projectPath, 'chart', 'xs-security.json'),
111
- context
112
- )
113
117
  if(isIndependentCommand) await writeMd5File(join(this.projectPath, 'chart', '.cds-add-helm-files.md5'), context.newTrackingData);
114
118
  }
115
119
 
@@ -118,13 +122,13 @@ module.exports = class XsuaaTemplate extends require('../templateBase') {
118
122
  // TODO: Find a generic solution for situation like these.
119
123
  if (isMultitenant) {
120
124
  const MultitenancyTemplate = require(`../multitenancy`)
121
- const multitenancyTemplate = new MultitenancyTemplate(this.projectPath, this.generator)
125
+ const multitenancyTemplate = new MultitenancyTemplate(this.generator)
122
126
  await multitenancyTemplate.runDependentMerging(context)
123
127
  }
124
128
 
125
129
  if (isExtensible) {
126
130
  const ExtensibilityTemplate = require(`../extensibility`)
127
- const extensibilityTemplate = new ExtensibilityTemplate(this.projectPath, this.generator)
131
+ const extensibilityTemplate = new ExtensibilityTemplate(this.generator)
128
132
  await extensibilityTemplate.runDependentMerging(context)
129
133
  }
130
134
  }
@@ -27,84 +27,3 @@ export interface CDSGenerator {
27
27
  */
28
28
  uiConfig?: any;
29
29
  }
30
-
31
- /**
32
- * This is a helper interface to provide support for different file system implementations.
33
- * Depending on the scenario different file systems will be used, e.g. Yeoman vs. native FS.
34
- */
35
- export interface FsUtil {
36
- /**
37
- * Writes the given string into a file.
38
- * @param filepath the absolute file path
39
- * @param content the string to serialize to JSON
40
- */
41
- writeFile(filePath: string, content: string, skipLogFile?: boolean): Promise<void>;
42
-
43
- /**
44
- * Writes the given object as JSON into a file.
45
- * @param filepath the absolute file path
46
- * @param object the object to serialize to JSON
47
- */
48
- writeJSON(filepath: string, object: any, options?: any): Promise<void>;
49
-
50
- /**
51
- * Writes YAML object
52
- * @param filepath the absolute file path
53
- * @param yaml the yaml object
54
- * @param skipFileLog do not log files if true
55
- */
56
- writeYAML(filepath: string, yaml: any, skipFileLog?: boolean): Promise<void>;
57
-
58
- /**
59
- * Reads a file from given path.
60
- * @param filePath the absolute file path
61
- * @param options optional read options
62
- * @returns the file content as string
63
- */
64
- readFile(filePath: string, options?: any): Promise<string>;
65
-
66
- /**
67
- * Reads a JSON object from a file.
68
- * @param filePath the absolute file path
69
- * @param projectDescriptor a project descriptor
70
- * @returns the file content as object
71
- */
72
- readJSON(filePath: string, projectDescriptor: any): Promise<any>;
73
-
74
- /**
75
- * Reads a JSON object from a file.
76
- * @param filePath the absolute file path
77
- * @returns the file content as object
78
- */
79
- readJSONC(filePath: string): Promise<any>;
80
-
81
- /**
82
- * Reads a JSON object from a file.
83
- * @param filePath the absolute file path
84
- * @param projectDescriptor a project descriptor
85
- * @returns the file content as yaml object
86
- */
87
- readYAML(filePath: string, projectDescriptor: any): Promise<any>;
88
-
89
- /**
90
- * Copies a file or folder from given source to destination.
91
- * @param source the source path
92
- * @param destination the destination path
93
- * @param options optional copy options
94
- * @param skipFileLog do not log files if true
95
- */
96
- copy(source: string, destination: string, options?: any, skipFileLog?: boolean): Promise<void>;
97
- }
98
-
99
- /**
100
- * Helper for managing npm related tasks
101
- * @deprecated
102
- */
103
- export interface NpmUtil {
104
- /**
105
- * Installs npm packages
106
- * @param cwd working folder
107
- * @param options npm install options, e.g. force
108
- */
109
- install(cwd?: string, options?: any): Promise<void>
110
- }
@@ -76,17 +76,17 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
76
76
 
77
77
  const { additions, overwrites, deletions, relationships } = semantics
78
78
  additions?.forEach(existence => {
79
- existenceMap.set(existence.ref, undefined)
80
- templateExistenceMap.set(existence.ref, undefined)
79
+ existenceMap.set(existence, undefined)
80
+ templateExistenceMap.set(existence, undefined)
81
81
  })
82
82
  deletions?.forEach(deletion => {
83
- existenceMap.set(deletion.item.ref, undefined)
83
+ existenceMap.set(deletion.item, undefined)
84
84
  })
85
85
  relationships?.forEach(relationship => {
86
86
  const [existence] = relationship.into
87
87
  const [insertExistence] = relationship.insert
88
- const ref = insertExistence.ref + ' -> ' + existence.ref
89
- existenceMap.set(ref, undefined)
88
+ const hash = insertExistence + ' -> ' + existence
89
+ existenceMap.set(hash, undefined)
90
90
  })
91
91
 
92
92
  let collectionStack = [target.contents]
@@ -99,17 +99,17 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
99
99
  let where = existence.in
100
100
  if (typeof where === 'string') return true // just a key path on the document root
101
101
  else where = where[0] // a more complex list of constraints, a sequence/array is involved
102
- const neededParent = dict.get(where.ref)?.node
102
+ const neededParent = dict.get(where)?.node
103
103
  return collectionStack.includes(neededParent) // lookbehind in parent collection stack
104
104
  // REVISIT: Only look behind until sequence is reached?
105
- }).forEach(({ where, ref }) => {
105
+ }).forEach(item => {
106
106
  const json = JSON.parse(String(node))
107
- const whereFulfilled = where.every(constraint =>
107
+ const whereFulfilled = item.where.every(constraint =>
108
108
  constraint.isEqualTo === _getProperty(json, constraint.property)
109
109
  )
110
110
  if (whereFulfilled) {
111
111
  const [collection] = collectionStack
112
- dict.set(ref, { json, node, index, collection })
112
+ dict.set(item, { json, node, index, collection })
113
113
  }
114
114
  })
115
115
  if (additions) _addToMap(additions)
@@ -212,7 +212,7 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
212
212
  overwrites?.forEach(({ item, withValue }) => {
213
213
  const keyPath = Array.isArray(item) && typeof item[0] === 'string' ? item[0] : typeof item === 'string' ? item : item[1]
214
214
  const inExistence = Array.isArray(item) && typeof item[0] === 'object' ? item[0] : typeof item === 'object' ? item : item[1]
215
- const node = inExistence && existenceMap.get(inExistence.ref) ? existenceMap.get(inExistence.ref).node : collectionStack[collectionStack.length - 1]
215
+ const node = inExistence && existenceMap.get(inExistence) ? existenceMap.get(inExistence).node : collectionStack[collectionStack.length - 1]
216
216
  const keys = keyPath.split('.')
217
217
  if (!node.getIn(keys)) return
218
218
  _getYAMLProperty(node, keys.slice(0, keys.length - 1).join('.'))
@@ -220,8 +220,8 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
220
220
  })
221
221
 
222
222
  // 4. Delete existences from the project (e.g. separate deployer module when adding mtx)
223
- deletions?.forEach(({ item: { ref }, relationships }) => {
224
- const existence = existenceMap.get(ref)
223
+ deletions?.forEach(({ item, relationships }) => {
224
+ const existence = existenceMap.get(item)
225
225
  if (!existence) return
226
226
  existence.collection.delete(existence.index)
227
227
 
@@ -250,7 +250,7 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
250
250
  let [,parent] = collectionStack
251
251
  additions?.filter(addition => {
252
252
  if (typeof addition.in === 'string' || Array.isArray(addition.in) && typeof addition.in[0] === 'string') return true
253
- const inExistence = Array.isArray(addition.in) ? existenceMap.get(addition.in[0].ref) : existenceMap.get(addition.in.ref)
253
+ const inExistence = Array.isArray(addition.in) ? existenceMap.get(addition.in[0]) : existenceMap.get(addition.in)
254
254
  return inExistence?.node === parent
255
255
  })
256
256
  .filter(addition => {
@@ -260,12 +260,10 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
260
260
  if (keys.length > 1) parent = collectionStack[keys.length]
261
261
  return _getYAMLProperty(parent, keyPath) === targetNode
262
262
  })
263
- .filter(({ ref }) => {
264
- return !existenceMap.get(ref)
265
- })
266
- .forEach(({ ref, at }) => {
267
- const templateNode = templateExistenceMap.get(ref).node
268
- at !== undefined ? targetNode.items.splice(at, 0, templateNode) : targetNode.add(templateNode)
263
+ .filter(item => !existenceMap.get(item))
264
+ .forEach(item => {
265
+ const templateNode = templateExistenceMap.get(item).node
266
+ item.at !== undefined ? targetNode.items.splice(item.at, 0, templateNode) : targetNode.add(templateNode)
269
267
  })
270
268
  }
271
269
  },
@@ -286,8 +284,8 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
286
284
  const relationship = semantics.relationships?.filter(r => r)
287
285
  .find(relationship => {
288
286
  const [existence, keyPath] = relationship.into
289
- if (!existenceMap.get(existence.ref)) return false
290
- const existingNode = _getYAMLProperty(existenceMap.get(existence.ref).node, keyPath)
287
+ if (!existenceMap.get(existence)) return false
288
+ const existingNode = _getYAMLProperty(existenceMap.get(existence).node, keyPath)
291
289
  return targetNode === existingNode
292
290
  })
293
291
  if (!relationship) return
@@ -295,12 +293,12 @@ async function mergeYAML(into, from, projectDescriptor, semantics = {}) {
295
293
  const intoKey = relationship.into[relationship.into.length - 1]
296
294
  const [existence, existenceKeyPath] = relationship.insert
297
295
  const missingPairs = [existence]
298
- .filter(({ref}) =>
299
- !targetJSON.some(item =>
300
- _getProperty(existenceMap.get(ref).json, existenceKeyPath) === item[intoKey]
296
+ .filter(item =>
297
+ !targetJSON.some(targetItem =>
298
+ _getProperty(existenceMap.get(item).json, existenceKeyPath) === targetItem[intoKey]
301
299
  )
302
300
  )
303
- .map(({ref}) => existenceMap.get(ref).node.get(intoKey))
301
+ .map(item => existenceMap.get(item).node.get(intoKey))
304
302
  missingPairs.forEach(pair => {
305
303
  targetNode.add({ [intoKey]: pair })
306
304
  })
@@ -1,7 +1,6 @@
1
- const path = require('path')
2
1
  const cds = require('../../cds')
2
+ const { path, read, exists } = cds.utils
3
3
  const { BuildTaskFactory } = cds.build
4
- const { read, exists } = cds.utils
5
4
  const P_LANGUAGE_JAVA = 'java'
6
5
  const P_LANGUAGE_NODEJS = 'nodejs'
7
6
  const DEFAULT_P_LANGUAGE = P_LANGUAGE_NODEJS
@@ -16,31 +15,34 @@ const BUILD_TASK_MTX_SIDECAR = "mtx-sidecar"
16
15
  const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
17
16
  const LOG = console
18
17
 
19
- module.exports = class ProjectReader {
18
+ module.exports = new class ProjectReader {
20
19
 
21
- constructor(projectPath) {
22
- this.projectPath = projectPath
20
+ constructor() {
21
+ this.readProject = this.readProject.bind(this)
23
22
  }
24
23
 
24
+ // REVISIT: There should be a better (and parallelizable) API provided by cds.env
25
25
  /**
26
26
  * Returns cds.env using 'production' profile by default as mta deployment is executed with having production profile set.
27
27
  */
28
- async getEnv(profile = 'production') {
28
+ getEnv(profile = 'production') {
29
29
  const cdsEnvVar = process.env.CDS_ENV
30
30
  process.env.CDS_ENV = profile
31
31
  try {
32
- return cds.env.for('cds', exists(this.projectPath) ? this.projectPath : this.cwd);
32
+ return cds.env.for('cds')
33
33
  } finally {
34
34
  cdsEnvVar ? process.env.CDS_ENV = cdsEnvVar : delete process.env.CDS_ENV
35
35
  }
36
36
  }
37
37
 
38
- async read(options) {
39
- const env = await this.getEnv();
38
+ async readProject(options) {
39
+ if (options?.cwd) cds.root = options.cwd
40
+
41
+ const env = this.getEnv()
40
42
  DEBUG && DEBUG({ env })
41
43
 
42
44
  const cap = await this._getCapDescriptor(env, options)
43
- const ui = await this._getUiDescriptor(env)
45
+ const ui = this._getUiDescriptor(env)
44
46
  const projectDescriptor = { cap, ui }
45
47
  this._validateDbFolders(cap)
46
48
 
@@ -49,28 +51,27 @@ module.exports = class ProjectReader {
49
51
  }
50
52
 
51
53
  _validateDbFolders(cap) {
52
- const { root, db: dbs, isJava } = cap
54
+ const { db: dbs, isJava } = cap
53
55
  if (isJava) {
54
- for (const db of dbs) if (!exists(path.join(root, db.path, 'package.json'))) {
56
+ for (const db of dbs) if (!exists(path.join(db.path, 'package.json'))) {
55
57
  LOG.error(`Missing 'package.json' in folder ${db.path} - 'cds add hana' adds configuration for SAP HANA`)
56
58
  }
57
59
  }
58
60
  }
59
61
 
60
62
  async _getCapDescriptor(env, options) {
61
- const projectPath = this.projectPath
62
- const _hasFacet = template => require(`../template/${template}`).hasFacet(env, projectPath, options)
63
+ const _hasFacet = template => require(`../template/${template}`).hasFacet(env, options)
63
64
  const cap = {
64
65
  for: options?.for,
65
66
  pLanguage: DEFAULT_P_LANGUAGE,
66
67
  srv: {},
67
68
  db: [],
68
69
  requires: [],
69
- get root() { return projectPath },
70
70
  get hasRequires() { return this.requires.length > 0 },
71
71
  get needsSidecar() { return (options?.add.has('hana') || this.db.length > 0) && (this.isJava || !this.isMultitenant) },
72
72
  get isNodejs() { return this.pLanguage === P_LANGUAGE_NODEJS },
73
73
  get isJava() { return this.pLanguage === P_LANGUAGE_JAVA },
74
+ get hasSidecar() { return exists('mtx', 'sidecar') },
74
75
  get isMultitenant() { return _hasFacet('multitenancy') },
75
76
  get isExtensible() { return _hasFacet('extensibility') },
76
77
  get hasHana() { return _hasFacet('hana') },
@@ -92,13 +93,15 @@ module.exports = class ProjectReader {
92
93
  const nullLogger = { debug: () => { }, log: () => { }, warn: () => { }, error: () => { }, write: () => { } }
93
94
 
94
95
  // no file path resolving as the directories might not exist - e.g. cds add will create the mtx/sidecar folder
95
- const buildTasks = await new BuildTaskFactory(nullLogger, cds).getTasks({ root: this.projectPath, resolve: false, mta: false })
96
+ // REVISIT: Leaner API from cds.build
97
+ const buildTasks = await new BuildTaskFactory(nullLogger, cds).getTasks({ root: cds.root, resolve: false, mta: false })
96
98
 
97
99
  await Promise.all(buildTasks.map(async (task) => {
98
- const srcPath = path.resolve(this.projectPath, task.src)
100
+ const srcPath = path.resolve(cds.root, task.src)
99
101
  const srcFolder = path.basename(srcPath)
100
102
  const relDestPath = path.join(env?.build?.target ?? "gen", task.dest || task.src)?.replace(/\\/g, '/')
101
103
 
104
+
102
105
  switch (task.for) {
103
106
  case BUILD_TASK_HANA:
104
107
  // check whether we have a hana service binding defined as a hana build task is returned by default for compatibility reasons if neither hana, nor sqlite has been defined
@@ -158,7 +161,7 @@ module.exports = class ProjectReader {
158
161
  throw new Error("Projects using the classic CAP Java SDK are not supported")
159
162
  }
160
163
 
161
- const descriptor = { archiveName: path.basename(this.projectPath) + "-exec.jar" }
164
+ const descriptor = { archiveName: path.basename(cds.root) + "-exec.jar" }
162
165
  const archiveName = pomJson?.project?.artifactId?.[0]
163
166
  const suffix = pomJson?.project?.packaging?.[0]
164
167
  if (archiveName) descriptor.archiveName = archiveName + '-exec.' + suffix
@@ -166,8 +169,8 @@ module.exports = class ProjectReader {
166
169
  }
167
170
  }
168
171
 
169
- async _getUiDescriptor(env) {
170
- const absoluteAppPath = path.join(this.projectPath, env.folders.app)
172
+ _getUiDescriptor(env) {
173
+ const absoluteAppPath = path.join(cds.root, env.folders.app)
171
174
  return {
172
175
  appPath: exists(absoluteAppPath) ? env.folders.app : null
173
176
  }
@@ -180,7 +183,7 @@ module.exports = class ProjectReader {
180
183
 
181
184
  // 1.1. java - use parent pom.xml
182
185
  if (cap.pLanguage === P_LANGUAGE_JAVA) {
183
- const pomXmlPath = path.join(this.projectPath, 'pom.xml')
186
+ const pomXmlPath = path.join(cds.root, 'pom.xml')
184
187
  try {
185
188
  if (exists(pomXmlPath)) {
186
189
  const pomJson = await this._parseXml(pomXmlPath)
@@ -213,7 +216,7 @@ module.exports = class ProjectReader {
213
216
 
214
217
  // 1.2. use package.json
215
218
  if (!cap.appName) {
216
- const packageJsonPath = path.join(this.projectPath, 'package.json')
219
+ const packageJsonPath = path.join(cds.root, 'package.json')
217
220
  if (exists(packageJsonPath)) {
218
221
  try {
219
222
  const { name, version, description } = await read(packageJsonPath)
@@ -233,7 +236,7 @@ module.exports = class ProjectReader {
233
236
  }
234
237
 
235
238
  // 2. use project name and static default values
236
- cap.appName = cap.appName ?? path.basename(this.projectPath)
239
+ cap.appName = cap.appName ?? path.basename(cds.root)
237
240
  cap.appDescription = cap.appDescription ?? cap.appName + " application"
238
241
  cap.appId = cap.appId ?? cap.appName
239
242
  cap.appVersion = cap.appVersion ?? '1.0.0'
@@ -304,7 +307,7 @@ module.exports = class ProjectReader {
304
307
  */
305
308
  async _parseXml(pomXmlPath) {
306
309
  const xmljs = require('xml-js');
307
- let xmljsParse = {};
310
+ const xmljsParse = {};
308
311
  try {
309
312
  const xml = await read(pomXmlPath, 'utf-8')
310
313
  xmljsParse.project = JSON.parse(await xmljs.xml2json(xml, {