@sap/cds-dk 7.1.1 → 7.3.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.
- package/bin/activate.js +2 -2
- package/bin/build.js +5 -2
- package/bin/compile/index.js +13 -5
- package/bin/env.js +0 -3
- package/bin/extend.js +25 -3
- package/bin/gen.js +27 -0
- package/bin/help.js +2 -1
- package/bin/import.js +7 -3
- package/bin/push.js +10 -6
- package/bin/watch.js +3 -4
- package/bin/watched.js +2 -3
- package/lib/bind/bindingUtil.js +1 -0
- package/lib/build/{buildTaskHandler.js → buildPlugin.js} +25 -30
- package/lib/build/buildTaskEngine.js +84 -89
- package/lib/build/buildTaskFactory.js +23 -44
- package/lib/build/buildTaskProvider.js +24 -19
- package/lib/build/buildTaskProviderFactory.js +176 -66
- package/lib/build/constants.js +6 -5
- package/lib/build/index.js +4 -3
- package/lib/build/provider/{buildTaskHandlerEdmx.js → edmxBuildPlugin.js} +6 -6
- package/lib/build/provider/{buildTaskHandlerFeatureToggles.js → featureToggleBuildPlugin.js} +3 -3
- package/lib/build/provider/fiori/index.js +5 -6
- package/lib/build/provider/hana/index.js +67 -36
- package/lib/build/provider/{buildTaskHandlerInternal.js → internalBuildPlugin.js} +11 -11
- package/lib/build/provider/{buildTaskProviderInternal.js → internalBuildTaskProvider.js} +42 -53
- package/lib/build/provider/java/index.js +24 -24
- package/lib/build/provider/mtx/index.js +4 -4
- package/lib/build/provider/mtx/resourcesTarBuilder.js +8 -8
- package/lib/build/provider/mtx-extension/index.js +17 -9
- package/lib/build/provider/mtx-sidecar/index.js +17 -11
- package/lib/build/provider/nodejs/index.js +63 -29
- package/lib/build/util.js +11 -19
- package/lib/client/auth_manager.js +6 -12
- package/lib/client/base_command.js +1 -1
- package/lib/client/download_legacy.js +48 -0
- package/lib/client/params.js +5 -0
- package/lib/client/push.js +41 -1
- package/lib/client/settings_manager.js +12 -3
- package/lib/compile/openapi/csdl2openapi.js +30 -21
- package/lib/compile/openapi/index.js +7 -3
- package/lib/env/schema.js +36 -0
- package/lib/env/schemas/cds-rc.json +101 -0
- package/lib/import/common.js +24 -11
- package/lib/import/importapi.js +2 -2
- package/lib/import/message.js +3 -1
- package/lib/import/odata/v4parser.js +23 -7
- package/lib/index.js +5 -0
- package/lib/init/constants.js +9 -12
- package/lib/init/index.js +86 -74
- package/lib/init/template/_merging/registry-mta.js +21 -12
- package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +3 -2
- package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +0 -5
- package/lib/init/template/cf-manifest/index.js +2 -2
- package/lib/init/template/connectivity/index.js +5 -4
- package/lib/init/template/destination/index.js +2 -5
- package/lib/init/template/h2/index.js +16 -0
- package/lib/init/template/hana/index.js +2 -3
- package/lib/init/template/helm/chart/values.root.schema.json +0 -5
- package/lib/init/template/helm/chart/values.schema.json +0 -4
- package/lib/init/template/helm/index.js +1 -1
- package/lib/init/template/html5-repo/index.js +27 -3
- package/lib/init/template/lint/index.js +3 -7
- package/lib/init/template/mta/index.js +9 -3
- package/lib/init/template/multitenancy/files/package.json.hbs +0 -3
- package/lib/init/template/multitenancy/index.js +2 -0
- package/lib/init/template/nodejs/index.js +3 -3
- package/lib/init/template/pipeline/index.js +2 -4
- package/lib/init/template/postgres/files/mta.yaml.hbs +27 -0
- package/lib/init/template/postgres/files/package.json.hbs +5 -0
- package/lib/init/template/postgres/index.js +52 -0
- package/lib/init/template/sample/index.js +5 -7
- package/lib/init/template/sqlite/index.js +8 -3
- package/lib/init/template/tiny-sample/index.js +16 -30
- package/lib/init/util/mvn.js +25 -1
- package/lib/init/util/projectReader.js +2 -7
- package/lib/init/util/template.js +7 -6
- package/lib/util/pruneAxiosErrors.js +22 -8
- package/lib/watch/node-watch.js +6 -1
- package/npm-shrinkwrap.json +2207 -384
- package/package.json +3 -3
- package/lib/init/template/auditlog/files/mta.yaml.hbs +0 -14
- package/lib/init/template/auditlog/files/package.json.hbs +0 -27
- package/lib/init/template/auditlog/index.js +0 -74
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const cds = require('../../../cds')
|
|
5
|
-
const
|
|
5
|
+
const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
6
6
|
const ResourcesTarProvider = require('./resourcesTarBuilder')
|
|
7
7
|
const { FOLDER_GEN } = require('../../constants')
|
|
8
8
|
const { BuildError } = require('../../util')
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class MtxBuildPlugin extends EdmxBuildPlugin {
|
|
11
11
|
get priority() {
|
|
12
12
|
// should be scheduled after 'hana' build tasks are finished
|
|
13
|
-
return
|
|
13
|
+
return EdmxBuildPlugin.PRIORITY_MIN_VALUE
|
|
14
14
|
}
|
|
15
15
|
init() {
|
|
16
16
|
super.init()
|
|
@@ -40,4 +40,4 @@ Upgrade to @sap/cds-mtxs following the migration guide https://cap.cloud.sap/doc
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
module.exports =
|
|
43
|
+
module.exports = MtxBuildPlugin
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const cds = require('../../../cds')
|
|
3
|
-
const
|
|
3
|
+
const InternalBuildPlugin = require('../internalBuildPlugin')
|
|
4
4
|
|
|
5
5
|
const { BUILD_TASK_HANA } = require('../../constants')
|
|
6
6
|
const fs = require('fs')
|
|
7
|
-
const { WARNING } =
|
|
7
|
+
const { WARNING } = InternalBuildPlugin
|
|
8
8
|
const DEFAULT_TAR_NAME = "resources.tgz"
|
|
9
9
|
|
|
10
10
|
class ResourcesTarBuilder {
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
11
|
+
constructor(plugin) {
|
|
12
|
+
this._plugin = plugin
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
get
|
|
15
|
+
get plugin() { return this._plugin }
|
|
16
16
|
|
|
17
17
|
async createTar(dest, model) {
|
|
18
18
|
const { root, resources } = await this._getResources(model)
|
|
19
19
|
if (resources.length === 0) {
|
|
20
20
|
// packTarArchive fails otherwise
|
|
21
|
-
this.
|
|
21
|
+
this.plugin.pushMessage("No deployment resources found - skip resources.tgz", WARNING)
|
|
22
22
|
return
|
|
23
23
|
}
|
|
24
24
|
await this.writeTarFile(path.join(dest, DEFAULT_TAR_NAME), root, resources)
|
|
@@ -36,7 +36,7 @@ class ResourcesTarBuilder {
|
|
|
36
36
|
async writeTarFile(tarFile, root, resources) {
|
|
37
37
|
const { tar } = require('../../../../lib').utils
|
|
38
38
|
await tar.czfd(tarFile, root, resources)
|
|
39
|
-
this.
|
|
39
|
+
this.plugin.pushFile(tarFile)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
async _getResources(model) {
|
|
@@ -83,7 +83,7 @@ class ResourcesTarBuilder {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async _getHanaTenantDbDest() {
|
|
86
|
-
const hanaTask = this.
|
|
86
|
+
const hanaTask = this.plugin.context.tasks.find(task => task.for === BUILD_TASK_HANA)
|
|
87
87
|
return hanaTask?.dest
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -2,12 +2,12 @@ const path = require('path')
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const InternalBuildPlugin = require('../internalBuildPlugin')
|
|
6
6
|
const { FOLDER_GEN, EXTENSION_POINT_VALIDATION } = require('../../constants')
|
|
7
7
|
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
8
8
|
const { BuildError, BuildMessage } = require('../../util')
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class MtxExtensionBuildPlugin extends InternalBuildPlugin {
|
|
11
11
|
init() {
|
|
12
12
|
super.init()
|
|
13
13
|
if (cds.env.build.target === '.') {
|
|
@@ -37,12 +37,10 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
37
37
|
extModel.requires.length = 0
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
41
|
+
// perform all model operations before the linter is called as the linter
|
|
42
|
+
// will modify the model causing the creation of 'unresolved' associations
|
|
44
43
|
await this.compileToJson(extModel, path.join(destExt, 'extension.csn'))
|
|
45
|
-
|
|
46
44
|
await this.collectLanguageBundles(extModel, path.join(destExt, 'i18n'))
|
|
47
45
|
|
|
48
46
|
const files = Object.keys(await cds.deploy.resources(model))
|
|
@@ -56,6 +54,16 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
56
54
|
})
|
|
57
55
|
)
|
|
58
56
|
}
|
|
57
|
+
if (!this.hasBuildOption(EXTENSION_POINT_VALIDATION, false)) {
|
|
58
|
+
try {
|
|
59
|
+
// throws error in case of linting errors
|
|
60
|
+
this._lintExtModel(extModel, model)
|
|
61
|
+
} catch(error) {
|
|
62
|
+
// cleanup existing files in case of an error
|
|
63
|
+
await fs.promises.rm(this.task.dest, { recursive: true })
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
const packageJson = path.join(src, 'package.json')
|
|
@@ -90,7 +98,7 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
90
98
|
|
|
91
99
|
return paths
|
|
92
100
|
}
|
|
93
|
-
|
|
101
|
+
|
|
94
102
|
_lintExtModel(extModel, model) {
|
|
95
103
|
const linter = this._linter()
|
|
96
104
|
if (!linter) {
|
|
@@ -128,4 +136,4 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
128
136
|
}
|
|
129
137
|
}
|
|
130
138
|
|
|
131
|
-
module.exports =
|
|
139
|
+
module.exports = MtxExtensionBuildPlugin
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const cds = require('../../../cds')
|
|
3
3
|
const fs = require('fs')
|
|
4
|
-
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION } = require('../../constants')
|
|
5
|
-
const
|
|
4
|
+
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION, CONTENT_EDMX, FLAVOR_LOCALIZED_EDMX } = require('../../constants')
|
|
5
|
+
const NodejsBuildPlugin = require('../nodejs')
|
|
6
6
|
const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
|
|
7
|
-
const { ERROR } =
|
|
7
|
+
const { ERROR } = NodejsBuildPlugin
|
|
8
8
|
const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
|
|
9
9
|
|
|
10
10
|
const DEFAULT_MAIN_FOLDER = "_main"
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
13
13
|
get priority() {
|
|
14
14
|
// should be scheduled after 'hana' build tasks are finished
|
|
15
|
-
return
|
|
15
|
+
return NodejsBuildPlugin.PRIORITY_MIN_VALUE
|
|
16
16
|
}
|
|
17
17
|
init() {
|
|
18
18
|
super.init()
|
|
@@ -47,7 +47,6 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
47
47
|
const destSidecarSrc = path.join(destSidecar, cds.env.folders.srv)
|
|
48
48
|
const model = this._compileSidecarSync(sidecarEnv)
|
|
49
49
|
await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
|
|
50
|
-
|
|
51
50
|
await this.collectLanguageBundles(model, path.join(destSidecarSrc, getI18nDefaultFolder()))
|
|
52
51
|
await this.copySrvContent(this.task.src, destSidecar, destSidecar)
|
|
53
52
|
}
|
|
@@ -76,16 +75,23 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
76
75
|
const destRoot = this.task.dest
|
|
77
76
|
const destMain = path.join(destRoot, main)
|
|
78
77
|
const destMainSrv = path.join(destMain, cds.env.folders.srv)
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
78
|
+
const model = await this.model()
|
|
79
|
+
if (!model) {
|
|
81
80
|
return
|
|
82
81
|
}
|
|
83
|
-
const { dictionary, sources } = await this.compileAll(
|
|
82
|
+
const { dictionary, sources } = await this.compileAll(model, destMainSrv, destMain)
|
|
84
83
|
await this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain)
|
|
85
84
|
|
|
85
|
+
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
86
|
+
//inferred model expected by cds.compile.to.edmx()
|
|
87
|
+
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) } // disabled by default
|
|
88
|
+
const baseModel = cds.compiler.compileSources({ 'base.json': dictionary.base }, { messages: this.messages })
|
|
89
|
+
await this.compileToEdmx(baseModel, path.join(destMainSrv, 'odata', cds.env.odata.version), compileOptions)
|
|
90
|
+
}
|
|
91
|
+
|
|
86
92
|
// create resources TAR
|
|
87
93
|
// resources are determined based on available database build task, SQLite as fallback
|
|
88
|
-
await new ResourcesTarProvider(this).createTar(destMain,
|
|
94
|
+
await new ResourcesTarProvider(this).createTar(destMain, model)
|
|
89
95
|
|
|
90
96
|
// copy package.json and .cdsrc.json from project root
|
|
91
97
|
await this._copyMainConfigFiles(cds.root, destMain)
|
|
@@ -139,4 +145,4 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
139
145
|
return promises
|
|
140
146
|
}
|
|
141
147
|
}
|
|
142
|
-
module.exports =
|
|
148
|
+
module.exports = MtxSidecarBuildPlugin
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
|
-
const
|
|
4
|
+
const EdmxBuildPlugin = require('../edmxBuildPlugin')
|
|
5
5
|
const { BuildError } = require('../../util')
|
|
6
|
-
const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX,
|
|
7
|
-
|
|
8
|
-
const { WARNING } =
|
|
6
|
+
const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
|
|
7
|
+
CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX } = require('../../constants')
|
|
8
|
+
const { WARNING } = EdmxBuildPlugin
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
11
11
|
init() {
|
|
12
12
|
super.init()
|
|
13
13
|
if (this.task.options.compileDest) {
|
|
@@ -28,8 +28,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
28
28
|
async build() {
|
|
29
29
|
const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, cds.env.folders.srv)
|
|
30
30
|
const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
|
|
31
|
-
|
|
32
|
-
if (cds.env.odata?.version === ODATA_VERSION_V2) {
|
|
31
|
+
if (cds.env.odata.version === ODATA_VERSION_V2) {
|
|
33
32
|
// log warning as nodejs is only supporting odata version V4
|
|
34
33
|
this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", WARNING)
|
|
35
34
|
}
|
|
@@ -44,39 +43,74 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
44
43
|
// collect and write language bundles into single i18n.json file
|
|
45
44
|
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
46
45
|
|
|
47
|
-
if (this.hasBuildOption(CONTENT_EDMX,
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
47
|
+
//REVISIT: inferred model expected by cds.compile.to.edmx()
|
|
48
|
+
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
|
|
49
|
+
const baseModel = cds.compiler.compileSources({ 'base.json': dictionary.base }, { messages: this.messages })
|
|
50
|
+
await this.compileToEdmx(baseModel, path.join(this.destSrv, 'odata', cds.env.odata.version), compileOptions)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
|
|
53
54
|
const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
54
55
|
await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
|
|
55
|
-
|
|
56
|
+
if (this.context.options.ws) {
|
|
57
|
+
await this._addWorkspaceDependencies(destRoot)
|
|
58
|
+
}
|
|
56
59
|
}
|
|
57
60
|
return this._result
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
// REVISIT: fix for
|
|
61
|
-
// Instead of adding relative paths we may better delete the entire entry as relative paths
|
|
62
|
-
// cannot be resolved in cloud deployments anyway.
|
|
63
|
+
// REVISIT: fix for simple mono repos using npm workspaces
|
|
63
64
|
// Support for HANA native artifacts defined in sub-modules required
|
|
64
|
-
async
|
|
65
|
-
const packageJson = path.join(dest, 'package.json')
|
|
65
|
+
async _addWorkspaceDependencies(dest) {
|
|
66
66
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
for (let dependency in dependencies) {
|
|
71
|
-
if (dependency.startsWith('@capire/')) {
|
|
72
|
-
dependencies[dependency] = path.relative(dest, path.dirname(require.resolve(dependency + '/package.json')))
|
|
73
|
-
changed = true
|
|
74
|
-
}
|
|
67
|
+
const workspaceRoot = path.resolve(cds.root, this.getBuildOption("workspaceRoot") ?? '.')
|
|
68
|
+
if (workspaceRoot !== cds.root && !fs.existsSync(workspaceRoot)) {
|
|
69
|
+
throw new BuildError(`The given workspaceRoot path ${workspaceRoot} does not exist`)
|
|
75
70
|
}
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const packageJson = require(path.join(dest, 'package.json'))
|
|
72
|
+
const workspaces = workspaceRoot === cds.root ? packageJson.workspaces : require(path.join(workspaceRoot, 'package.json')).workspaces
|
|
73
|
+
if (workspaces?.length) {
|
|
74
|
+
this.logger._debug && this.logger.debug(`Existing npm workspace configuration '${JSON.stringify(workspaces, ' ')}'`)
|
|
75
|
+
const { tar } = require('../../../../lib').utils
|
|
76
|
+
const { dependencies } = packageJson
|
|
77
|
+
const folders = fs.readdirSync(workspaceRoot).filter(res => {
|
|
78
|
+
try {
|
|
79
|
+
return fs.lstatSync(path.join(workspaceRoot, res)).isDirectory()
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
this.logger._debug && this.logger.debug(`Existing workspace subfolders '${JSON.stringify(folders, ' ')}'`)
|
|
85
|
+
|
|
86
|
+
const workspaceNames = new Set()
|
|
87
|
+
let changed
|
|
88
|
+
for (const workspace of workspaces) {
|
|
89
|
+
//REVISIT: workspaces must only use simple glob like 'foo*', nested folders are not supported
|
|
90
|
+
const regex = new RegExp('^' + workspace.split('/').pop().replace(/\*/g, '.*') + '$')
|
|
91
|
+
for (const folder of folders) {
|
|
92
|
+
if (!workspaceNames.has(folder) && folder.match(regex)) {
|
|
93
|
+
workspaceNames.add(folder)
|
|
94
|
+
const plugin = require(require.resolve(path.join(workspaceRoot, folder, 'package.json')))
|
|
95
|
+
const tarFile = `${plugin.name.replace(/\//g, '-').replace(/@/, '')}-${plugin.version}.tgz`
|
|
96
|
+
if (dependencies[plugin.name] === '*') {
|
|
97
|
+
const tarFilePath = path.join(dest, tarFile)
|
|
98
|
+
dependencies[plugin.name] = `file:${tarFile}`
|
|
99
|
+
await tar.czfd(tarFilePath, path.join(workspaceRoot, folder))
|
|
100
|
+
this.pushFile(tarFilePath)
|
|
101
|
+
changed = true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (changed) {
|
|
107
|
+
return this.write(JSON.stringify(packageJson, null, ' ')).to(path.join(dest, 'package.json'))
|
|
108
|
+
}
|
|
78
109
|
}
|
|
79
|
-
} catch (e) {
|
|
110
|
+
} catch (e) {
|
|
111
|
+
this.pushMessage(`Failed to add npm workspace dependencies: ${e.message}`)
|
|
112
|
+
this.logger.debug(e)
|
|
113
|
+
}
|
|
80
114
|
}
|
|
81
115
|
|
|
82
116
|
async clean() {
|
|
@@ -91,7 +125,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
91
125
|
|
|
92
126
|
async _copyNativeContent(srcRoot, srcSrv, destRoot, destSrv) {
|
|
93
127
|
// project/srv/** -> 'gen/srv/srv/**'
|
|
94
|
-
const filesFilter = await this.copySrvContent(srcSrv, destRoot, this.hasBuildOption('flatDeployLayout', true)? destRoot : destSrv)
|
|
128
|
+
const filesFilter = await this.copySrvContent(srcSrv, destRoot, this.hasBuildOption('flatDeployLayout', true) ? destRoot : destSrv)
|
|
95
129
|
|
|
96
130
|
// project/* -> 'gen/srv/*'
|
|
97
131
|
await this.copyProjectRootContent(srcRoot, destRoot, (entry) => !filesFilter.includes(path.basename(entry)))
|
|
@@ -176,4 +210,4 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
176
210
|
}
|
|
177
211
|
}
|
|
178
212
|
|
|
179
|
-
module.exports =
|
|
213
|
+
module.exports = NodejsBuildPlugin
|
package/lib/build/util.js
CHANGED
|
@@ -119,6 +119,14 @@ function getDefaultModelOptions() {
|
|
|
119
119
|
return _pushModelPaths(modelPaths)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* For valid paths replace '\\' with '/' and remove trailing '/'. Otherwise return as is
|
|
124
|
+
* @param {*} dir
|
|
125
|
+
*/
|
|
126
|
+
function normalizePath(dir) {
|
|
127
|
+
return typeof dir === "string" ? dir.replace(/\\/g, '/').replace(/\/$/, '') : dir
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
function _pushModelPaths(...modelPaths) {
|
|
123
131
|
const model = new Set()
|
|
124
132
|
// may contain nested arrays
|
|
@@ -131,7 +139,7 @@ function _pushModelPaths(...modelPaths) {
|
|
|
131
139
|
if (roots.includes(m)) {
|
|
132
140
|
const dir = path.resolve(cds.root, m)
|
|
133
141
|
if (fs.existsSync(dir)) {
|
|
134
|
-
model.add(m
|
|
142
|
+
model.add(m)
|
|
135
143
|
} else if (fs.existsSync(dir + FILE_EXT_CDS)) { //might be cds file name, compatibility to old build configs
|
|
136
144
|
model.add(m)
|
|
137
145
|
}
|
|
@@ -140,7 +148,7 @@ function _pushModelPaths(...modelPaths) {
|
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
})
|
|
143
|
-
return [...model]
|
|
151
|
+
return [...model].map(m => normalizePath(m))
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
function flatten(modelPaths) {
|
|
@@ -154,22 +162,6 @@ function flatten(modelPaths) {
|
|
|
154
162
|
}, [])
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
/**
|
|
158
|
-
* Copy a file or directory. The directory can have contents.
|
|
159
|
-
* REVISIT: 'fs.promises.cp' replacement for nodejs 14
|
|
160
|
-
* @param src <String> Note that if src is a directory it will copy everything inside of this directory, not the entire directory itself.
|
|
161
|
-
* @param dest <String> Note that if src is a file, dest cannot be a directory.
|
|
162
|
-
*/
|
|
163
|
-
async function copy(src, dest) {
|
|
164
|
-
if ((await fs.promises.stat(src)).isDirectory()) {
|
|
165
|
-
const entries = await fs.promises.readdir(src)
|
|
166
|
-
return Promise.all(entries.map(async each => copy(path.join(src, each), path.join(dest, each))))
|
|
167
|
-
} else {
|
|
168
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true })
|
|
169
|
-
return fs.promises.copyFile(src, dest)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
165
|
function getI18nDefaultFolder() {
|
|
174
166
|
// keep '_i18n' as preferred folder for compatibility reasons
|
|
175
167
|
if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
|
|
@@ -276,9 +268,9 @@ module.exports = {
|
|
|
276
268
|
resolveRequiredSapModels,
|
|
277
269
|
getDefaultModelOptions,
|
|
278
270
|
flatten,
|
|
279
|
-
copy,
|
|
280
271
|
getI18nDefaultFolder,
|
|
281
272
|
isClassicMtx,
|
|
273
|
+
normalizePath,
|
|
282
274
|
BuildMessage,
|
|
283
275
|
BuildError
|
|
284
276
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const axios = require('axios');
|
|
2
2
|
|
|
3
|
+
require('../util/pruneAxiosErrors');
|
|
4
|
+
|
|
3
5
|
const cds = require('../cds');
|
|
4
6
|
const DEBUG = cds.debug('cli');
|
|
5
7
|
const { SettingsManager } = require('./settings_manager');
|
|
@@ -54,16 +56,7 @@ function* reqParams(params) {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
async function retrieveTokenOrPasscodeUrl(params) {
|
|
57
|
-
if (params.has('token')) {
|
|
58
|
-
if (params.get('renewLogin') || params.get('tokenExpirationDate') <= Date.now()) {
|
|
59
|
-
DEBUG?.((params.get('renewLogin') ? 'Renewing' : 'Refreshing expired') + ' authentication token');
|
|
60
|
-
params.delete('token');
|
|
61
|
-
params.delete('tokenExpirationDate');
|
|
62
|
-
} else {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (!params.has('passcode') && !params.has('refreshToken') && params.has('passcodeUrl')) {
|
|
59
|
+
if (params.has('token') || !params.has('refreshToken') && !params.has('passcode') && params.has('passcodeUrl')) {
|
|
67
60
|
return;
|
|
68
61
|
}
|
|
69
62
|
|
|
@@ -76,7 +69,7 @@ async function retrieveTokenOrPasscodeUrl(params) {
|
|
|
76
69
|
DEBUG?.(`Getting authentication token or passcode URL from ${method.toUpperCase()} ${(params.obfuscateQueryParams(url))}`);
|
|
77
70
|
response = await axios[method](url, data);
|
|
78
71
|
} catch (error) {
|
|
79
|
-
if (error.
|
|
72
|
+
if (error.status === 404) { // may represent 405 in case of wrong method
|
|
80
73
|
continue;
|
|
81
74
|
}
|
|
82
75
|
throw getMessage('Failed to get authentication token', { error });
|
|
@@ -95,7 +88,8 @@ async function retrieveTokenOrPasscodeUrl(params) {
|
|
|
95
88
|
|
|
96
89
|
if (authData.access_token) {
|
|
97
90
|
addTokenInfo(params, authData);
|
|
98
|
-
}
|
|
91
|
+
}
|
|
92
|
+
if (authData.passcode_url) {
|
|
99
93
|
addUrlInfo(params, authData);
|
|
100
94
|
}
|
|
101
95
|
}
|
|
@@ -20,7 +20,7 @@ module.exports = class BaseCommand {
|
|
|
20
20
|
static handleHttpError(error, params, { url, command } = {}) {
|
|
21
21
|
url = url ?? params.get('appUrl');
|
|
22
22
|
const prefix = `Request to ${url} failed`;
|
|
23
|
-
switch (error.
|
|
23
|
+
switch (error.status) {
|
|
24
24
|
case 401:
|
|
25
25
|
throw getMessage(`${prefix}: invalid authentication.\nRetry with valid passcode${
|
|
26
26
|
params.has('passcodeUrl') ? ` from ${params.get('passcodeUrl')}` : ''
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const cds = require ('../cds')
|
|
2
|
+
const { tar } = cds.utils;
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { join } = require('path');
|
|
6
|
+
const axios = require('axios');
|
|
7
|
+
|
|
8
|
+
const { login } = require('./auth_manager');
|
|
9
|
+
const BaseCommand = require('./base_command');
|
|
10
|
+
|
|
11
|
+
const DOWNLOAD_FILE_NAME = 'migrated_projects.tgz'; // TODO try to add tenant later, if possible
|
|
12
|
+
|
|
13
|
+
module.exports = class Download extends BaseCommand {
|
|
14
|
+
|
|
15
|
+
static async run(paramValues) {
|
|
16
|
+
if (!tar) {
|
|
17
|
+
throw `cds pull requires @sap/cds version >= 6.2. Current version is ${cds.version}\n`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const params = await login(paramValues);
|
|
21
|
+
|
|
22
|
+
const projectFolder = params.get('projectFolder');
|
|
23
|
+
const downloadPath = join(projectFolder, DOWNLOAD_FILE_NAME);
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(downloadPath) && !(await require('./helper/question').askBooleanQuestion(`File ${DOWNLOAD_FILE_NAME} already exists. Continue (yN)? `, false, false))) {
|
|
26
|
+
console.log('Download cancelled');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(`Downloading migrated @sap/mtx extension project(s)`);
|
|
31
|
+
const downloadedTgz = await this.getTgz(params);
|
|
32
|
+
await fs.promises.writeFile(downloadPath, downloadedTgz);
|
|
33
|
+
|
|
34
|
+
//await tar.xz(baseModelTgz).to(target); // expand if feature is requested later
|
|
35
|
+
|
|
36
|
+
console.log(`Finished.`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async getTgz(params) {
|
|
40
|
+
const tagRule = params.get('tagRule');
|
|
41
|
+
const downloadUrl = `${(params.get('appUrl'))}/-/cds/extensibility/getMigratedProjects`;
|
|
42
|
+
const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
|
|
43
|
+
|
|
44
|
+
return axios.post(downloadUrl, { tagRule }, options)
|
|
45
|
+
.then(response => Buffer.from(response.data))
|
|
46
|
+
.catch(error => this.handleHttpError(error, params, { url: downloadUrl }));
|
|
47
|
+
}
|
|
48
|
+
}
|
package/lib/client/params.js
CHANGED
|
@@ -162,6 +162,8 @@ class BasicParams {
|
|
|
162
162
|
this.projectFolder = new Param('projectFolder', '');
|
|
163
163
|
this.directory = this.projectFolder.newAlias('directory');
|
|
164
164
|
|
|
165
|
+
this.tagRule = new Param('tagRule', '');
|
|
166
|
+
|
|
165
167
|
// - non-persisted, internal
|
|
166
168
|
this.reqAuth = new Param('reqAuth', null, { obfuscate: ObfuscationLevel.full, internal: true });
|
|
167
169
|
this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
|
|
@@ -222,6 +224,9 @@ class AllParams extends BasicParams {
|
|
|
222
224
|
this["delete-settings"] = this.deleteSettings.newAlias('delete-settings');
|
|
223
225
|
this.clearInvalid = new Param('clearInvalid', false);
|
|
224
226
|
this["clear-invalid"] = this.clearInvalid.newAlias('clear-invalid');
|
|
227
|
+
|
|
228
|
+
// download for mxts
|
|
229
|
+
this["download-migrated-projects"] = new Param('download-migrated-projects', false)
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
232
|
|
package/lib/client/push.js
CHANGED
|
@@ -9,6 +9,13 @@ const { getMessage } = require('./helper/logging');
|
|
|
9
9
|
const { schemaRegex } = require('./helper/url_helper');
|
|
10
10
|
const buildSystem = require('../build')
|
|
11
11
|
|
|
12
|
+
const JOB_STATUS = {
|
|
13
|
+
QUEUED: 'QUEUED',
|
|
14
|
+
RUNNING: 'RUNNING',
|
|
15
|
+
FINISHED: 'FINISHED',
|
|
16
|
+
FAILED: 'FAILED'
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
module.exports = class Push extends BaseCommand {
|
|
13
20
|
|
|
14
21
|
static async run(paramValues, pushOptions = {}) {
|
|
@@ -27,7 +34,11 @@ module.exports = class Push extends BaseCommand {
|
|
|
27
34
|
const target = Object.assign({ url }, subdomain && { subdomain });
|
|
28
35
|
|
|
29
36
|
console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
|
|
30
|
-
await this.pushTgz(params, content, extensionName);
|
|
37
|
+
const push = await this.pushTgz(params, content, extensionName);
|
|
38
|
+
const async = !params.get('sync');
|
|
39
|
+
if (async) {
|
|
40
|
+
await this.pollUntilFinished(push.headers.location, params)
|
|
41
|
+
}
|
|
31
42
|
console.log ('Activation succeeded.\n')
|
|
32
43
|
}
|
|
33
44
|
|
|
@@ -61,6 +72,10 @@ module.exports = class Push extends BaseCommand {
|
|
|
61
72
|
static async pushTgz(params, tgz, extensionName) {
|
|
62
73
|
const pushUrl = `${params.get('appUrl')}/-/cds/extensibility/push`;
|
|
63
74
|
const options = { ...params.get('reqAuth') };
|
|
75
|
+
const async = !params.get('sync');
|
|
76
|
+
if (async) {
|
|
77
|
+
options.headers = { prefer: 'respond-async', ...options.headers }
|
|
78
|
+
}
|
|
64
79
|
|
|
65
80
|
return axios.post(pushUrl, {
|
|
66
81
|
extension: tgz,
|
|
@@ -68,4 +83,29 @@ module.exports = class Push extends BaseCommand {
|
|
|
68
83
|
}, options)
|
|
69
84
|
.catch(error => this.handleHttpError(error, params, { url: pushUrl }));
|
|
70
85
|
}
|
|
86
|
+
|
|
87
|
+
static async pollUntilFinished(jobUrl, params) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const options = { ...params.get('reqAuth') };
|
|
90
|
+
|
|
91
|
+
const timeout = setTimeout(() => {
|
|
92
|
+
clearInterval(interval);
|
|
93
|
+
reject(new Error('cds push timed out after 5 minutes'));
|
|
94
|
+
}, 5 * 60 * 1000);
|
|
95
|
+
|
|
96
|
+
const interval = setInterval(async () => {
|
|
97
|
+
const { status, error } = (await axios.get(jobUrl, options)).data;
|
|
98
|
+
if (status === JOB_STATUS.FINISHED || status === JOB_STATUS.FAILED) {
|
|
99
|
+
clearInterval(interval);
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
|
|
102
|
+
if (status === JOB_STATUS.FINISHED) {
|
|
103
|
+
resolve();
|
|
104
|
+
} else if (status === JOB_STATUS.FAILED) {
|
|
105
|
+
reject(new Error(error));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, 1000);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
71
111
|
}
|
|
@@ -112,7 +112,7 @@ async function getTokenBaseUrl(params, renewUrl) {
|
|
|
112
112
|
existingPath = path;
|
|
113
113
|
break;
|
|
114
114
|
} catch (error) {
|
|
115
|
-
if ([401, 404].includes(error.
|
|
115
|
+
if ([401, 404].includes(error.status)) {
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
118
118
|
throw error;
|
|
@@ -344,6 +344,13 @@ class SettingsManager {
|
|
|
344
344
|
params.merge(auth[storage]);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
+
function removeObsoleteToken() {
|
|
348
|
+
if (params.get('renewLogin') || params.get('tokenExpirationDate') <= Date.now()) {
|
|
349
|
+
DEBUG?.((params.get('renewLogin') ? 'Renewing' : 'Refreshing expired') + ' authentication token');
|
|
350
|
+
params.delete('token');
|
|
351
|
+
params.delete('tokenExpirationDate');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
347
354
|
|
|
348
355
|
if (params.has('username')) {
|
|
349
356
|
if (!params.has('password') && !logout) {
|
|
@@ -381,8 +388,10 @@ class SettingsManager {
|
|
|
381
388
|
};
|
|
382
389
|
|
|
383
390
|
setRelevantAuth(auth);
|
|
384
|
-
|
|
385
|
-
|
|
391
|
+
if (params.has('token')) {
|
|
392
|
+
removeObsoleteToken();
|
|
393
|
+
}
|
|
394
|
+
if (!logout && !params.has('token') && !params.has('refreshToken') && !params.has('passcode') && !params.has('clientsecret') && !params.has('key')) {
|
|
386
395
|
await addPasscode();
|
|
387
396
|
}
|
|
388
397
|
}
|