mcdev 3.1.3 → 4.0.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/.eslintrc.json +67 -7
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -1
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
- package/.github/dependabot.yml +14 -0
- package/.github/workflows/code-analysis.yml +57 -0
- package/.husky/commit-msg +10 -0
- package/.husky/post-checkout +5 -0
- package/.husky/pre-commit +2 -1
- package/.prettierrc +8 -0
- package/.vscode/settings.json +1 -1
- package/LICENSE +2 -2
- package/README.md +134 -45
- package/boilerplate/config.json +5 -11
- package/boilerplate/files/.prettierrc +8 -0
- package/boilerplate/files/.vscode/extensions.json +0 -1
- package/boilerplate/files/.vscode/settings.json +28 -2
- package/boilerplate/files/README.md +2 -2
- package/boilerplate/forcedUpdates.json +10 -0
- package/boilerplate/npm-dependencies.json +5 -5
- package/docs/dist/documentation.md +2795 -1724
- package/jsconfig.json +1 -1
- package/lib/Builder.js +166 -75
- package/lib/Deployer.js +244 -96
- package/lib/MetadataTypeDefinitions.js +2 -0
- package/lib/MetadataTypeInfo.js +2 -0
- package/lib/Retriever.js +61 -84
- package/lib/cli.js +116 -11
- package/lib/index.js +241 -561
- package/lib/metadataTypes/AccountUser.js +101 -95
- package/lib/metadataTypes/Asset.js +677 -248
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +451 -354
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +387 -372
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +61 -48
- package/lib/metadataTypes/DataExtractType.js +14 -8
- package/lib/metadataTypes/Discovery.js +21 -16
- package/lib/metadataTypes/Email.js +32 -12
- package/lib/metadataTypes/EmailSendDefinition.js +85 -80
- package/lib/metadataTypes/EventDefinition.js +61 -43
- package/lib/metadataTypes/FileTransfer.js +72 -52
- package/lib/metadataTypes/Filter.js +11 -4
- package/lib/metadataTypes/Folder.js +149 -117
- package/lib/metadataTypes/FtpLocation.js +14 -8
- package/lib/metadataTypes/ImportFile.js +61 -64
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +668 -454
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +204 -103
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +145 -81
- package/lib/metadataTypes/SetDefinition.js +20 -8
- package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
- package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
- package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
- package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
- package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
- package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
- package/lib/metadataTypes/definitions/Query.definition.js +4 -0
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +4 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
- package/lib/retrieveChangelog.js +7 -6
- package/lib/util/auth.js +117 -0
- package/lib/util/businessUnit.js +55 -66
- package/lib/util/cache.js +194 -0
- package/lib/util/cli.js +90 -116
- package/lib/util/config.js +302 -0
- package/lib/util/devops.js +240 -50
- package/lib/util/file.js +120 -191
- package/lib/util/init.config.js +195 -69
- package/lib/util/init.git.js +45 -50
- package/lib/util/init.js +72 -59
- package/lib/util/init.npm.js +48 -39
- package/lib/util/util.js +280 -564
- package/package.json +44 -33
- package/test/dataExtension.test.js +152 -0
- package/test/mockRoot/.mcdev-auth.json +8 -0
- package/test/mockRoot/.mcdevrc.json +67 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
- package/test/query.test.js +149 -0
- package/test/resourceFactory.js +142 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
- package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
- package/test/resources/9999999/dataExtension/build-expected.json +51 -0
- package/test/resources/9999999/dataExtension/create-expected.json +23 -0
- package/test/resources/9999999/dataExtension/create-response.xml +54 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
- package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
- package/test/resources/9999999/dataExtension/template-expected.json +51 -0
- package/test/resources/9999999/dataExtension/update-expected.json +55 -0
- package/test/resources/9999999/dataExtension/update-response.xml +52 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
- package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
- package/test/resources/9999999/query/build-expected.json +8 -0
- package/test/resources/9999999/query/get-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +11 -0
- package/test/resources/9999999/query/post-expected.json +11 -0
- package/test/resources/9999999/query/template-expected.json +8 -0
- package/test/resources/auth.json +32 -0
- package/test/resources/rest404-response.json +5 -0
- package/test/resources/retrieve-response.xml +21 -0
- package/test/utils.js +107 -0
- package/types/mcdev.d.js +301 -0
- package/CHANGELOG.md +0 -126
- package/PULL_REQUEST_TEMPLATE.md +0 -19
- package/test/util/file.js +0 -51
package/lib/util/file.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/* eslint-disable no-control-regex */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const TYPE = require('../../types/mcdev.d');
|
|
4
5
|
const fs = require('fs-extra');
|
|
5
6
|
const packageJson = require('../../package.json');
|
|
6
|
-
const path = require('path');
|
|
7
|
+
const path = require('node:path');
|
|
7
8
|
const prettier = require('prettier');
|
|
8
9
|
const Util = require('./util');
|
|
9
|
-
const
|
|
10
|
-
const updateNotifier = require('update-notifier-git');
|
|
10
|
+
const updateNotifier = require('update-notifier');
|
|
11
11
|
|
|
12
12
|
// inform user when there is an update
|
|
13
13
|
const notifier = updateNotifier({
|
|
14
14
|
pkg: packageJson,
|
|
15
15
|
updateCheckInterval: 1000 * 3600 * 24, // once per day
|
|
16
|
-
remoteUrl: packageJson.repository.url.split('git+')[1],
|
|
17
16
|
});
|
|
18
17
|
// Notify using the built-in convenience method
|
|
19
18
|
notifier.notify();
|
|
@@ -24,9 +23,10 @@ notifier.notify();
|
|
|
24
23
|
const File = {
|
|
25
24
|
/**
|
|
26
25
|
* copies a file from one path to another
|
|
27
|
-
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @
|
|
26
|
+
*
|
|
27
|
+
* @param {string} from - full filepath including name of existing file
|
|
28
|
+
* @param {string} to - full filepath including name where file should go
|
|
29
|
+
* @returns {object} - results object
|
|
30
30
|
*/
|
|
31
31
|
async copyFile(from, to) {
|
|
32
32
|
try {
|
|
@@ -34,21 +34,20 @@ const File = {
|
|
|
34
34
|
return { status: 'ok', file: from };
|
|
35
35
|
} catch (ex) {
|
|
36
36
|
// This can happen in some cases where referencing files deleted in Commit
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return { status: 'failed', statusMessage: ex.message, file: from };
|
|
45
|
-
}
|
|
37
|
+
return ex.message.startsWith('ENOENT: no such file or directory')
|
|
38
|
+
? {
|
|
39
|
+
status: 'skipped',
|
|
40
|
+
statusMessage: 'deleted from repository',
|
|
41
|
+
file: from,
|
|
42
|
+
}
|
|
43
|
+
: { status: 'failed', statusMessage: ex.message, file: from };
|
|
46
44
|
}
|
|
47
45
|
},
|
|
48
46
|
/**
|
|
49
47
|
* makes sure Windows accepts path names
|
|
50
|
-
*
|
|
51
|
-
* @
|
|
48
|
+
*
|
|
49
|
+
* @param {string} path - filename or path
|
|
50
|
+
* @returns {string} - corrected string
|
|
52
51
|
*/
|
|
53
52
|
filterIllegalPathChars(path) {
|
|
54
53
|
return (
|
|
@@ -69,13 +68,20 @@ const File = {
|
|
|
69
68
|
// convert closing-curly brackets back for templating
|
|
70
69
|
.split('%7D')
|
|
71
70
|
.join('}')
|
|
71
|
+
// convert brackets back for asset blocks
|
|
72
|
+
.split('%5B')
|
|
73
|
+
.join('[')
|
|
74
|
+
// convert brackets back for asset blocks
|
|
75
|
+
.split('%5D')
|
|
76
|
+
.join(']')
|
|
72
77
|
);
|
|
73
78
|
},
|
|
74
79
|
|
|
75
80
|
/**
|
|
76
81
|
* makes sure Windows accepts file names
|
|
77
|
-
*
|
|
78
|
-
* @
|
|
82
|
+
*
|
|
83
|
+
* @param {string} filename - filename or path
|
|
84
|
+
* @returns {string} - corrected string
|
|
79
85
|
*/
|
|
80
86
|
filterIllegalFilenames(filename) {
|
|
81
87
|
return (
|
|
@@ -90,13 +96,20 @@ const File = {
|
|
|
90
96
|
// convert closing-curly brackets back for templating
|
|
91
97
|
.split('%7D')
|
|
92
98
|
.join('}')
|
|
99
|
+
// convert brackets back for asset blocks
|
|
100
|
+
.split('%5B')
|
|
101
|
+
.join('[')
|
|
102
|
+
// convert brackets back for asset blocks
|
|
103
|
+
.split('%5D')
|
|
104
|
+
.join(']')
|
|
93
105
|
);
|
|
94
106
|
},
|
|
95
107
|
|
|
96
108
|
/**
|
|
97
109
|
* makes sure Windows accepts file names
|
|
98
|
-
*
|
|
99
|
-
* @
|
|
110
|
+
*
|
|
111
|
+
* @param {string} filename - filename or path
|
|
112
|
+
* @returns {string} - corrected string
|
|
100
113
|
*/
|
|
101
114
|
reverseFilterIllegalFilenames(filename) {
|
|
102
115
|
return decodeURIComponent(filename).split('_STAR_').join('*');
|
|
@@ -104,10 +117,12 @@ const File = {
|
|
|
104
117
|
|
|
105
118
|
/**
|
|
106
119
|
* Takes various types of path strings and formats into a platform specific path
|
|
120
|
+
*
|
|
107
121
|
* @param {string|string[]} denormalizedPath directory the file will be written to
|
|
108
|
-
* @returns {
|
|
122
|
+
* @returns {string} Path strings
|
|
109
123
|
*/
|
|
110
124
|
normalizePath: function (denormalizedPath) {
|
|
125
|
+
/* eslint-disable unicorn/prefer-ternary */
|
|
111
126
|
if (Array.isArray(denormalizedPath)) {
|
|
112
127
|
// if the value is undefined set to empty string to allow parsing
|
|
113
128
|
return path.join(...denormalizedPath.map((val) => val || ''));
|
|
@@ -115,22 +130,22 @@ const File = {
|
|
|
115
130
|
// if directory is empty put . as otherwill will write to c://
|
|
116
131
|
return path.join(denormalizedPath || '.');
|
|
117
132
|
}
|
|
133
|
+
/* eslint-enable unicorn/prefer-ternary */
|
|
118
134
|
},
|
|
119
135
|
/**
|
|
120
136
|
* Saves json content to a file in the local file system. Will create the parent directory if it does not exist
|
|
137
|
+
*
|
|
121
138
|
* @param {string|string[]} directory directory the file will be written to
|
|
122
|
-
* @param {
|
|
123
|
-
* @param {
|
|
139
|
+
* @param {string} filename name of the file without '.json' ending
|
|
140
|
+
* @param {object} content filecontent
|
|
124
141
|
* @returns {Promise} Promise
|
|
125
142
|
*/
|
|
126
143
|
writeJSONToFile: async function (directory, filename, content) {
|
|
127
144
|
directory = this.filterIllegalPathChars(this.normalizePath(directory));
|
|
128
145
|
filename = this.filterIllegalFilenames(filename);
|
|
129
|
-
|
|
130
|
-
fs.mkdirpSync(directory);
|
|
131
|
-
}
|
|
146
|
+
await fs.ensureDir(directory);
|
|
132
147
|
try {
|
|
133
|
-
|
|
148
|
+
return fs.writeJSON(path.join(directory, filename + '.json'), content, { spaces: 4 });
|
|
134
149
|
} catch (ex) {
|
|
135
150
|
Util.logger.error('File.writeJSONToFile:: error | ' + ex.message);
|
|
136
151
|
}
|
|
@@ -138,57 +153,30 @@ const File = {
|
|
|
138
153
|
/**
|
|
139
154
|
* Saves beautified files in the local file system. Will create the parent directory if it does not exist
|
|
140
155
|
* ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this
|
|
156
|
+
*
|
|
141
157
|
* @param {string|string[]} directory directory the file will be written to
|
|
142
|
-
* @param {
|
|
143
|
-
* @param {
|
|
144
|
-
* @param {
|
|
145
|
-
* @param {
|
|
146
|
-
* @returns {Promise
|
|
158
|
+
* @param {string} filename name of the file without suffix
|
|
159
|
+
* @param {string} filetype filetype ie. JSON or SSJS
|
|
160
|
+
* @param {string} content filecontent
|
|
161
|
+
* @param {TYPE.TemplateMap} [templateVariables] templating variables to be replaced in the metadata
|
|
162
|
+
* @returns {Promise.<boolean>} Promise
|
|
147
163
|
*/
|
|
148
164
|
writePrettyToFile: async function (directory, filename, filetype, content, templateVariables) {
|
|
149
|
-
let formatted;
|
|
150
|
-
if (filetype === 'sql') {
|
|
151
|
-
formatted = this._beautify_sql(content);
|
|
152
|
-
} else {
|
|
153
|
-
// we need to ensure formatted is a String, not a Promise
|
|
154
|
-
formatted = await this._beautify_prettier(directory, filename, filetype, content);
|
|
155
|
-
}
|
|
165
|
+
let formatted = await this._beautify_prettier(directory, filename, filetype, content);
|
|
156
166
|
if (templateVariables) {
|
|
157
167
|
formatted = Util.replaceByObject(formatted, templateVariables);
|
|
158
168
|
}
|
|
159
|
-
|
|
160
169
|
return this.writeToFile(directory, filename, filetype, formatted);
|
|
161
170
|
},
|
|
162
|
-
/**
|
|
163
|
-
* helper for writePrettyToFile, applying sql formatting onto given stringified content
|
|
164
|
-
* @param {String} content filecontent
|
|
165
|
-
* @returns {String} original string on error; formatted string on success
|
|
166
|
-
*/
|
|
167
|
-
_beautify_sql: function (content) {
|
|
168
|
-
let formatted;
|
|
169
|
-
try {
|
|
170
|
-
formatted = sql.format(content, {
|
|
171
|
-
language: 'sql', // Defaults to "sql"
|
|
172
|
-
indent: ' ', // Defaults to two spaces,W
|
|
173
|
-
uppercase: true, // Defaults to false
|
|
174
|
-
linesBetweenQueries: 1, // Defaults to 1
|
|
175
|
-
});
|
|
176
|
-
// if templating variables were in the code, those now have extra spaces
|
|
177
|
-
formatted = formatted.split('{ { { ').join('{{{').split(' } } }').join('}}}');
|
|
178
|
-
} catch (ex) {
|
|
179
|
-
Util.logger.debug('SQL Formatter Exception: ' + ex.message);
|
|
180
|
-
formatted = content;
|
|
181
|
-
}
|
|
182
|
-
return formatted;
|
|
183
|
-
},
|
|
184
171
|
/**
|
|
185
172
|
* helper for writePrettyToFile, applying prettier onto given stringified content
|
|
186
173
|
* ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this
|
|
174
|
+
*
|
|
187
175
|
* @param {string|string[]} directory directory the file will be written to
|
|
188
|
-
* @param {
|
|
189
|
-
* @param {
|
|
190
|
-
* @param {
|
|
191
|
-
* @returns {
|
|
176
|
+
* @param {string} filename name of the file without suffix
|
|
177
|
+
* @param {string} filetype filetype ie. JSON or SSJS
|
|
178
|
+
* @param {string} content filecontent
|
|
179
|
+
* @returns {string} original string on error; formatted string on success
|
|
192
180
|
*/
|
|
193
181
|
_beautify_prettier: function (directory, filename, filetype, content) {
|
|
194
182
|
if (!FileFs.prettierConfig) {
|
|
@@ -234,6 +222,9 @@ const File = {
|
|
|
234
222
|
case 'md':
|
|
235
223
|
FileFs.prettierConfig.parser = 'markdown';
|
|
236
224
|
break;
|
|
225
|
+
case 'sql':
|
|
226
|
+
FileFs.prettierConfig.parser = 'sql';
|
|
227
|
+
break;
|
|
237
228
|
default:
|
|
238
229
|
FileFs.prettierConfig.parser = 'babel';
|
|
239
230
|
}
|
|
@@ -252,7 +243,7 @@ const File = {
|
|
|
252
243
|
filename + '.error',
|
|
253
244
|
'log',
|
|
254
245
|
`Error Log\nParser: ${FileFs.prettierConfig.parser}\n${ex.message.replace(
|
|
255
|
-
/[\
|
|
246
|
+
/[\u001B\u009B][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
|
|
256
247
|
|
|
257
248
|
''
|
|
258
249
|
)}`
|
|
@@ -265,23 +256,25 @@ const File = {
|
|
|
265
256
|
},
|
|
266
257
|
/**
|
|
267
258
|
* Saves text content to a file in the local file system. Will create the parent directory if it does not exist
|
|
259
|
+
*
|
|
268
260
|
* @param {string|string[]} directory directory the file will be written to
|
|
269
|
-
* @param {
|
|
270
|
-
* @param {
|
|
271
|
-
* @param {
|
|
272
|
-
* @param {
|
|
273
|
-
* @returns {Promise
|
|
261
|
+
* @param {string} filename name of the file without '.json' ending
|
|
262
|
+
* @param {string} filetype filetype suffix
|
|
263
|
+
* @param {string} content filecontent
|
|
264
|
+
* @param {object} [encoding] added for certain file types (like images)
|
|
265
|
+
* @returns {Promise.<boolean>} Promise
|
|
274
266
|
*/
|
|
275
267
|
writeToFile: async function (directory, filename, filetype, content, encoding) {
|
|
276
268
|
directory = this.filterIllegalPathChars(this.normalizePath(directory));
|
|
277
|
-
|
|
278
|
-
fs.mkdirpSync(directory);
|
|
279
|
-
}
|
|
269
|
+
await fs.ensureDir(directory);
|
|
280
270
|
// filter characters that are illegal for file names in Windows
|
|
281
271
|
filename = this.filterIllegalFilenames(filename);
|
|
282
|
-
|
|
272
|
+
const filePath = path.join(directory, filename + '.' + filetype);
|
|
283
273
|
try {
|
|
284
|
-
await fs.
|
|
274
|
+
if (await fs.pathExists(filePath)) {
|
|
275
|
+
Util.logger.debug(`Overwriting: ${filePath}`);
|
|
276
|
+
}
|
|
277
|
+
await fs.writeFile(filePath, content, encoding);
|
|
285
278
|
return true;
|
|
286
279
|
} catch (ex) {
|
|
287
280
|
Util.logger.error('File.writeToFile:: error | ' + ex.message);
|
|
@@ -291,11 +284,12 @@ const File = {
|
|
|
291
284
|
|
|
292
285
|
/**
|
|
293
286
|
* Saves json content to a file in the local file system. Will create the parent directory if it does not exist
|
|
294
|
-
*
|
|
295
|
-
* @param {
|
|
296
|
-
* @param {
|
|
297
|
-
* @param {
|
|
298
|
-
* @
|
|
287
|
+
*
|
|
288
|
+
* @param {string | string[]} directory directory where the file is stored
|
|
289
|
+
* @param {string} filename name of the file without '.json' ending
|
|
290
|
+
* @param {boolean} sync should execute sync (default is async)
|
|
291
|
+
* @param {boolean} cleanPath should execute sync (default is true)
|
|
292
|
+
* @returns {Promise | object} Promise or JSON object depending on if async or not
|
|
299
293
|
*/
|
|
300
294
|
readJSONFile: function (directory, filename, sync, cleanPath) {
|
|
301
295
|
try {
|
|
@@ -326,30 +320,32 @@ const File = {
|
|
|
326
320
|
},
|
|
327
321
|
/**
|
|
328
322
|
* reads file from local file system.
|
|
329
|
-
*
|
|
330
|
-
* @param {
|
|
331
|
-
* @param {
|
|
332
|
-
* @param {
|
|
333
|
-
* @
|
|
323
|
+
*
|
|
324
|
+
* @param {string | string[]} directory directory where the file is stored
|
|
325
|
+
* @param {string} filename name of the file without '.json' ending
|
|
326
|
+
* @param {string} filetype filetype suffix
|
|
327
|
+
* @param {string} [encoding='utf8'] read file with encoding (defaults to utf-8)
|
|
328
|
+
* @returns {Promise.<string>} file contents
|
|
334
329
|
*/
|
|
335
|
-
|
|
330
|
+
readFilteredFilename: function (directory, filename, filetype, encoding) {
|
|
336
331
|
try {
|
|
337
332
|
directory = this.filterIllegalPathChars(this.normalizePath(directory));
|
|
338
333
|
filename = this.filterIllegalFilenames(filename);
|
|
339
334
|
return fs.readFile(path.join(directory, filename + '.' + filetype), encoding || 'utf8');
|
|
340
335
|
} catch (ex) {
|
|
341
|
-
Util.logger.error('File.
|
|
336
|
+
Util.logger.error('File.readFilteredFilename:: error | ' + ex.message);
|
|
342
337
|
}
|
|
343
338
|
},
|
|
344
339
|
/**
|
|
345
340
|
* reads directories to a specific depth returning an array
|
|
346
341
|
* of file paths to be iterated over
|
|
342
|
+
*
|
|
347
343
|
* @example ['deploy/mcdev/bu1']
|
|
348
|
-
* @param {
|
|
349
|
-
* @param {
|
|
350
|
-
* @param {
|
|
351
|
-
* @param {
|
|
352
|
-
* @returns {Promise
|
|
344
|
+
* @param {string} directory directory to checkin
|
|
345
|
+
* @param {number} depth how many levels to check (1 base)
|
|
346
|
+
* @param {boolean} [includeStem] include the parent directory in the response
|
|
347
|
+
* @param {number} [_stemLength] set recursively for subfolders. do not set manually!
|
|
348
|
+
* @returns {Promise.<string[]>} array of fully defined file paths
|
|
353
349
|
*/
|
|
354
350
|
readDirectories: async function (directory, depth, includeStem, _stemLength) {
|
|
355
351
|
try {
|
|
@@ -362,8 +358,8 @@ const File = {
|
|
|
362
358
|
for (const dirent of raw) {
|
|
363
359
|
const direntPath = path.join(directory, dirent.name);
|
|
364
360
|
if (
|
|
365
|
-
fs.
|
|
366
|
-
fs.
|
|
361
|
+
(await fs.pathExists(direntPath)) &&
|
|
362
|
+
(await fs.lstat(direntPath)).isDirectory() &&
|
|
367
363
|
depth > 0
|
|
368
364
|
) {
|
|
369
365
|
const nestedChildren = await this.readDirectories(
|
|
@@ -376,14 +372,15 @@ const File = {
|
|
|
376
372
|
}
|
|
377
373
|
}
|
|
378
374
|
if (children.length === 0) {
|
|
379
|
-
if
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
375
|
+
// if not includeStem then remove base directory and leading slahes and backslashes
|
|
376
|
+
return includeStem
|
|
377
|
+
? [directory]
|
|
378
|
+
: [
|
|
379
|
+
directory
|
|
380
|
+
.slice(Math.max(0, _stemLength))
|
|
381
|
+
.replace(/^\\+/, '')
|
|
382
|
+
.replace(/^\/+/, ''),
|
|
383
|
+
];
|
|
387
384
|
} else {
|
|
388
385
|
return children;
|
|
389
386
|
}
|
|
@@ -396,12 +393,14 @@ const File = {
|
|
|
396
393
|
/**
|
|
397
394
|
* reads directories to a specific depth returning an array
|
|
398
395
|
* of file paths to be iterated over using sync api (required in constructors)
|
|
396
|
+
* TODO - merge with readDirectories. so far the logic is really different
|
|
397
|
+
*
|
|
399
398
|
* @example ['deploy/mcdev/bu1']
|
|
400
|
-
* @param {
|
|
401
|
-
* @param {
|
|
402
|
-
* @param {
|
|
403
|
-
* @param {
|
|
404
|
-
* @returns {
|
|
399
|
+
* @param {string} directory directory to checkin
|
|
400
|
+
* @param {number} [depth] how many levels to check (1 base)
|
|
401
|
+
* @param {boolean} [includeStem] include the parent directory in the response
|
|
402
|
+
* @param {number} [_stemLength] set recursively for subfolders. do not set manually!
|
|
403
|
+
* @returns {string[]} array of fully defined file paths
|
|
405
404
|
*/
|
|
406
405
|
readDirectoriesSync: function (directory, depth, includeStem, _stemLength) {
|
|
407
406
|
try {
|
|
@@ -417,8 +416,8 @@ const File = {
|
|
|
417
416
|
children.push(directory);
|
|
418
417
|
} else {
|
|
419
418
|
// remove base directory and leading slahes and backslashes
|
|
420
|
-
const currentPath = directory.
|
|
421
|
-
children.push(currentPath
|
|
419
|
+
const currentPath = directory.slice(Math.max(0, _stemLength)).replace(path.sep, '');
|
|
420
|
+
children.push(currentPath || '.');
|
|
422
421
|
}
|
|
423
422
|
// read all directories
|
|
424
423
|
const raw = fs.readdirSync(directory, { withFileTypes: true });
|
|
@@ -442,94 +441,24 @@ const File = {
|
|
|
442
441
|
Util.logger.debug(ex.stack);
|
|
443
442
|
}
|
|
444
443
|
},
|
|
445
|
-
/**
|
|
446
|
-
* loads central properties from config file
|
|
447
|
-
* @param {Boolean} [silent] omit throwing errors and print messages; assuming not silent if not set
|
|
448
|
-
* @returns {Object} central properties object
|
|
449
|
-
*/
|
|
450
|
-
loadConfigFile(silent) {
|
|
451
|
-
let properties;
|
|
452
|
-
if (fs.existsSync(Util.configFileName)) {
|
|
453
|
-
// properties = JSON.parse(fs.readFileSync(Util.configFileName, 'utf8'));
|
|
454
|
-
try {
|
|
455
|
-
properties = fs.readJsonSync(Util.configFileName);
|
|
456
|
-
} catch (ex) {
|
|
457
|
-
Util.logger.error(`${ex.code}: ${ex.message}`);
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
if (fs.existsSync(Util.authFileName)) {
|
|
461
|
-
let auth;
|
|
462
|
-
try {
|
|
463
|
-
auth = fs.readJsonSync(Util.authFileName);
|
|
464
|
-
} catch (ex) {
|
|
465
|
-
Util.logger.error(`${ex.code}: ${ex.message}`);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
if (!auth.credentials) {
|
|
470
|
-
const err = `${Util.authFileName} is not set up correctly.`;
|
|
471
|
-
Util.logger.error(err);
|
|
472
|
-
throw new Error(err);
|
|
473
|
-
}
|
|
474
|
-
for (const cred in properties.credentials) {
|
|
475
|
-
const configset = properties.credentials[cred];
|
|
476
|
-
const authset = auth.credentials[cred];
|
|
477
|
-
if (authset) {
|
|
478
|
-
if (configset.eid === authset.eid) {
|
|
479
|
-
for (const key in authset) {
|
|
480
|
-
configset[key] = authset[key];
|
|
481
|
-
}
|
|
482
|
-
} else if (!silent) {
|
|
483
|
-
const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`;
|
|
484
|
-
Util.logger.error(err);
|
|
485
|
-
throw new Error(err);
|
|
486
|
-
}
|
|
487
|
-
} else if (!silent) {
|
|
488
|
-
Util.logger.warn(
|
|
489
|
-
`'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.`
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
} else if (!silent) {
|
|
494
|
-
Util.logger.warn(
|
|
495
|
-
`${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.`
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
return properties;
|
|
500
|
-
},
|
|
501
444
|
/**
|
|
502
445
|
* helper that splits the config back into auth & config parts to save them separately
|
|
503
|
-
*
|
|
504
|
-
* @
|
|
446
|
+
*
|
|
447
|
+
* @param {TYPE.Mcdevrc} properties central properties object
|
|
448
|
+
* @returns {Promise.<void>} -
|
|
505
449
|
*/
|
|
506
450
|
async saveConfigFile(properties) {
|
|
507
|
-
const auth = { credentials: {} };
|
|
508
|
-
const config = properties;
|
|
509
|
-
for (const cred in config.credentials) {
|
|
510
|
-
auth.credentials[cred] = {};
|
|
511
|
-
// copy id+secret+tenant to auth file
|
|
512
|
-
auth.credentials[cred].clientId = config.credentials[cred].clientId;
|
|
513
|
-
auth.credentials[cred].clientSecret = config.credentials[cred].clientSecret;
|
|
514
|
-
auth.credentials[cred].tenant = config.credentials[cred].tenant;
|
|
515
|
-
// copy eid as well to make sure we can test for equality when merging the files during runtime
|
|
516
|
-
auth.credentials[cred].eid = config.credentials[cred].eid;
|
|
517
|
-
// delete id+secret from config file
|
|
518
|
-
delete config.credentials[cred].clientId;
|
|
519
|
-
delete config.credentials[cred].clientSecret;
|
|
520
|
-
delete config.credentials[cred].tenant;
|
|
521
|
-
}
|
|
522
451
|
// we want to save to save the full version here to allow us to upgrade configs properly in the future
|
|
523
|
-
|
|
452
|
+
properties.version = packageJson.version;
|
|
524
453
|
|
|
525
|
-
await this.writeJSONToFile('', Util.configFileName.split('.json')[0],
|
|
526
|
-
await this.writeJSONToFile('', Util.authFileName.split('.json')[0], auth);
|
|
454
|
+
await this.writeJSONToFile('', Util.configFileName.split('.json')[0], properties);
|
|
527
455
|
Util.logger.info(`✔️ ${Util.configFileName} and ${Util.authFileName} saved successfully`);
|
|
528
456
|
},
|
|
529
457
|
/**
|
|
530
458
|
* Initalises Prettier formatting lib async.
|
|
531
|
-
*
|
|
532
|
-
* @
|
|
459
|
+
*
|
|
460
|
+
* @param {string} [filetype='html'] filetype ie. JSON or SSJS
|
|
461
|
+
* @returns {Promise.<boolean>} success of config load
|
|
533
462
|
*/
|
|
534
463
|
async initPrettier(filetype) {
|
|
535
464
|
if (FileFs.prettierConfig === null) {
|