@sap/cds 6.3.0 → 6.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/bin/build/buildTaskEngine.js +15 -12
- package/bin/build/constants.js +2 -0
- package/bin/build/util.js +2 -2
- package/bin/deploy/to-hana/cfUtil.js +48 -63
- package/lib/compile/parse.js +2 -1
- package/lib/srv/bindings.js +1 -0
- package/lib/srv/protocols/index.js +3 -1
- package/lib/utils/tar.js +43 -8
- package/libx/_runtime/fiori/generic/before.js +5 -2
- package/libx/_runtime/hana/search2Contains.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 6.3.2 - 2022-11-21
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `cds deploy` reports errors correctly
|
|
12
|
+
- Reference resolution in QL API
|
|
13
|
+
- `cds.parse.path` to correctly handle special characters
|
|
14
|
+
- `cds build` issues on Windows: build with large number of files and build on Git Bash.
|
|
15
|
+
- `odata` as default protocol for enabled middlewares feature
|
|
16
|
+
|
|
17
|
+
## Version 6.3.1 - 2022-11-04
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `cds build` no longer reports false positive validation errors for built-in MTX models like `@sap/cds/srv/mtx` or `@sap/cds-mtxs/srv/bootstrap`
|
|
22
|
+
- `cds deploy` handles empty result from `cf` call correctly
|
|
23
|
+
- `$search` fails on columns composed by a CQL expression that uses the SAP HANA `coalesce` predicate
|
|
24
|
+
- Draft ownership was erroneously checked for bound actions on active instances
|
|
25
|
+
- `cds watch/run/serve --with-mocks` no longer start randomly with missing mocked services. This could happen if previous runs crashed with errors and left bad state in the local service registry.
|
|
26
|
+
|
|
7
27
|
## Version 6.3.0 - 2022-10-28
|
|
8
28
|
|
|
9
29
|
### Added
|
|
@@ -2,8 +2,8 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const _cds = require('./cds'), { log } = _cds.exec
|
|
4
4
|
const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = require('@sap/cds-compiler')
|
|
5
|
-
const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
|
|
6
|
-
const { OUTPUT_MODE_DEFAULT, SEVERITIES, LOG_LEVELS, LOG_MODULE_NAMES } = require('./constants')
|
|
5
|
+
const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels, hasJavaNature } = require('./util')
|
|
6
|
+
const { OUTPUT_MODE_DEFAULT, SEVERITIES, LOG_LEVELS, LOG_MODULE_NAMES, CDS_MODEL_EXCLUDE_LIST } = require('./constants')
|
|
7
7
|
const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
|
|
8
8
|
const BuildTaskHandlerInternal = require('./provider/buildTaskHandlerInternal')
|
|
9
9
|
|
|
@@ -28,6 +28,7 @@ class BuildTaskEngine {
|
|
|
28
28
|
|
|
29
29
|
async processTasks(tasks, buildOptions, clean = true) {
|
|
30
30
|
const startTime = Date.now()
|
|
31
|
+
const messages = []
|
|
31
32
|
|
|
32
33
|
if (buildOptions) {
|
|
33
34
|
// clone as data may be stored as part of the buildOptions object
|
|
@@ -48,14 +49,16 @@ class BuildTaskEngine {
|
|
|
48
49
|
buildOptions.target = path.resolve(buildOptions.root, this.env.build.target)
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
// Java projects don't have node modules installed on project root level
|
|
53
|
+
if (!hasJavaNature([buildOptions.root])) {
|
|
54
|
+
// validate required @sap namespace models - log only
|
|
55
|
+
const { unresolved, missing } = this._resolveRequiredSapServices(tasks)
|
|
56
|
+
if (unresolved.length > 0) {
|
|
57
|
+
messages.push(new BuildMessage(`Required CDS service models [${unresolved.join(', ')}] cannot be resolved. Make sure to install the missing npm modules.`))
|
|
58
|
+
}
|
|
59
|
+
if (missing.length > 0) {
|
|
60
|
+
messages.push(new BuildMessage(`Required CDS service models [${missing.join(', ')}] are missing in custom build tasks. Make sure to add the missing models.`))
|
|
61
|
+
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
// create build task handlers
|
|
@@ -85,7 +88,7 @@ class BuildTaskEngine {
|
|
|
85
88
|
return buildResult
|
|
86
89
|
} catch (error) {
|
|
87
90
|
this._logBuildOutput(handlers, buildOptions)
|
|
88
|
-
|
|
91
|
+
|
|
89
92
|
// cds CLI layer logs in case of an exception if invoked from CLI
|
|
90
93
|
if (!buildOptions.cli) {
|
|
91
94
|
this._logMessages(buildOptions, [...BuildTaskEngine._getErrorMessages([error]), ...messages])
|
|
@@ -336,7 +339,7 @@ class BuildTaskEngine {
|
|
|
336
339
|
|
|
337
340
|
const unresolved = resolveRequiredSapModels(this.cds, [...taskModelPaths])
|
|
338
341
|
// are the required service models contained in the task's options.model
|
|
339
|
-
const missing = srvModelPaths.filter(m => m.startsWith('@sap/') && !taskModelPaths.has(m) && !unresolved.find(u => u === m))
|
|
342
|
+
const missing = srvModelPaths.filter(m => m.startsWith('@sap/') && !CDS_MODEL_EXCLUDE_LIST.includes(m) && !taskModelPaths.has(m) && !unresolved.find(u => u === m))
|
|
340
343
|
|
|
341
344
|
return { unresolved, missing }
|
|
342
345
|
}
|
package/bin/build/constants.js
CHANGED
|
@@ -45,6 +45,8 @@ exports.FOLDER_GEN = "gen"
|
|
|
45
45
|
exports.FILE_EXT_CDS = ".cds"
|
|
46
46
|
exports.MTX_SIDECAR_FOLDER = "mtx/sidecar" // default name of the mtx sidecar folder
|
|
47
47
|
exports.DEFAULT_CSN_FILE_NAME = "csn.json"
|
|
48
|
+
// REVISIT: the models are not required if a custom server.js file is used for MTX bootstrap
|
|
49
|
+
exports.CDS_MODEL_EXCLUDE_LIST = ['@sap/cds/srv/mtx', '@sap/cds-mtxs/srv/bootstrap']
|
|
48
50
|
|
|
49
51
|
exports.CDS_CONFIG_PATH_SEP = "/"
|
|
50
52
|
exports.SKIP_ASSERT_COMPILER_V2 = "skip-assert-compiler-v2"
|
package/bin/build/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
-
const { SEVERITY_ERROR } = require('./constants')
|
|
3
|
+
const { SEVERITY_ERROR, CDS_MODEL_EXCLUDE_LIST } = require('./constants')
|
|
4
4
|
|
|
5
5
|
function getProperty(src, segments) {
|
|
6
6
|
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
@@ -142,7 +142,7 @@ function isStreamlinedMtx(cds) {
|
|
|
142
142
|
*/
|
|
143
143
|
function resolveRequiredSapModels(cds, modelPaths) {
|
|
144
144
|
return modelPaths.filter(p => {
|
|
145
|
-
if (p.startsWith('@sap/')) {
|
|
145
|
+
if (p.startsWith('@sap/') && !CDS_MODEL_EXCLUDE_LIST.includes(p)) {
|
|
146
146
|
const files = cds.resolve(p)
|
|
147
147
|
return !files || files.length === 0
|
|
148
148
|
}
|
|
@@ -2,6 +2,10 @@ const cp = require('child_process');
|
|
|
2
2
|
const fsp = require('fs').promises;
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const util = require('util');
|
|
6
|
+
|
|
7
|
+
const IS_WIN = os.platform() === 'win32';
|
|
8
|
+
const execAsync = util.promisify(cp.exec);
|
|
5
9
|
|
|
6
10
|
const cds = require('../../../lib');
|
|
7
11
|
const LOG = cds.log ? cds.log('deploy') : console;
|
|
@@ -33,46 +37,23 @@ class CfUtil {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
async _cfRun(...args) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
LOG.debug('>>> ' + cmdLine);
|
|
41
|
-
}
|
|
40
|
+
args = args.map(arg => arg.replace(/"/g, '\\"'));
|
|
41
|
+
const cmdLine = `${CF_COMMAND} "${args.join('" "')}"`;
|
|
42
|
+
DEBUG && console.time(cmdLine);
|
|
43
|
+
LOG.debug('>>>', cmdLine);
|
|
42
44
|
|
|
43
45
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let stdout = '';
|
|
48
|
-
child.stdout.on('data', (data) => {
|
|
49
|
-
stdout += data;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
let stderr = '';
|
|
53
|
-
child.stderr.on('data', (data) => {
|
|
54
|
-
stderr += data;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
child.on('error', (err) => {
|
|
58
|
-
DEBUG && LOG.debug(`${stdout}\n${stderr}`);
|
|
59
|
-
if (err.code === 'ENOENT') {
|
|
60
|
-
reject(new Error(`Command ${bold(CF_COMMAND)} not found. Make sure to install the Cloud Foundry Command Line Interface.`));
|
|
61
|
-
} else {
|
|
62
|
-
reject(err);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
child.on('close', (code) => {
|
|
67
|
-
DEBUG && LOG.debug(`${stdout}\n${stderr}`);
|
|
68
|
-
if (!code) {
|
|
69
|
-
resolve(stdout.trim());
|
|
70
|
-
} else {
|
|
71
|
-
const errorMessage = `${stdout}\n${stderr}`;
|
|
72
|
-
reject(new Error(errorMessage.trim()));
|
|
73
|
-
}
|
|
74
|
-
});
|
|
46
|
+
const result = await execAsync(cmdLine, {
|
|
47
|
+
shell: IS_WIN,
|
|
48
|
+
stdio: ['inherit', 'pipe', 'inherit']
|
|
75
49
|
});
|
|
50
|
+
result.stdout = result.stdout?.trim();
|
|
51
|
+
result.stderr = result.stderr?.trim();
|
|
52
|
+
return result;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
err.stdout = err.stdout?.trim();
|
|
55
|
+
err.stderr = err.stderr?.trim();
|
|
56
|
+
throw err;
|
|
76
57
|
} finally {
|
|
77
58
|
// eslint-disable-next-line no-console
|
|
78
59
|
DEBUG && console.timeEnd(cmdLine);
|
|
@@ -98,17 +79,20 @@ class CfUtil {
|
|
|
98
79
|
args.push(JSON.stringify(bodyObj)); // cfRun uses spawn so no special handling for quotes on cli required
|
|
99
80
|
}
|
|
100
81
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
82
|
+
const result = await this._cfRun(...args);
|
|
83
|
+
let response = {};
|
|
84
|
+
if (result.stdout) {
|
|
85
|
+
response = JSON.parse(result.stdout);
|
|
86
|
+
} else if (result.stderr) {
|
|
87
|
+
response = { errors: [{ title: result.stderr }] };
|
|
104
88
|
}
|
|
105
89
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const errorMessage = result.errors.map((entry) => `${entry.title}: ${entry.detail} (${entry.code})`).join('\n');
|
|
90
|
+
if (response.errors) {
|
|
91
|
+
const errorMessage = response.errors.map((entry) => `${entry.title || ''}: ${entry.detail || ''} (${entry.code || ''})`).join('\n');
|
|
109
92
|
throw new Error(errorMessage);
|
|
110
93
|
}
|
|
111
|
-
|
|
94
|
+
|
|
95
|
+
return response;
|
|
112
96
|
}
|
|
113
97
|
|
|
114
98
|
_extract(string, pattern, errorMsg) {
|
|
@@ -138,23 +122,24 @@ class CfUtil {
|
|
|
138
122
|
|
|
139
123
|
async getCfTargetFromCli() {
|
|
140
124
|
const result = await this._cfRun('target');
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
125
|
+
if (result?.stdout) {
|
|
126
|
+
return {
|
|
127
|
+
apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
|
|
128
|
+
user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
|
|
129
|
+
org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
|
|
130
|
+
space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
147
133
|
}
|
|
148
134
|
|
|
149
135
|
async getCfTarget() {
|
|
150
|
-
// check if token is valid or expired / missing
|
|
151
|
-
await this._cfRun('oauth-token');
|
|
136
|
+
await this._cfRun('oauth-token'); // check if token is valid or expired / missing
|
|
152
137
|
return await this.getCfTargetFromConfigFile() || await this.getCfTargetFromCli();
|
|
153
138
|
}
|
|
154
139
|
|
|
155
140
|
async getCfSpaceInfo() {
|
|
156
141
|
if (!this.spaceInfo) {
|
|
157
|
-
|
|
142
|
+
LOG.debug('getting space info');
|
|
158
143
|
|
|
159
144
|
const target = await this.getCfTarget();
|
|
160
145
|
|
|
@@ -179,7 +164,7 @@ class CfUtil {
|
|
|
179
164
|
}
|
|
180
165
|
|
|
181
166
|
async getService(serviceName, showMessage = true) {
|
|
182
|
-
showMessage &&
|
|
167
|
+
showMessage && console.log(`Getting service ${bold(serviceName)}`);
|
|
183
168
|
const spaceInfo = await this.getCfSpaceInfo();
|
|
184
169
|
|
|
185
170
|
let counter = POLL_COUNTER;
|
|
@@ -208,7 +193,7 @@ class CfUtil {
|
|
|
208
193
|
throw new Error(`The returned service reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(serviceInstance, null, 4)}`);
|
|
209
194
|
|
|
210
195
|
default:
|
|
211
|
-
|
|
196
|
+
console.error(`Unsupported server response state '${serviceInstance?.last_operation?.state}'. Waiting for next response.`);
|
|
212
197
|
break;
|
|
213
198
|
}
|
|
214
199
|
}
|
|
@@ -221,11 +206,11 @@ class CfUtil {
|
|
|
221
206
|
|
|
222
207
|
const probeService = await this.getService(serviceName, false);
|
|
223
208
|
if (probeService) {
|
|
224
|
-
|
|
209
|
+
console.log(`Getting service ${bold(serviceName)}`);
|
|
225
210
|
return probeService;
|
|
226
211
|
}
|
|
227
212
|
|
|
228
|
-
|
|
213
|
+
console.log(`Creating service ${bold(serviceName)} - please be patient...`);
|
|
229
214
|
|
|
230
215
|
const spaceInfo = await this.getCfSpaceInfo();
|
|
231
216
|
|
|
@@ -263,7 +248,7 @@ class CfUtil {
|
|
|
263
248
|
}
|
|
264
249
|
|
|
265
250
|
const postResult = await this._cfRequest('/v3/service_instances', undefined, body);
|
|
266
|
-
if (postResult
|
|
251
|
+
if (postResult?.errors) {
|
|
267
252
|
throw new Error(postResult.errors[0].detail);
|
|
268
253
|
}
|
|
269
254
|
|
|
@@ -277,7 +262,7 @@ class CfUtil {
|
|
|
277
262
|
|
|
278
263
|
|
|
279
264
|
async getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
|
|
280
|
-
showMessage &&
|
|
265
|
+
showMessage && console.log(`Getting service key ${bold(serviceKeyName)}`);
|
|
281
266
|
|
|
282
267
|
let counter = POLL_COUNTER;
|
|
283
268
|
while (counter > 0) {
|
|
@@ -303,7 +288,7 @@ class CfUtil {
|
|
|
303
288
|
throw new Error(`The returned binding reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(binding, null, 4)}`);
|
|
304
289
|
|
|
305
290
|
default:
|
|
306
|
-
|
|
291
|
+
console.error(`Unsupported server response state '${binding?.last_operation?.state}'. Waiting for next response.`);
|
|
307
292
|
break;
|
|
308
293
|
}
|
|
309
294
|
}
|
|
@@ -316,11 +301,11 @@ class CfUtil {
|
|
|
316
301
|
|
|
317
302
|
const serviceKey = await this.getServiceKey(serviceInstance, serviceKeyName, false);
|
|
318
303
|
if (serviceKey) {
|
|
319
|
-
|
|
304
|
+
console.log(`Getting service key ${bold(serviceKeyName)}`);
|
|
320
305
|
return serviceKey;
|
|
321
306
|
}
|
|
322
307
|
|
|
323
|
-
|
|
308
|
+
console.log(`Creating service key ${bold(serviceKeyName)} - please be patient...`);
|
|
324
309
|
|
|
325
310
|
const body = {
|
|
326
311
|
type: 'key',
|
|
@@ -338,7 +323,7 @@ class CfUtil {
|
|
|
338
323
|
}
|
|
339
324
|
|
|
340
325
|
const postResult = await this._cfRequest('/v3/service_credential_bindings', undefined, body);
|
|
341
|
-
if (postResult
|
|
326
|
+
if (postResult?.errors) {
|
|
342
327
|
throw new Error(postResult.errors[0].detail);
|
|
343
328
|
}
|
|
344
329
|
|
package/lib/compile/parse.js
CHANGED
|
@@ -16,7 +16,8 @@ const parse = module.exports = Object.assign (cds_parse, {
|
|
|
16
16
|
}},
|
|
17
17
|
path: (x,...values) => {
|
|
18
18
|
if (x && x.raw) return tagged (parse.path,x,...values)
|
|
19
|
-
if (/^[A-Za-
|
|
19
|
+
if (/^[A-Za-z_0-9.$]*$/.test(x)) return {ref:[x]}
|
|
20
|
+
if ((cds.context?.model || cds.model)?.definitions[x]) return {ref:[x]}
|
|
20
21
|
let {SELECT} = parse.cql('SELECT from '+x)
|
|
21
22
|
return SELECT.from
|
|
22
23
|
},
|
package/lib/srv/bindings.js
CHANGED
|
@@ -10,7 +10,9 @@ class ProtocolAdapter {
|
|
|
10
10
|
for (let [k,o] of Object.entries(protocols)) if (typeof o === 'string') protocols[k] = {path:o}
|
|
11
11
|
if (!protocols.odata) protocols.odata = { impl: join(__dirname,'odata-v4') }
|
|
12
12
|
if (!protocols.rest) protocols.rest = { impl: join(__dirname,'rest') }
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
// odata must always be first for fallback
|
|
15
|
+
return this.protocols = { odata: protocols.odata, ...protocols }
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/**
|
package/lib/utils/tar.js
CHANGED
|
@@ -5,7 +5,7 @@ const spawn = /\btar\b/.test(process.env.DEBUG) ? (cmd, args, options) => {
|
|
|
5
5
|
return child_process.spawn(cmd, args, options)
|
|
6
6
|
} : child_process.spawn
|
|
7
7
|
|
|
8
|
-
const cds = require('../index'), { fs, path, mkdirp } = cds.utils
|
|
8
|
+
const cds = require('../index'), { fs, path, mkdirp, exists, rimraf } = cds.utils
|
|
9
9
|
const _resolve = (...x) => path.resolve (cds.root,...x)
|
|
10
10
|
|
|
11
11
|
// tar does not work properly on Windows (by npm/jest tests) w/o this change
|
|
@@ -15,6 +15,21 @@ const win = path => {
|
|
|
15
15
|
if (Array.isArray(path)) return path.map(el => win(el))
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// Copy files to temp dir on Windows and pack temp dir.
|
|
19
|
+
// cli tar has a size limit on Windows.
|
|
20
|
+
const createTemp = async (root, files) => {
|
|
21
|
+
const temp = await fs.promises.mkdtemp(`${fs.realpathSync(require('os').tmpdir())}${path.sep}tar-`)
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const fname = path.relative(root, file)
|
|
24
|
+
const destination = path.join(temp, fname)
|
|
25
|
+
const dirname = path.dirname(destination)
|
|
26
|
+
if (!await exists(dirname)) await fs.promises.mkdir(dirname, { recursive: true })
|
|
27
|
+
await fs.promises.copyFile(file, destination)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return temp
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
/**
|
|
19
34
|
* Creates a tar archive, to an in-memory Buffer, or piped to write stream or file.
|
|
20
35
|
* @example ```js
|
|
@@ -39,15 +54,31 @@ const win = path => {
|
|
|
39
54
|
* - `.then()` collects the tar output into an in-memory `Buffer`
|
|
40
55
|
* - `.to()` is a convenient shortcut to pipe the output into a write stream
|
|
41
56
|
*/
|
|
42
|
-
exports.create = (dir='.', ...args) => {
|
|
57
|
+
exports.create = async (dir='.', ...args) => {
|
|
43
58
|
|
|
44
59
|
if (typeof dir === 'string') dir = _resolve(dir)
|
|
45
60
|
if (Array.isArray(dir)) [ dir, ...args ] = [ cds.root, dir, ...args ]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
|
|
62
|
+
let c, temp
|
|
63
|
+
if (process.platform === 'win32') {
|
|
64
|
+
const spawnDir = (dir, args) => {
|
|
65
|
+
if (args.some(arg => arg === '-f')) return spawn ('tar', ['c', '-C', win(dir), ...win(args)])
|
|
66
|
+
else return spawn ('tar', ['cf', '-', '-C', win(dir), ...win(args)])
|
|
67
|
+
}
|
|
68
|
+
args.push('.')
|
|
69
|
+
if (Array.isArray(args[0])) {
|
|
70
|
+
temp = await createTemp(dir, args[0])
|
|
71
|
+
args.shift()
|
|
72
|
+
c = spawnDir(temp, args)
|
|
73
|
+
} else {
|
|
74
|
+
c = spawnDir(dir, args)
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
if (Array.isArray(args[0])) args.push (...args.shift().map (f => path.isAbsolute(f) ? path.relative(dir,f) : f))
|
|
78
|
+
else args.push('.')
|
|
79
|
+
|
|
80
|
+
c = spawn ('tar', ['c', '-C', dir, ...args])
|
|
81
|
+
}
|
|
51
82
|
|
|
52
83
|
return {__proto__:c, // returning a thenable + fluent ChildProcess...
|
|
53
84
|
|
|
@@ -60,6 +91,10 @@ exports.create = (dir='.', ...args) => {
|
|
|
60
91
|
const bb=[]; c.stdout.on('data', b => bb.push(b))
|
|
61
92
|
c.on('close', ()=>r(Buffer.concat(bb)))
|
|
62
93
|
c.on('error', e)
|
|
94
|
+
if (process.platform === 'win32') {
|
|
95
|
+
c.on('close', async () => temp && exists(temp) && await rimraf(temp))
|
|
96
|
+
c.on('error', async () => temp && exists(temp) && await rimraf(temp))
|
|
97
|
+
}
|
|
63
98
|
},
|
|
64
99
|
|
|
65
100
|
/**
|
|
@@ -107,7 +142,7 @@ exports.extract = (archive, ...args) => ({
|
|
|
107
142
|
to (...dest) {
|
|
108
143
|
if (typeof dest === 'string') dest = _resolve(...dest)
|
|
109
144
|
const input = typeof archive !== 'string' || archive == '-' ? '-' : _resolve(archive)
|
|
110
|
-
const x = spawn(
|
|
145
|
+
const x = spawn('tar', ['xf', win(input), '-C', win(dest), ...args])
|
|
111
146
|
if (archive === '-') return x.stdin
|
|
112
147
|
if (Buffer.isBuffer(archive)) archive = require('stream').Readable.from (archive)
|
|
113
148
|
if (typeof archive !== 'string') archive.pipe (x.stdin)
|
|
@@ -77,7 +77,7 @@ const _getRoot = req => {
|
|
|
77
77
|
return root
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const _getDraftDataFromExistingDraft = async (req, root) => {
|
|
80
|
+
const _getDraftDataFromExistingDraft = async (req, root, isBoundAction) => {
|
|
81
81
|
if (!root) return []
|
|
82
82
|
if (root?.IsActiveEntity === false) {
|
|
83
83
|
const query = _getSelectDraftDataCqn(root.entityName, root.where)
|
|
@@ -85,6 +85,9 @@ const _getDraftDataFromExistingDraft = async (req, root) => {
|
|
|
85
85
|
return result
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// do not expect validate draft ownership for action call on active instances
|
|
89
|
+
if (isBoundAction) return []
|
|
90
|
+
|
|
88
91
|
const rootWhere = getKeysCondition(req)
|
|
89
92
|
const query = _getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere)
|
|
90
93
|
const result = await cds.tx(req).run(query)
|
|
@@ -147,8 +150,8 @@ const _deleteCancel = async function (req) {
|
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
const _validateDraftBoundAction = async function (req) {
|
|
150
|
-
const result = await _getDraftDataFromExistingDraft(req, _getRoot(req))
|
|
151
153
|
const isBoundAction = true
|
|
154
|
+
const result = await _getDraftDataFromExistingDraft(req, _getRoot(req), isBoundAction)
|
|
152
155
|
if (result && result.length > 0) _validateDraft(req, result, isBoundAction)
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -95,7 +95,9 @@ const isContainsPredicateSupported = (query, entity, columns2Search) => {
|
|
|
95
95
|
const _isColumnFunc = (columns2Search, columnsDefs) =>
|
|
96
96
|
columns2Search.some(column2Search => {
|
|
97
97
|
if (column2Search.func) return true
|
|
98
|
-
return columnsDefs?.some(
|
|
98
|
+
return columnsDefs?.some(
|
|
99
|
+
columnDef => (columnDef.func && columnDef.as === column2Search.ref[0]) || columnDef?.xpr?.some(xpr => xpr.func)
|
|
100
|
+
)
|
|
99
101
|
})
|
|
100
102
|
|
|
101
103
|
module.exports = {
|