mcdev 4.0.1 → 4.1.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/.github/ISSUE_TEMPLATE/bug.yml +74 -72
- package/README.md +16 -20
- package/docs/dist/documentation.md +34 -39
- package/lib/Deployer.js +3 -21
- package/lib/Retriever.js +33 -7
- package/lib/index.js +17 -7
- package/lib/metadataTypes/Asset.js +54 -17
- package/lib/metadataTypes/Automation.js +9 -3
- package/lib/metadataTypes/Script.js +2 -1
- package/lib/util/devops.js +5 -2
- package/lib/util/init.js +2 -2
- package/lib/util/init.npm.js +1 -1
- package/lib/util/util.js +3 -2
- package/package.json +4 -4
- package/types/mcdev.d.js +2 -1
|
@@ -1,76 +1,78 @@
|
|
|
1
1
|
name: 🐞 Bug
|
|
2
2
|
description: File a bug/issue
|
|
3
|
-
title:
|
|
3
|
+
title: '[BUG] <title>'
|
|
4
4
|
labels: [bug, NEW]
|
|
5
5
|
body:
|
|
6
|
-
- type: checkboxes
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- type: textarea
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- type: textarea
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- type: textarea
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- type: dropdown
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
6
|
+
- type: checkboxes
|
|
7
|
+
attributes:
|
|
8
|
+
label: Is there an existing issue for this?
|
|
9
|
+
description: Please [search here](https://github.com/Accenture/sfmc-devtools/issues) to see if an issue already exists for your problem.
|
|
10
|
+
options:
|
|
11
|
+
- label: I have searched the existing issues
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
attributes:
|
|
15
|
+
label: Current Behavior
|
|
16
|
+
description: A clear & concise description of what you're experiencing.
|
|
17
|
+
validations:
|
|
18
|
+
required: false
|
|
19
|
+
- type: textarea
|
|
20
|
+
attributes:
|
|
21
|
+
label: Expected Behavior
|
|
22
|
+
description: A clear & concise description of what you expected to happen.
|
|
23
|
+
validations:
|
|
24
|
+
required: false
|
|
25
|
+
- type: textarea
|
|
26
|
+
attributes:
|
|
27
|
+
label: Steps To Reproduce
|
|
28
|
+
description: Steps to reproduce the behavior.
|
|
29
|
+
value: |
|
|
30
|
+
1. Go to '...'
|
|
31
|
+
2. Click on '....'
|
|
32
|
+
3. Run '...'
|
|
33
|
+
4. See error...
|
|
34
|
+
validations:
|
|
35
|
+
required: false
|
|
36
|
+
- type: dropdown
|
|
37
|
+
id: version
|
|
38
|
+
attributes:
|
|
39
|
+
label: Version
|
|
40
|
+
description: What version of our software are you running? (mcdev --version)
|
|
41
|
+
options:
|
|
42
|
+
- 4.0.2
|
|
43
|
+
- 4.0.1
|
|
44
|
+
- 4.0.0
|
|
45
|
+
- 3.1.3
|
|
46
|
+
- 3.1.2
|
|
47
|
+
- 3.1.1
|
|
48
|
+
- 3.1.0
|
|
49
|
+
- 3.0.3
|
|
50
|
+
- 3.0.2
|
|
51
|
+
- 3.0.1
|
|
52
|
+
- develop-branch
|
|
53
|
+
validations:
|
|
54
|
+
required: true
|
|
55
|
+
- type: textarea
|
|
56
|
+
attributes:
|
|
57
|
+
label: Environment
|
|
58
|
+
description: |
|
|
59
|
+
examples:
|
|
60
|
+
- **Operating system (Windows/Mac/Linux/...)**: Windows 10
|
|
61
|
+
- **Node (node --version)**: 13.14.0
|
|
62
|
+
- **npm (npm --version)**: 7.6.3
|
|
63
|
+
value: |
|
|
64
|
+
- OS:
|
|
65
|
+
- Node:
|
|
66
|
+
- npm:
|
|
67
|
+
validations:
|
|
68
|
+
required: false
|
|
69
|
+
- type: checkboxes
|
|
70
|
+
attributes:
|
|
71
|
+
label: Participation
|
|
72
|
+
options:
|
|
73
|
+
- label: I am willing to submit a pull request for this issue.
|
|
74
|
+
required: false
|
|
75
|
+
- type: textarea
|
|
76
|
+
attributes:
|
|
77
|
+
label: Additional comments
|
|
78
|
+
description: Is there anything else that's important for the team to know?
|
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ If you experience issues installing Accenture SFMC DevTools, please check out th
|
|
|
123
123
|
1. Install Accenture SFMC DevTools by running `npm install -g mcdev` (prefix with `sudo` on MacOS)
|
|
124
124
|
- If you get an error, please see the below troubleshooting section.
|
|
125
125
|
|
|
126
|
-
When completed
|
|
126
|
+
When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.0`).
|
|
127
127
|
|
|
128
128
|
> **_Side note for proud nerds_:**
|
|
129
129
|
>
|
|
@@ -272,13 +272,15 @@ To work with our **developer-version** or to install a **specific older version*
|
|
|
272
272
|
npm install -g accenture/sfmc-devtools#develop
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
+
_Note: Regardless of which tag or branch you install_
|
|
276
|
+
|
|
275
277
|
**Install specific version (using a version tag on npm):**
|
|
276
278
|
|
|
277
279
|
```bash
|
|
278
|
-
npm install -g mcdev@
|
|
280
|
+
npm install -g mcdev@4.1.0
|
|
279
281
|
```
|
|
280
282
|
|
|
281
|
-
**Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop,
|
|
283
|
+
**Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop, 4.1.0)!
|
|
282
284
|
|
|
283
285
|
> **Note**: The version is currently _not_ updated on the developer branch until a new release is published. Hence, you will not see a change if you run `mcdev --version`.
|
|
284
286
|
|
|
@@ -1483,25 +1485,21 @@ If you want to enhance Accenture SFMC DevTools you are welcome to fork the repo
|
|
|
1483
1485
|
|
|
1484
1486
|
Instead of installing Accenture SFMC DevTools as a npm dependency from our git repo, we recommend cloning our repo and then linking it locally:
|
|
1485
1487
|
|
|
1486
|
-
Assuming you cloned Accenture SFMC DevTools into `C:\repos\sfmc-devtools\` (or `~/repos/sfmc-devtools/` on Mac)
|
|
1487
|
-
|
|
1488
|
-
_Local install:_
|
|
1489
|
-
|
|
1490
|
-
```bash
|
|
1491
|
-
npm install --save-dev "C:\repos\sfmc-devtools"
|
|
1492
|
-
```
|
|
1488
|
+
Assuming you cloned Accenture SFMC DevTools into `C:\repos\sfmc-devtools\` (or `~/repos/sfmc-devtools/` on Mac):
|
|
1493
1489
|
|
|
1494
|
-
|
|
1490
|
+
1. Open a terminal in your repo folder (`repos/sfmc-devtools/`)
|
|
1491
|
+
2. Execute `npm install` to download all the dependencies.
|
|
1492
|
+
3. Execute `npx husky install` to enable our git hooks.
|
|
1493
|
+
4. Execute `npm install -g "C:\repos\sfmc-devtools"` (this installs mcdev globally on your computer based on your cloned repo folder. Any changes you make in there will take immediate effect without the need for publishing or re-installing it).
|
|
1495
1494
|
|
|
1496
|
-
|
|
1495
|
+
This should tell npm to create a symlink to your cloned local directoty, allowing you to see updates you make in your mcdev repo instantly.
|
|
1497
1496
|
|
|
1498
|
-
|
|
1499
|
-
npm install -g "C:\repos\sfmc-devtools"
|
|
1500
|
-
```
|
|
1497
|
+
To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `4.1.0`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `4.1.0-dev` and then run `mcdev --version` again to verify that your change propagates instantly.
|
|
1501
1498
|
|
|
1502
|
-
|
|
1499
|
+
> **Not recommended:** Alternatively, you can install it locally only by opening a terminal in your project directory and executing `npm install --save-dev "C:\repos\sfmc-devtools"`
|
|
1500
|
+
> To run the local version you need to prepend "npx" before your commands, e.g. `npx mcdev --version`
|
|
1503
1501
|
|
|
1504
|
-
|
|
1502
|
+
**Note:** On MacOS you might need to prepend `sudo` to elevate your command.
|
|
1505
1503
|
|
|
1506
1504
|
**Local vs. Global developer installation:**
|
|
1507
1505
|
|
|
@@ -1509,8 +1507,6 @@ If you use Accenture SFMC DevTools in your team it is recommended to install you
|
|
|
1509
1507
|
|
|
1510
1508
|
If you do need to install it locally, make sure you don't commit your project's package.json with this change or you might break mcdev for other developers in your team that either didn't clone the Accenture SFMC DevTools repo or stored in a different directory.
|
|
1511
1509
|
|
|
1512
|
-
To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `3.2.0`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `3.2.0-dev` and then run `mcdev --version` again to verify that your change propagates instantly.
|
|
1513
|
-
|
|
1514
1510
|
<a name="local-install"></a>
|
|
1515
1511
|
|
|
1516
1512
|
### 9.2. Local install
|
|
@@ -1536,7 +1532,7 @@ The following explains how you _could_ install it locally for certain edge cases
|
|
|
1536
1532
|
4. Afterwards, install Accenture SFMC DevTools by running `npm install --save-dev mcdev`
|
|
1537
1533
|
- If you get an error, please see the below troubleshooting section.
|
|
1538
1534
|
|
|
1539
|
-
When completed
|
|
1535
|
+
When completed run `mcdev --version` and it will show you which version you installed (e.g. `4.1.0`).
|
|
1540
1536
|
|
|
1541
1537
|
### 9.3. NPM Scripts
|
|
1542
1538
|
|
|
@@ -180,8 +180,9 @@ Provides default functionality that can be overwritten by child metadata type cl
|
|
|
180
180
|
## Typedefs
|
|
181
181
|
|
|
182
182
|
<dl>
|
|
183
|
-
<dt><a href="#
|
|
184
|
-
<dd
|
|
183
|
+
<dt><a href="#TypeKeyCombo">TypeKeyCombo</a> : <code>Object.<string, string></code></dt>
|
|
184
|
+
<dd><p>object-key=metadata type, value=array of external keys</p>
|
|
185
|
+
</dd>
|
|
185
186
|
<dt><a href="#Cache">Cache</a> : <code>Object.<string, any></code></dt>
|
|
186
187
|
<dd><p>key=customer key</p>
|
|
187
188
|
</dd>
|
|
@@ -346,7 +347,6 @@ Source and target business units are also compared before the deployment to appl
|
|
|
346
347
|
* _instance_
|
|
347
348
|
* [.metadata](#Deployer+metadata) : <code>TYPE.MultiMetadataTypeMap</code>
|
|
348
349
|
* [._deploy([typeArr], [keyArr], [fromRetrieve])](#Deployer+_deploy) ⇒ <code>Promise</code>
|
|
349
|
-
* [.deployCallback(result, metadataType)](#Deployer+deployCallback) ⇒ <code>void</code>
|
|
350
350
|
* _static_
|
|
351
351
|
* [.deploy(businessUnit, [selectedTypesArr], [keyArr], [fromRetrieve])](#Deployer.deploy) ⇒ <code>Promise.<void></code>
|
|
352
352
|
* [._deployBU(cred, bu, properties, [typeArr], [keyArr], [fromRetrieve])](#Deployer._deployBU) ⇒ <code>Promise</code>
|
|
@@ -378,22 +378,10 @@ Deploy all metadata that is located in the deployDir
|
|
|
378
378
|
|
|
379
379
|
| Param | Type | Description |
|
|
380
380
|
| --- | --- | --- |
|
|
381
|
-
| [typeArr] | <code>Array.<
|
|
381
|
+
| [typeArr] | <code>Array.<TYPE.SupportedMetadataTypes></code> | limit deployment to given metadata type (can include subtype) |
|
|
382
382
|
| [keyArr] | <code>Array.<string></code> | limit deployment to given metadata keys |
|
|
383
383
|
| [fromRetrieve] | <code>boolean</code> | if true, no folders will be updated/created |
|
|
384
384
|
|
|
385
|
-
<a name="Deployer+deployCallback"></a>
|
|
386
|
-
|
|
387
|
-
### deployer.deployCallback(result, metadataType) ⇒ <code>void</code>
|
|
388
|
-
Gets called for every deployed metadata entry
|
|
389
|
-
|
|
390
|
-
**Kind**: instance method of [<code>Deployer</code>](#Deployer)
|
|
391
|
-
|
|
392
|
-
| Param | Type | Description |
|
|
393
|
-
| --- | --- | --- |
|
|
394
|
-
| result | <code>object</code> | Deployment result |
|
|
395
|
-
| metadataType | <code>string</code> | Name of metadata type |
|
|
396
|
-
|
|
397
385
|
<a name="Deployer.deploy"></a>
|
|
398
386
|
|
|
399
387
|
### Deployer.deploy(businessUnit, [selectedTypesArr], [keyArr], [fromRetrieve]) ⇒ <code>Promise.<void></code>
|
|
@@ -405,7 +393,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business
|
|
|
405
393
|
| Param | Type | Description |
|
|
406
394
|
| --- | --- | --- |
|
|
407
395
|
| businessUnit | <code>string</code> | references credentials from properties.json |
|
|
408
|
-
| [selectedTypesArr] | <code>Array.<
|
|
396
|
+
| [selectedTypesArr] | <code>Array.<TYPE.SupportedMetadataTypes></code> | limit deployment to given metadata type |
|
|
409
397
|
| [keyArr] | <code>Array.<string></code> | limit deployment to given metadata keys |
|
|
410
398
|
| [fromRetrieve] | <code>boolean</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
|
|
411
399
|
|
|
@@ -422,7 +410,7 @@ helper for deploy()
|
|
|
422
410
|
| cred | <code>string</code> | name of Credential |
|
|
423
411
|
| bu | <code>string</code> | name of BU |
|
|
424
412
|
| properties | <code>TYPE.Mcdevrc</code> | General configuration to be used in retrieve |
|
|
425
|
-
| [typeArr] | <code>Array.<
|
|
413
|
+
| [typeArr] | <code>Array.<TYPE.SupportedMetadataTypes></code> | limit deployment to given metadata type |
|
|
426
414
|
| [keyArr] | <code>Array.<string></code> | limit deployment to given metadata keys |
|
|
427
415
|
| [fromRetrieve] | <code>boolean</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
|
|
428
416
|
|
|
@@ -518,6 +506,7 @@ handler for 'mcdev createDeltaPkg
|
|
|
518
506
|
| argv | <code>object</code> | yargs parameters |
|
|
519
507
|
| [argv.range] | <code>string</code> | git commit range into deploy directory |
|
|
520
508
|
| [argv.filter] | <code>string</code> | filter file paths that start with any |
|
|
509
|
+
| [argv.diffArr] | <code>Array.<TYPE.DeltaPkgItem></code> | list of files to include in delta package (skips git diff when provided) |
|
|
521
510
|
| [argv.skipInteraction] | <code>TYPE.skipInteraction</code> | allows to skip interactive wizard |
|
|
522
511
|
|
|
523
512
|
<a name="Mcdev.selectTypes"></a>
|
|
@@ -550,7 +539,7 @@ Retrieve all metadata from the specified business unit into the local file syste
|
|
|
550
539
|
| Param | Type | Description |
|
|
551
540
|
| --- | --- | --- |
|
|
552
541
|
| businessUnit | <code>string</code> | references credentials from properties.json |
|
|
553
|
-
| [selectedTypesArr] | <code>Array.<
|
|
542
|
+
| [selectedTypesArr] | <code>Array.<TYPE.SupportedMetadataTypes></code> \| <code>TYPE.TypeKeyCombo</code> | limit retrieval to given metadata type |
|
|
554
543
|
| [keys] | <code>Array.<string></code> | limit retrieval to given metadata key |
|
|
555
544
|
| [changelogOnly] | <code>boolean</code> | skip saving, only create json in memory |
|
|
556
545
|
|
|
@@ -565,7 +554,7 @@ Deploys all metadata located in the 'deploy' directory to the specified business
|
|
|
565
554
|
| Param | Type | Default | Description |
|
|
566
555
|
| --- | --- | --- | --- |
|
|
567
556
|
| businessUnit | <code>string</code> | | references credentials from properties.json |
|
|
568
|
-
| [selectedTypesArr] | <code>Array.<
|
|
557
|
+
| [selectedTypesArr] | <code>Array.<TYPE.SupportedMetadataTypes></code> | | limit deployment to given metadata type |
|
|
569
558
|
| [keyArr] | <code>Array.<string></code> | | limit deployment to given metadata keys |
|
|
570
559
|
| [fromRetrieve] | <code>boolean</code> | <code>false</code> | optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder |
|
|
571
560
|
|
|
@@ -840,7 +829,7 @@ FileTransfer MetadataType
|
|
|
840
829
|
* [.requestSubType(subType, subTypeArray, [retrieveDir], [templateName], [templateVariables], key)](#Asset.requestSubType) ⇒ <code>Promise</code>
|
|
841
830
|
* [.requestAndSaveExtended(items, subType, retrieveDir, [templateVariables])](#Asset.requestAndSaveExtended) ⇒ <code>Promise</code>
|
|
842
831
|
* [._retrieveExtendedFile(metadata, subType, retrieveDir)](#Asset._retrieveExtendedFile) ⇒ <code>Promise.<void></code>
|
|
843
|
-
* [._readExtendedFileFromFS(metadata, subType, deployDir)](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.<
|
|
832
|
+
* [._readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly])](#Asset._readExtendedFileFromFS) ⇒ <code>Promise.<string></code>
|
|
844
833
|
* [.postRetrieveTasks(metadata)](#Asset.postRetrieveTasks) ⇒ <code>TYPE.CodeExtractItem</code>
|
|
845
834
|
* [.preDeployTasks(metadata, deployDir, buObject)](#Asset.preDeployTasks) ⇒ <code>Promise.<TYPE.AssetItem></code>
|
|
846
835
|
* [._getMainSubtype(extendedSubType)](#Asset._getMainSubtype) ⇒ <code>string</code>
|
|
@@ -974,19 +963,20 @@ This method retrieves these and saves them alongside the metadata json
|
|
|
974
963
|
|
|
975
964
|
<a name="Asset._readExtendedFileFromFS"></a>
|
|
976
965
|
|
|
977
|
-
### Asset.\_readExtendedFileFromFS(metadata, subType, deployDir) ⇒ <code>Promise.<
|
|
966
|
+
### Asset.\_readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly]) ⇒ <code>Promise.<string></code>
|
|
978
967
|
helper for this.preDeployTasks()
|
|
979
968
|
Some metadata types store their actual content as a separate file, e.g. images
|
|
980
969
|
This method reads these from the local FS stores them in the metadata object allowing to deploy it
|
|
981
970
|
|
|
982
971
|
**Kind**: static method of [<code>Asset</code>](#Asset)
|
|
983
|
-
**Returns**: <code>Promise.<
|
|
972
|
+
**Returns**: <code>Promise.<string></code> - if found will return the path of the binary file
|
|
984
973
|
|
|
985
|
-
| Param | Type | Description |
|
|
986
|
-
| --- | --- | --- |
|
|
987
|
-
| metadata | <code>TYPE.AssetItem</code> | a single asset |
|
|
988
|
-
| subType | <code>TYPE.AssetSubType</code> | group of similar assets to put in a folder (ie. images) |
|
|
989
|
-
| deployDir | <code>string</code> | directory of deploy files |
|
|
974
|
+
| Param | Type | Default | Description |
|
|
975
|
+
| --- | --- | --- | --- |
|
|
976
|
+
| metadata | <code>TYPE.AssetItem</code> | | a single asset |
|
|
977
|
+
| subType | <code>TYPE.AssetSubType</code> | | group of similar assets to put in a folder (ie. images) |
|
|
978
|
+
| deployDir | <code>string</code> | | directory of deploy files |
|
|
979
|
+
| [pathOnly] | <code>boolean</code> | <code>false</code> | used by getFilesToCommit which does not need the binary file to be actually read |
|
|
990
980
|
|
|
991
981
|
<a name="Asset.postRetrieveTasks"></a>
|
|
992
982
|
|
|
@@ -4418,8 +4408,8 @@ Retrieve metadata of specified types into local file system and Retriever.metada
|
|
|
4418
4408
|
|
|
4419
4409
|
| Param | Type | Description |
|
|
4420
4410
|
| --- | --- | --- |
|
|
4421
|
-
| metadataTypes | <code>Array.<
|
|
4422
|
-
| [namesOrKeys] | <code>Array.<string></code> | name of Metadata to retrieveAsTemplate or list of keys for normal retrieval |
|
|
4411
|
+
| metadataTypes | <code>Array.<TYPE.SupportedMetadataTypes></code> | list of metadata types to retrieve; can include subtypes! |
|
|
4412
|
+
| [namesOrKeys] | <code>Array.<string></code> \| <code>TYPE.TypeKeyCombo</code> | name of Metadata to retrieveAsTemplate or list of keys for normal retrieval |
|
|
4423
4413
|
| [templateVariables] | <code>TYPE.TemplateMap</code> | Object of values which can be replaced (in case of templating) |
|
|
4424
4414
|
| [changelogOnly] | <code>boolean</code> | skip saving, only create json in memory |
|
|
4425
4415
|
|
|
@@ -4448,7 +4438,7 @@ CLI entry for SFMC DevTools
|
|
|
4448
4438
|
* [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
|
|
4449
4439
|
* [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.<string></code>
|
|
4450
4440
|
* [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
|
|
4451
|
-
* [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
|
|
4441
|
+
* [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
|
|
4452
4442
|
* [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
|
|
4453
4443
|
* [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
|
|
4454
4444
|
|
|
@@ -4645,7 +4635,7 @@ let's you dynamically walk down an object and get a value
|
|
|
4645
4635
|
|
|
4646
4636
|
<a name="Util.execSync"></a>
|
|
4647
4637
|
|
|
4648
|
-
### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
|
|
4638
|
+
### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
|
|
4649
4639
|
helper to run other commands as if run manually by user
|
|
4650
4640
|
|
|
4651
4641
|
**Kind**: static method of [<code>Util</code>](#Util)
|
|
@@ -4654,6 +4644,7 @@ helper to run other commands as if run manually by user
|
|
|
4654
4644
|
| --- | --- | --- |
|
|
4655
4645
|
| cmd | <code>string</code> | to be executed command |
|
|
4656
4646
|
| [args] | <code>Array.<string></code> | list of arguments |
|
|
4647
|
+
| [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
|
|
4657
4648
|
|
|
4658
4649
|
<a name="Util.templateSearchResult"></a>
|
|
4659
4650
|
|
|
@@ -4942,7 +4933,7 @@ DevOps helper class
|
|
|
4942
4933
|
* [.getDeltaList(properties, [range], [saveToDeployDir], [filterPaths])](#DevOps.getDeltaList) ⇒ <code>Promise.<Array.<TYPE.DeltaPkgItem>></code>
|
|
4943
4934
|
* [~delta](#DevOps.getDeltaList..delta) : <code>Array.<TYPE.DeltaPkgItem></code>
|
|
4944
4935
|
* [~copied](#DevOps.getDeltaList..copied) : <code>TYPE.DeltaPkgItem</code>
|
|
4945
|
-
* [.buildDeltaDefinitions(properties, range, [skipInteraction])](#DevOps.buildDeltaDefinitions) ⇒ <code>Promise.<Array.<TYPE.DeltaPkgItem>></code>
|
|
4936
|
+
* [.buildDeltaDefinitions(properties, range, [diffArr], [skipInteraction])](#DevOps.buildDeltaDefinitions) ⇒ <code>Promise.<Array.<TYPE.DeltaPkgItem>></code>
|
|
4946
4937
|
* [~deltaDeployAll](#DevOps.buildDeltaDefinitions..deltaDeployAll) : <code>Array.<TYPE.DeltaPkgItem></code>
|
|
4947
4938
|
* [.document(directory, jsonReport)](#DevOps.document) ⇒ <code>void</code>
|
|
4948
4939
|
* [.getFilesToCommit(properties, buObject, metadataType, keyArr)](#DevOps.getFilesToCommit) ⇒ <code>Promise.<Array.<string>></code>
|
|
@@ -4978,7 +4969,7 @@ Interactive commit selection if no commits are passed.
|
|
|
4978
4969
|
**Kind**: inner constant of [<code>getDeltaList</code>](#DevOps.getDeltaList)
|
|
4979
4970
|
<a name="DevOps.buildDeltaDefinitions"></a>
|
|
4980
4971
|
|
|
4981
|
-
### DevOps.buildDeltaDefinitions(properties, range, [skipInteraction]) ⇒ <code>Promise.<Array.<TYPE.DeltaPkgItem>></code>
|
|
4972
|
+
### DevOps.buildDeltaDefinitions(properties, range, [diffArr], [skipInteraction]) ⇒ <code>Promise.<Array.<TYPE.DeltaPkgItem>></code>
|
|
4982
4973
|
wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
|
|
4983
4974
|
|
|
4984
4975
|
**Kind**: static method of [<code>DevOps</code>](#DevOps)
|
|
@@ -4988,6 +4979,7 @@ wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
|
|
|
4988
4979
|
| --- | --- | --- |
|
|
4989
4980
|
| properties | <code>TYPE.Mcdevrc</code> | project config file |
|
|
4990
4981
|
| range | <code>string</code> | git commit range |
|
|
4982
|
+
| [diffArr] | <code>Array.<TYPE.DeltaPkgItem></code> | instead of running git diff the method can also get a list of files to process |
|
|
4991
4983
|
| [skipInteraction] | <code>TYPE.SkipInteraction</code> | allows to skip interactive wizard |
|
|
4992
4984
|
|
|
4993
4985
|
<a name="DevOps.buildDeltaDefinitions..deltaDeployAll"></a>
|
|
@@ -6206,7 +6198,7 @@ Util that contains logger and simple util methods
|
|
|
6206
6198
|
* [.inverseGet(objs, val)](#Util.inverseGet) ⇒ <code>string</code>
|
|
6207
6199
|
* [.getMetadataHierachy(metadataTypes)](#Util.getMetadataHierachy) ⇒ <code>Array.<string></code>
|
|
6208
6200
|
* [.resolveObjPath(path, obj)](#Util.resolveObjPath) ⇒ <code>any</code>
|
|
6209
|
-
* [.execSync(cmd, [args])](#Util.execSync) ⇒ <code>undefined</code>
|
|
6201
|
+
* [.execSync(cmd, [args], [hideOutput])](#Util.execSync) ⇒ <code>undefined</code>
|
|
6210
6202
|
* [.templateSearchResult(results, keyToSearch, searchValue)](#Util.templateSearchResult) ⇒ <code>TYPE.MetadataTypeItem</code>
|
|
6211
6203
|
* [.setLoggingLevel(argv)](#Util.setLoggingLevel) ⇒ <code>void</code>
|
|
6212
6204
|
|
|
@@ -6403,7 +6395,7 @@ let's you dynamically walk down an object and get a value
|
|
|
6403
6395
|
|
|
6404
6396
|
<a name="Util.execSync"></a>
|
|
6405
6397
|
|
|
6406
|
-
### Util.execSync(cmd, [args]) ⇒ <code>undefined</code>
|
|
6398
|
+
### Util.execSync(cmd, [args], [hideOutput]) ⇒ <code>undefined</code>
|
|
6407
6399
|
helper to run other commands as if run manually by user
|
|
6408
6400
|
|
|
6409
6401
|
**Kind**: static method of [<code>Util</code>](#Util)
|
|
@@ -6412,6 +6404,7 @@ helper to run other commands as if run manually by user
|
|
|
6412
6404
|
| --- | --- | --- |
|
|
6413
6405
|
| cmd | <code>string</code> | to be executed command |
|
|
6414
6406
|
| [args] | <code>Array.<string></code> | list of arguments |
|
|
6407
|
+
| [hideOutput] | <code>boolean</code> | if true, output of command will be hidden from CLI |
|
|
6415
6408
|
|
|
6416
6409
|
<a name="Util.templateSearchResult"></a>
|
|
6417
6410
|
|
|
@@ -6491,9 +6484,11 @@ wrapper around our standard winston logging to console and logfile
|
|
|
6491
6484
|
initiate winston logger
|
|
6492
6485
|
|
|
6493
6486
|
**Kind**: global function
|
|
6494
|
-
<a name="
|
|
6487
|
+
<a name="TypeKeyCombo"></a>
|
|
6488
|
+
|
|
6489
|
+
## TypeKeyCombo : <code>Object.<string, string></code>
|
|
6490
|
+
object-key=metadata type, value=array of external keys
|
|
6495
6491
|
|
|
6496
|
-
## SupportedMetadataTypes : <code>Object.<string, string></code>
|
|
6497
6492
|
**Kind**: global typedef
|
|
6498
6493
|
<a name="Cache"></a>
|
|
6499
6494
|
|
|
@@ -6776,7 +6771,7 @@ SOAP format
|
|
|
6776
6771
|
| binary | <code>boolean</code> | is a binary file |
|
|
6777
6772
|
| moved | <code>boolean</code> | git thinks this file was moved |
|
|
6778
6773
|
| [fromPath] | <code>string</code> | git thinks this relative path is where the file was before |
|
|
6779
|
-
| type |
|
|
6774
|
+
| type | <code>SupportedMetadataTypes</code> | metadata type |
|
|
6780
6775
|
| externalKey | <code>string</code> | key |
|
|
6781
6776
|
| name | <code>string</code> | name |
|
|
6782
6777
|
| gitAction | <code>'move'</code> \| <code>'add/update'</code> \| <code>'delete'</code> | what git recognized as an action |
|
package/lib/Deployer.js
CHANGED
|
@@ -45,7 +45,7 @@ class Deployer {
|
|
|
45
45
|
* Deploys all metadata located in the 'deploy' directory to the specified business unit
|
|
46
46
|
*
|
|
47
47
|
* @param {string} businessUnit references credentials from properties.json
|
|
48
|
-
* @param {
|
|
48
|
+
* @param {TYPE.SupportedMetadataTypes[]} [selectedTypesArr] limit deployment to given metadata type
|
|
49
49
|
* @param {string[]} [keyArr] limit deployment to given metadata keys
|
|
50
50
|
* @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
|
|
51
51
|
* @returns {Promise.<void>} -
|
|
@@ -158,7 +158,7 @@ class Deployer {
|
|
|
158
158
|
* @param {string} cred name of Credential
|
|
159
159
|
* @param {string} bu name of BU
|
|
160
160
|
* @param {TYPE.Mcdevrc} properties General configuration to be used in retrieve
|
|
161
|
-
* @param {
|
|
161
|
+
* @param {TYPE.SupportedMetadataTypes[]} [typeArr] limit deployment to given metadata type
|
|
162
162
|
* @param {string[]} [keyArr] limit deployment to given metadata keys
|
|
163
163
|
* @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
|
|
164
164
|
* @returns {Promise} ensure that BUs are worked on sequentially
|
|
@@ -182,7 +182,7 @@ class Deployer {
|
|
|
182
182
|
/**
|
|
183
183
|
* Deploy all metadata that is located in the deployDir
|
|
184
184
|
*
|
|
185
|
-
* @param {
|
|
185
|
+
* @param {TYPE.SupportedMetadataTypes[]} [typeArr] limit deployment to given metadata type (can include subtype)
|
|
186
186
|
* @param {string[]} [keyArr] limit deployment to given metadata keys
|
|
187
187
|
* @param {boolean} [fromRetrieve] if true, no folders will be updated/created
|
|
188
188
|
* @returns {Promise} Promise
|
|
@@ -247,28 +247,10 @@ class Deployer {
|
|
|
247
247
|
this.buObject
|
|
248
248
|
);
|
|
249
249
|
cache.mergeMetadata(type, result);
|
|
250
|
-
this.deployCallback(result, type);
|
|
251
250
|
}
|
|
252
251
|
}
|
|
253
252
|
}
|
|
254
253
|
|
|
255
|
-
/**
|
|
256
|
-
* Gets called for every deployed metadata entry
|
|
257
|
-
*
|
|
258
|
-
* @param {object} result Deployment result
|
|
259
|
-
* @param {string} metadataType Name of metadata type
|
|
260
|
-
* @returns {void}
|
|
261
|
-
*/
|
|
262
|
-
deployCallback(result, metadataType) {
|
|
263
|
-
if (result) {
|
|
264
|
-
File.writeJSONToFile('logs/', 'deployResult_' + metadataType, result);
|
|
265
|
-
Util.logger.verbose(
|
|
266
|
-
'Deployer.deployCallback:: Results written to: ' +
|
|
267
|
-
path.normalize(process.cwd() + '/logs/deployResult_' + metadataType + '.json')
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
254
|
/**
|
|
273
255
|
* Returns metadata of a business unit that is saved locally
|
|
274
256
|
*
|
package/lib/Retriever.js
CHANGED
|
@@ -31,8 +31,8 @@ class Retriever {
|
|
|
31
31
|
/**
|
|
32
32
|
* Retrieve metadata of specified types into local file system and Retriever.metadata
|
|
33
33
|
*
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {string[]} [namesOrKeys] name of Metadata to retrieveAsTemplate or list of keys for normal retrieval
|
|
34
|
+
* @param {TYPE.SupportedMetadataTypes[]} metadataTypes list of metadata types to retrieve; can include subtypes!
|
|
35
|
+
* @param {string[]|TYPE.TypeKeyCombo} [namesOrKeys] name of Metadata to retrieveAsTemplate or list of keys for normal retrieval
|
|
36
36
|
* @param {TYPE.TemplateMap} [templateVariables] Object of values which can be replaced (in case of templating)
|
|
37
37
|
* @param {boolean} [changelogOnly] skip saving, only create json in memory
|
|
38
38
|
* @returns {Promise.<TYPE.MultiMetadataTypeList>} Promise of a list of retrieved items grouped by type {automation:[...], query:[...]}
|
|
@@ -42,12 +42,31 @@ class Retriever {
|
|
|
42
42
|
* @type {TYPE.MultiMetadataTypeList}
|
|
43
43
|
*/
|
|
44
44
|
const retrieveChangelog = {};
|
|
45
|
-
if (!namesOrKeys || !namesOrKeys.length) {
|
|
46
|
-
//
|
|
45
|
+
if (!namesOrKeys || (Array.isArray(namesOrKeys) && !namesOrKeys.length)) {
|
|
46
|
+
// no keys were provided, ensure we retrieve all
|
|
47
47
|
namesOrKeys = [null];
|
|
48
48
|
}
|
|
49
|
+
/** @type {TYPE.TypeKeyCombo} */
|
|
50
|
+
let typeKeyMap = {};
|
|
51
|
+
if (Array.isArray(namesOrKeys)) {
|
|
52
|
+
// no keys or array of keys was provided (likely called via CLI or to retrieve all)
|
|
53
|
+
// transform into TypeKeyCombo to iterate over it
|
|
54
|
+
for (const type of metadataTypes) {
|
|
55
|
+
typeKeyMap[type] = namesOrKeys;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
// assuming TypeKeyCombo was provided
|
|
59
|
+
typeKeyMap = namesOrKeys;
|
|
60
|
+
}
|
|
61
|
+
// defined colors for optionally printing the keys we filtered by
|
|
62
|
+
const color = {
|
|
63
|
+
reset: '\x1B[0m',
|
|
64
|
+
dim: '\x1B[2m',
|
|
65
|
+
};
|
|
49
66
|
for (const metadataType of Util.getMetadataHierachy(metadataTypes)) {
|
|
50
67
|
const [type, subType] = metadataType.split('-');
|
|
68
|
+
// if types were added by getMetadataHierachy() for caching, make sure the key-list is set to [null] for them which will retrieve all
|
|
69
|
+
typeKeyMap[metadataType] = typeKeyMap[metadataType] || [null];
|
|
51
70
|
// add client to metadata process class instead of passing every time
|
|
52
71
|
MetadataTypeInfo[type].client = auth.getSDK(this.buObject);
|
|
53
72
|
MetadataTypeInfo[type].properties = this.properties;
|
|
@@ -65,7 +84,7 @@ class Retriever {
|
|
|
65
84
|
Util.logger.info(`Retrieving as Template: ${metadataType}`);
|
|
66
85
|
|
|
67
86
|
result = await Promise.all(
|
|
68
|
-
|
|
87
|
+
typeKeyMap[metadataType].map((name) =>
|
|
69
88
|
MetadataTypeInfo[type].retrieveAsTemplate(
|
|
70
89
|
this.templateDir,
|
|
71
90
|
name,
|
|
@@ -75,11 +94,18 @@ class Retriever {
|
|
|
75
94
|
)
|
|
76
95
|
);
|
|
77
96
|
} else {
|
|
78
|
-
Util.logger.info(
|
|
97
|
+
Util.logger.info(
|
|
98
|
+
`Retrieving: ${metadataType}` +
|
|
99
|
+
(typeKeyMap[metadataType][0] !== null
|
|
100
|
+
? ` ${color.dim}(Keys: ${typeKeyMap[metadataType].join(', ')})${
|
|
101
|
+
color.reset
|
|
102
|
+
}`
|
|
103
|
+
: '')
|
|
104
|
+
);
|
|
79
105
|
result = await (changelogOnly
|
|
80
106
|
? MetadataTypeInfo[type].retrieveChangelog(this.buObject, null, subType)
|
|
81
107
|
: Promise.all(
|
|
82
|
-
|
|
108
|
+
typeKeyMap[metadataType].map((key) =>
|
|
83
109
|
MetadataTypeInfo[type].retrieve(
|
|
84
110
|
this.savePath,
|
|
85
111
|
null,
|
package/lib/index.js
CHANGED
|
@@ -49,6 +49,7 @@ class Mcdev {
|
|
|
49
49
|
* @param {string} [argv.range] git commit range
|
|
50
50
|
into deploy directory
|
|
51
51
|
* @param {string} [argv.filter] filter file paths that start with any
|
|
52
|
+
* @param {TYPE.DeltaPkgItem[]} [argv.diffArr] list of files to include in delta package (skips git diff when provided)
|
|
52
53
|
* @param {TYPE.skipInteraction} [argv.skipInteraction] allows to skip interactive wizard
|
|
53
54
|
* @returns {Promise.<TYPE.DeltaPkgItem[]>} list of changed items
|
|
54
55
|
*/
|
|
@@ -64,7 +65,12 @@ class Mcdev {
|
|
|
64
65
|
? // get source market and source BU from config
|
|
65
66
|
DevOps.getDeltaList(properties, argv.range, true, argv.filter)
|
|
66
67
|
: // If no custom filter was provided, use deployment marketLists & templating
|
|
67
|
-
DevOps.buildDeltaDefinitions(
|
|
68
|
+
DevOps.buildDeltaDefinitions(
|
|
69
|
+
properties,
|
|
70
|
+
argv.range,
|
|
71
|
+
argv.diffArr,
|
|
72
|
+
argv.skipInteraction
|
|
73
|
+
);
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
/**
|
|
@@ -105,7 +111,7 @@ class Mcdev {
|
|
|
105
111
|
* Retrieve all metadata from the specified business unit into the local file system.
|
|
106
112
|
*
|
|
107
113
|
* @param {string} businessUnit references credentials from properties.json
|
|
108
|
-
* @param {
|
|
114
|
+
* @param {TYPE.SupportedMetadataTypes[]|TYPE.TypeKeyCombo} [selectedTypesArr] limit retrieval to given metadata type
|
|
109
115
|
* @param {string[]} [keys] limit retrieval to given metadata key
|
|
110
116
|
* @param {boolean} [changelogOnly] skip saving, only create json in memory
|
|
111
117
|
* @returns {Promise.<object>} -
|
|
@@ -120,7 +126,9 @@ class Mcdev {
|
|
|
120
126
|
|
|
121
127
|
// assume a list was passed in and check each entry's validity
|
|
122
128
|
if (selectedTypesArr) {
|
|
123
|
-
for (const selectedType of selectedTypesArr)
|
|
129
|
+
for (const selectedType of Array.isArray(selectedTypesArr)
|
|
130
|
+
? selectedTypesArr
|
|
131
|
+
: Object.keys(selectedTypesArr)) {
|
|
124
132
|
if (!Util._isValidType(selectedType)) {
|
|
125
133
|
return;
|
|
126
134
|
}
|
|
@@ -195,7 +203,7 @@ class Mcdev {
|
|
|
195
203
|
* @private
|
|
196
204
|
* @param {string} cred name of Credential
|
|
197
205
|
* @param {string} bu name of BU
|
|
198
|
-
* @param {
|
|
206
|
+
* @param {TYPE.SupportedMetadataTypes[]|TYPE.TypeKeyCombo} [selectedTypesArr] limit retrieval to given metadata type/subtype
|
|
199
207
|
* @param {string[]} [keys] limit retrieval to given metadata key
|
|
200
208
|
* @param {boolean} [changelogOnly] skip saving, only create json in memory
|
|
201
209
|
* @returns {Promise.<object>} ensure that BUs are worked on sequentially
|
|
@@ -218,7 +226,9 @@ class Mcdev {
|
|
|
218
226
|
Util.logger.info(`\n :: Retrieving ${cred}/${bu}\n`);
|
|
219
227
|
const retrieveTypesArr = [];
|
|
220
228
|
if (selectedTypesArr) {
|
|
221
|
-
for (const selectedType of selectedTypesArr)
|
|
229
|
+
for (const selectedType of Array.isArray(selectedTypesArr)
|
|
230
|
+
? selectedTypesArr
|
|
231
|
+
: Object.keys(selectedTypesArr)) {
|
|
222
232
|
const [type, subType] = selectedType ? selectedType.split('-') : [];
|
|
223
233
|
const removePathArr = [properties.directories.retrieve, cred, bu, type];
|
|
224
234
|
if (
|
|
@@ -255,7 +265,7 @@ class Mcdev {
|
|
|
255
265
|
// await is required or the calls end up conflicting
|
|
256
266
|
const retrieveChangelog = await retriever.retrieve(
|
|
257
267
|
retrieveTypesArr,
|
|
258
|
-
keys,
|
|
268
|
+
Array.isArray(selectedTypesArr) ? keys : selectedTypesArr,
|
|
259
269
|
null,
|
|
260
270
|
changelogOnly
|
|
261
271
|
);
|
|
@@ -272,7 +282,7 @@ class Mcdev {
|
|
|
272
282
|
* Deploys all metadata located in the 'deploy' directory to the specified business unit
|
|
273
283
|
*
|
|
274
284
|
* @param {string} businessUnit references credentials from properties.json
|
|
275
|
-
* @param {
|
|
285
|
+
* @param {TYPE.SupportedMetadataTypes[]} [selectedTypesArr] limit deployment to given metadata type
|
|
276
286
|
* @param {string[]} [keyArr] limit deployment to given metadata keys
|
|
277
287
|
* @param {boolean} [fromRetrieve] optionally deploy whats defined via selectedTypesArr + keyArr directly from retrieve folder instead of from deploy folder
|
|
278
288
|
* @returns {Promise.<void>} -
|
|
@@ -276,7 +276,11 @@ class Asset extends MetadataType {
|
|
|
276
276
|
} else if (retrieveDir && !items.length) {
|
|
277
277
|
Util.logger.info(` Downloaded asset-${subType}: ${items.length}`);
|
|
278
278
|
}
|
|
279
|
-
|
|
279
|
+
|
|
280
|
+
return items.map((item) => {
|
|
281
|
+
item._subType = subType;
|
|
282
|
+
return item;
|
|
283
|
+
});
|
|
280
284
|
}
|
|
281
285
|
/**
|
|
282
286
|
* Retrieves extended metadata (files or extended content) of asset
|
|
@@ -446,20 +450,33 @@ class Asset extends MetadataType {
|
|
|
446
450
|
* @param {TYPE.AssetItem} metadata a single asset
|
|
447
451
|
* @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
|
|
448
452
|
* @param {string} deployDir directory of deploy files
|
|
449
|
-
* @
|
|
453
|
+
* @param {boolean} [pathOnly=false] used by getFilesToCommit which does not need the binary file to be actually read
|
|
454
|
+
* @returns {Promise.<string>} if found will return the path of the binary file
|
|
450
455
|
*/
|
|
451
|
-
static async _readExtendedFileFromFS(metadata, subType, deployDir) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
+
static async _readExtendedFileFromFS(metadata, subType, deployDir, pathOnly = false) {
|
|
457
|
+
// to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the fileName
|
|
458
|
+
// however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) "
|
|
459
|
+
if (!metadata?.fileProperties?.extension) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const fileExt = metadata.fileProperties.extension.split(' ')[0];
|
|
456
463
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
464
|
+
const path = File.normalizePath([
|
|
465
|
+
deployDir,
|
|
466
|
+
this.definition.type,
|
|
467
|
+
subType,
|
|
468
|
+
`${metadata.customerKey}.${fileExt}`,
|
|
469
|
+
]);
|
|
470
|
+
if (await File.pathExists(path)) {
|
|
471
|
+
if (!pathOnly) {
|
|
472
|
+
metadata.file = await File.readFilteredFilename(
|
|
473
|
+
[deployDir, this.definition.type, subType],
|
|
474
|
+
metadata.customerKey,
|
|
475
|
+
fileExt,
|
|
476
|
+
'base64'
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
return path;
|
|
463
480
|
}
|
|
464
481
|
}
|
|
465
482
|
/**
|
|
@@ -512,8 +529,8 @@ class Asset extends MetadataType {
|
|
|
512
529
|
metadata.memberId !== buObject.mid &&
|
|
513
530
|
!metadata[this.definition.keyField].startsWith(buObject.mid)
|
|
514
531
|
) {
|
|
515
|
-
// #3 make sure customer key is unique by
|
|
516
|
-
// check if this
|
|
532
|
+
// #3 make sure customer key is unique by suffixing it with target MID (unless we are deploying to the same MID)
|
|
533
|
+
// check if this suffixed with the source MID
|
|
517
534
|
const suffix = '-' + buObject.mid;
|
|
518
535
|
// for customer key max is 36 chars
|
|
519
536
|
metadata[this.definition.keyField] =
|
|
@@ -1362,6 +1379,7 @@ class Asset extends MetadataType {
|
|
|
1362
1379
|
const fileList = (
|
|
1363
1380
|
await Promise.all(
|
|
1364
1381
|
keyArr.map(async (key) => {
|
|
1382
|
+
// get subType, path an fileName by scanning the retrieve folder
|
|
1365
1383
|
let subType;
|
|
1366
1384
|
let filePath;
|
|
1367
1385
|
let fileName;
|
|
@@ -1391,8 +1409,13 @@ class Asset extends MetadataType {
|
|
|
1391
1409
|
break;
|
|
1392
1410
|
}
|
|
1393
1411
|
}
|
|
1394
|
-
if (
|
|
1412
|
+
if (
|
|
1413
|
+
Array.isArray(filePath) &&
|
|
1414
|
+
(await File.pathExists(File.normalizePath([...filePath, fileName])))
|
|
1415
|
+
) {
|
|
1416
|
+
// #1 load json to be able to find extracted text files & binary files
|
|
1395
1417
|
const metadata = File.readJSONFile(filePath, fileName, true, false);
|
|
1418
|
+
// #2 find all extracted text files
|
|
1396
1419
|
const fileListNested = (
|
|
1397
1420
|
await this._mergeCode(metadata, basePath, subType, metadata.customerKey)
|
|
1398
1421
|
).map((item) =>
|
|
@@ -1402,8 +1425,22 @@ class Asset extends MetadataType {
|
|
|
1402
1425
|
`${item.fileName}.${item.fileExt}`,
|
|
1403
1426
|
])
|
|
1404
1427
|
);
|
|
1428
|
+
const response = [
|
|
1429
|
+
File.normalizePath([...filePath, fileName]),
|
|
1430
|
+
...fileListNested,
|
|
1431
|
+
];
|
|
1432
|
+
// #3 get binary file
|
|
1433
|
+
const binaryFilePath = await this._readExtendedFileFromFS(
|
|
1434
|
+
metadata,
|
|
1435
|
+
subType,
|
|
1436
|
+
basePath,
|
|
1437
|
+
false
|
|
1438
|
+
);
|
|
1439
|
+
if (binaryFilePath) {
|
|
1440
|
+
response.push(binaryFilePath);
|
|
1441
|
+
}
|
|
1405
1442
|
|
|
1406
|
-
return
|
|
1443
|
+
return response;
|
|
1407
1444
|
} else {
|
|
1408
1445
|
return [];
|
|
1409
1446
|
}
|
|
@@ -792,9 +792,8 @@ class Automation extends MetadataType {
|
|
|
792
792
|
output += `* Recurrance: every ${ical.INTERVAL > 1 ? ical.INTERVAL : ''} ${
|
|
793
793
|
frequency === 'dai' ? 'day' : frequency
|
|
794
794
|
}${ical.INTERVAL > 1 ? 's' : ''} ${ical.COUNT ? `for ${ical.COUNT} times` : ''}\n`;
|
|
795
|
-
output += '\n';
|
|
796
795
|
} else if (json.schedule) {
|
|
797
|
-
output += `**Schedule:** Not defined\n
|
|
796
|
+
output += `**Schedule:** Not defined\n`;
|
|
798
797
|
}
|
|
799
798
|
} else if (json.type === 'triggered' && json.fileTrigger) {
|
|
800
799
|
output += `**File Trigger:**\n\n`;
|
|
@@ -803,12 +802,19 @@ class Automation extends MetadataType {
|
|
|
803
802
|
output += `* Pattern: ${json.fileTrigger.fileNamingPattern}\n`;
|
|
804
803
|
output += `* Folder: ${json.fileTrigger.folderLocationText}\n`;
|
|
805
804
|
}
|
|
805
|
+
// show table with automation steps
|
|
806
806
|
if (tabled && tabled.length) {
|
|
807
|
+
// add empty line to ensure the following table is rendered properly
|
|
808
|
+
output += '\n';
|
|
807
809
|
let tableSeparator = '';
|
|
808
810
|
const row1 = [];
|
|
809
811
|
for (const column of tabled[0]) {
|
|
810
812
|
row1.push(
|
|
811
|
-
`| ${column.title}${
|
|
813
|
+
`| ${column.title}${
|
|
814
|
+
column.description
|
|
815
|
+
? `<br>_<small>${column.description.replace(/\n/g, '<br>')}</small>_`
|
|
816
|
+
: ''
|
|
817
|
+
} `
|
|
812
818
|
);
|
|
813
819
|
tableSeparator += '| --- ';
|
|
814
820
|
}
|
|
@@ -284,7 +284,7 @@ class Script extends MetadataType {
|
|
|
284
284
|
ssjs = metadata.script;
|
|
285
285
|
fileExt = 'html';
|
|
286
286
|
Util.logger.warn(
|
|
287
|
-
|
|
287
|
+
` - Could not find script tags, saving code in ${metadata.name}.script-meta.html instead of as SSJS file.`
|
|
288
288
|
);
|
|
289
289
|
}
|
|
290
290
|
delete metadata.script;
|
|
@@ -315,6 +315,7 @@ class Script extends MetadataType {
|
|
|
315
315
|
const fileList = keyArr.flatMap((key) => [
|
|
316
316
|
File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]),
|
|
317
317
|
File.normalizePath([path, `${key}.${this.definition.type}-meta.ssjs`]),
|
|
318
|
+
File.normalizePath([path, `${key}.${this.definition.type}-meta.html`]),
|
|
318
319
|
]);
|
|
319
320
|
return fileList;
|
|
320
321
|
}
|
package/lib/util/devops.js
CHANGED
|
@@ -248,10 +248,11 @@ const DevOps = {
|
|
|
248
248
|
*
|
|
249
249
|
* @param {TYPE.Mcdevrc} properties project config file
|
|
250
250
|
* @param {string} range git commit range
|
|
251
|
+
* @param {TYPE.DeltaPkgItem[]} [diffArr] instead of running git diff the method can also get a list of files to process
|
|
251
252
|
* @param {TYPE.SkipInteraction} [skipInteraction] allows to skip interactive wizard
|
|
252
253
|
* @returns {Promise.<TYPE.DeltaPkgItem[]>} -
|
|
253
254
|
*/
|
|
254
|
-
async buildDeltaDefinitions(properties, range, skipInteraction) {
|
|
255
|
+
async buildDeltaDefinitions(properties, range, diffArr, skipInteraction) {
|
|
255
256
|
// check if sourceTargetMapping is valid
|
|
256
257
|
if (
|
|
257
258
|
!properties.options.deployment.sourceTargetMapping ||
|
|
@@ -304,7 +305,9 @@ const DevOps = {
|
|
|
304
305
|
const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
|
|
305
306
|
const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
|
|
306
307
|
|
|
307
|
-
const delta =
|
|
308
|
+
const delta = Array.isArray(diffArr)
|
|
309
|
+
? diffArr
|
|
310
|
+
: await DevOps.getDeltaList(properties, range, false, sourceBU);
|
|
308
311
|
// If only chaing templating and buildDefinition if required
|
|
309
312
|
if (!delta || delta.length === 0) {
|
|
310
313
|
// info/error messages was printed by DevOps.createDeltaPkg() already
|
package/lib/util/init.js
CHANGED
|
@@ -161,7 +161,7 @@ const Init = {
|
|
|
161
161
|
if (initGit.status === 'init') {
|
|
162
162
|
Util.logger.info(`Committing initial setup to Git:`);
|
|
163
163
|
Util.execSync('git', ['add', '.']);
|
|
164
|
-
Util.execSync('git', ['commit', '-m', '"Initial commit"', '--quiet']);
|
|
164
|
+
Util.execSync('git', ['commit', '-n', '-m', '"Initial commit"', '--quiet']);
|
|
165
165
|
Util.logger.info(`✔️ Configuration committed`);
|
|
166
166
|
}
|
|
167
167
|
|
|
@@ -204,7 +204,7 @@ const Init = {
|
|
|
204
204
|
if (gitStatus === 'init') {
|
|
205
205
|
Util.logger.info(`Committing first backup of your SFMC instance:`);
|
|
206
206
|
Util.execSync('git', ['add', '.']);
|
|
207
|
-
Util.execSync('git', ['commit', '-m', '"First instance backup"', '--quiet']);
|
|
207
|
+
Util.execSync('git', ['commit', '-n', '-m', '"First instance backup"', '--quiet']);
|
|
208
208
|
Util.logger.info(`✔️ SFMC instance backed up`);
|
|
209
209
|
} else if (gitStatus === 'update') {
|
|
210
210
|
Util.logger.warn(
|
package/lib/util/init.npm.js
CHANGED
|
@@ -49,7 +49,7 @@ const Init = {
|
|
|
49
49
|
this._getDefaultPackageJson(projectPackageJson);
|
|
50
50
|
await File.writeToFile('./', 'package', 'json', JSON.stringify(projectPackageJson));
|
|
51
51
|
// execute "no questions asked" npm init
|
|
52
|
-
Util.execSync('npm', ['init', '--yes']);
|
|
52
|
+
Util.execSync('npm', ['init', '--yes'], true);
|
|
53
53
|
try {
|
|
54
54
|
fileContent = File.readFileSync('package.json', 'utf8');
|
|
55
55
|
if (fileContent) {
|
package/lib/util/util.js
CHANGED
|
@@ -353,15 +353,16 @@ const Util = {
|
|
|
353
353
|
*
|
|
354
354
|
* @param {string} cmd to be executed command
|
|
355
355
|
* @param {string[]} [args] list of arguments
|
|
356
|
+
* @param {boolean} [hideOutput] if true, output of command will be hidden from CLI
|
|
356
357
|
* @returns {undefined}
|
|
357
358
|
*/
|
|
358
|
-
execSync(cmd, args) {
|
|
359
|
+
execSync(cmd, args, hideOutput) {
|
|
359
360
|
args = args || [];
|
|
360
361
|
Util.logger.info('⚡ ' + cmd + ' ' + args.join(' '));
|
|
361
362
|
|
|
362
363
|
// the following options ensure the user sees the same output and
|
|
363
364
|
// interaction options as if the command was manually run
|
|
364
|
-
const options = { stdio: [0, 1, 2] };
|
|
365
|
+
const options = hideOutput ? {} : { stdio: [0, 1, 2] };
|
|
365
366
|
return child_process.execSync(cmd + ' ' + args.join(' '), options);
|
|
366
367
|
},
|
|
367
368
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcdev",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Accenture Salesforce Marketing Cloud DevTools",
|
|
5
5
|
"author": "joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"lint-type": "eslint types/*.js",
|
|
37
37
|
"lint-test": "eslint test/**/*.js",
|
|
38
38
|
"upgrade": "npm-check --update",
|
|
39
|
-
"prepare": "husky install",
|
|
39
|
+
"manual-prepare": "husky install",
|
|
40
40
|
"test": "mocha"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
@@ -67,14 +67,14 @@
|
|
|
67
67
|
"eslint": "8.25.0",
|
|
68
68
|
"eslint-config-prettier": "8.5.0",
|
|
69
69
|
"eslint-config-ssjs": "1.1.11",
|
|
70
|
-
"eslint-plugin-jsdoc": "39.3.
|
|
70
|
+
"eslint-plugin-jsdoc": "39.3.13",
|
|
71
71
|
"eslint-plugin-mocha": "10.1.0",
|
|
72
72
|
"eslint-plugin-prettier": "4.2.1",
|
|
73
73
|
"eslint-plugin-unicorn": "44.0.2",
|
|
74
74
|
"husky": "8.0.1",
|
|
75
75
|
"jsdoc-to-markdown": "7.1.1",
|
|
76
76
|
"lint-staged": "13.0.3",
|
|
77
|
-
"mocha": "10.
|
|
77
|
+
"mocha": "10.1.0",
|
|
78
78
|
"mock-fs": "5.1.4",
|
|
79
79
|
"npm-check": "6.0.1",
|
|
80
80
|
"npm-run-all": "4.1.5",
|
package/types/mcdev.d.js
CHANGED
|
@@ -12,7 +12,8 @@ const SDK = require('sfmc-sdk');
|
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
14
14
|
* @typedef {Object.<string, string>} TemplateMap
|
|
15
|
-
* @typedef {'accountUser'|'asset'|'attributeGroup'|'automation'|'campaign'|'contentArea'|'dataExtension'|'dataExtensionField'|'dataExtensionTemplate'|'dataExtract'|'dataExtractType'|'discovery'|'email'|'emailSendDefinition'|'eventDefinition'|'fileTransfer'|'filter'|'folder'|'ftpLocation'|'importFile'|'interaction'|'list'|'mobileCode'|'mobileKeyword'|'query'|'role'|'script'|'setDefinition'|'triggeredSendDefinition'} SupportedMetadataTypes
|
|
15
|
+
* @typedef {'accountUser'|'asset'|'asset-archive'|'asset-asset'|'asset-audio'|'asset-block'|'asset-code'|'asset-document'|'asset-image'|'asset-message'|'asset-other'|'asset-rawimage'|'asset-template'|'asset-textfile'|'asset-video'|'attributeGroup'|'automation'|'campaign'|'contentArea'|'dataExtension'|'dataExtensionField'|'dataExtensionTemplate'|'dataExtract'|'dataExtractType'|'discovery'|'email'|'emailSendDefinition'|'eventDefinition'|'fileTransfer'|'filter'|'folder'|'ftpLocation'|'importFile'|'interaction'|'list'|'mobileCode'|'mobileKeyword'|'query'|'role'|'script'|'setDefinition'|'triggeredSendDefinition'} SupportedMetadataTypes
|
|
16
|
+
* @typedef {Object.<SupportedMetadataTypes, string[]>} TypeKeyCombo object-key=metadata type, value=array of external keys
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
/**
|