Skip to content

Commit

Permalink
Merge pull request #18 from knennigtri/dev
Browse files Browse the repository at this point in the history
Update for OAuth
  • Loading branch information
knennigtri committed Sep 26, 2023
2 parents b01f869 + 664143d commit 7aa6064
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 158 deletions.
88 changes: 55 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This is a project to automates postman collections using the [Reactor API](https
- [Export a Tag](#export-a-tag)
- [Import a Tag](#import-a-tag)
- [CEDRLP params](#cedrlp-params)
- [Import into other Organizations](#import-into-other-organizations)
- [Import into other Adobe Organizations](#import-into-other-adobe-organizations)
- [Delete tag properties that contain a specific string](#delete-tag-properties-that-contain-a-specific-string)
- [Using this tool without NPM](#using-this-tool-without-npm)
- [Postman files](#postman-files)
Expand All @@ -38,52 +38,68 @@ npm install -g @knennigtri/aep-tag-tool

Export a tag property:
```bash
aep-tag-tool -c myconfig.yml --export PR12345678901234567890
aep-tag-tool -c auth-config.json --export PR12345678901234567890
```

Import a tag property:
```bash
aep-tag-tool -c myconfig.yml --import myImportData.json
aep-tag-tool -c auth-config.json --import tagPropertyData.json
```

Delete a tag properties that contain 2022 in the title
```bash
aep-tag-tool -c myconfig.yml --delete "2022"
aep-tag-tool -c auth-config.json --delete "2023"
```

## Create config file for Authentication
1. Create and [Adobe IO project](https://developer.adobe.com/dep/guides/dev-console/create-project/)
1. Add the Experiance Platform Launch API
1. Generate a public/private key pair
2. Download the public/private key
2. Go to Service Account (JWT)
1. In the top right click **Download JSON**
2. Update downloaded file to `config.json`
3. Update `config.json` to include the path to the private.key you downloaded earlier.
```JSON
{
"CLIENT_SECRET": "xxxxxxxxxxxxxxxxxxxxx",
"ORG_ID": "xxxxxxxxxxxxxxxxxxxxx@AdobeOrg",
"API_KEY": "xxxxxxxxxxxxxxxxxxxxx",
"TECHNICAL_ACCOUNT_ID": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com",
"TECHNICAL_ACCOUNT_EMAIL": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com",
"PUBLIC_KEYS_WITH_EXPIRY": {},
"PRIVATE_Key": "path/to/private.key"
}
```
2. (JWT only) Download the public/private key
2. Go to the Credentials screen and download the JSON. Adobe has deprecated JWT and OAuth is preferred.

For OAuth credentials, make sure the JSON contains at least:

```json
{
"ORG_ID": "xxxxxxxxxxxxxxxxxxxxx@AdobeOrg",
"CLIENT_SECRETS": [ "xxxxxxxxxxxxxxxxxxxxx" ],
"CLIENT_ID": "xxxxxxxxxxxxxxxxxxxxx",
"SCOPES": [
"xxxxxxxxx",
"xxxxxxxxx",
"xxxxxxxxx"
]
}
```

For JWT credentials, download the private key add PRIVATE_KEY:

```json
{
"CLIENT_SECRET": "xxxxxxxxxxxxxxxxxxxxx",
"ORG_ID": "xxxxxxxxxxxxxxxxxxxxx@AdobeOrg",
"API_KEY": "xxxxxxxxxxxxxxxxxxxxx",
"TECHNICAL_ACCOUNT_ID": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com",
"TECHNICAL_ACCOUNT_EMAIL": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com",
"PUBLIC_KEYS_WITH_EXPIRY": {},
"PRIVATE_Key": "path/to/private.key"
}
```

Alternatively, you can use yaml as well:

```yaml
---
API_KEY: xxxxxxxxxxxxxxxxxxx
CLIENT_ID: xxxxxxxxxxxxxxxxxxx
CLIENT_SECRET: xxxxxxxxxxxxxxxxxxx
ORG_ID: xxxxxxxxxxxxxxxxxxx@AdobeOrg
TECHNICAL_ACCOUNT_ID: xxxxxxxxxxxxxxxxxxx@techacct.adobe.com
PRIVATE_KEY: ./private.key
SCOPES: [xxxxx, xxxxxx, xxxxx]
---
```

## Usage

```bash
aep-tag-tool -h
Usage: aep-tag-tool [ARGS]
Expand All @@ -94,11 +110,13 @@ Usage: aep-tag-tool [ARGS]
-d, --delete <searchStr> Mode to delete properties containing a specific string
-C,-E,-D,-R,-L,-P [import] Options to partially import. See -h import
-f, --file <file> [import] file containing import json
-t, --title <title> [import] optional new title of tag property;
-p, --pid <pid> [export, import] property ID
-s, --search <str> [delete] search string for properties deletion
-o, --output <folder> [export] folder path to save export property. Default ./
-g <postman_globals.json> Not supported currently
-v, --version Displays version of this package
--jwt Use if using JWT Auth. Deprecated by Adobe. Default is OAuth.
-h, --help
config
export
Expand Down Expand Up @@ -153,6 +171,7 @@ Requires:
Optionally include the property file with a parameter
```
-f, --file <file> [import] file containing import json
-t, --title <title> [import] optional new title of tag property;
-p, --pid <pid> [export, import] property ID
```
Note: PID is ignored unless importing to an existing property (-C is omited)
Expand All @@ -176,19 +195,21 @@ If `-C` is not used with the remaining parameters, a PID is required in paramete
`-R` Imports rule components. `propertyFile.rules.[rules]` is required.
`-L` Builds a library of all items the Dev environment
`-L` Builds a library of all items into the Dev environment
`-P` Publishes the library into Prod
### Import into other Organizations
1. Create a new [AEP Tag property](https://experienceleague.adobe.com/docs/experience-manager-learn/sites/integrations/experience-platform-launch/create-launch-property.html?lang=en).
1. Take note of the PID from the URL: Pxxxxxxxxxxxxxxxxxxxxxxx
2. Manually adding extensions automatically put default values related to your organization. Look at the propertyFile.json you'd like to import and manually install those extension in you new property
3. The command below will only import Data Elements (-D) and Rules (-R) from the propertyFile.json
### Import into other Adobe Organizations
1. Export the desired property as specified above
2. In the new Organization, create an Adobe IO project with the Launch API
1. download the OAuth JSON
3. The command below will only import (E)xtensions, (D)ata Elements and (R)ules from the origPropertyExport.json and build a (L)ibrary into the Dev Environment:
```
aep-tag-tool -c config.json --import propertyFile.json -p <yourPID> -DR
aep-tag-tool -c newOrg-oauth-config.json --import origPropertyExport.json -EDRL
```
4. Verify the import and build and deploy a new Library
1. Manually update any values unique to the Adobe org. Typically in the Extension values.
2. Verify the import and build and deploy a new Library
## Delete tag properties that contain a specific string
Quickly delete web properties that might have been created with this tool. Delete mode allows you to search for web properties in an Adobe organization based on a search string. If any web properties contain the search string, they are deleted. This is particularly useful if you are developing your own property to import/export since all properties end with a timestamp. Searching (-s) for `2022-10-25` would delete `MyProperty 2022-10-25T20:57:42.049Z`, `MyProperty 2022-10-25T21:57:42.049Z`, and `MyProperty 2022-10-25T20:58:42.049Z`.
Expand All @@ -208,9 +229,10 @@ Optionally include the search string with a parameter
The Postman collections apart of this tool can also be used with [Postman](https://www.postman.com/) or [npm newman](https://www.npmjs.com/package/newman). See the [extra docs](docs/README.md) to learn more.
### Postman files
* Download the [Authentication Collection](postman/Adobe%20IO%20Token.postman_collection.json)
* Download the [OAuth Authentication Collection](postman/Adobe%20IO%20Token%20OAuth.postman_collection.json)
* Download the [Import Collection](postman/Import%20Tag%20Property.postman_collection.json)
* Download the [Export Collection](postman/Export%20Tag%20Property.postman_collection.json)
* Download the [Delete Collection](postman/Export%20Tag%20Property.postman_collection.json)
* Download a sample [Environment file](postman/aep-tag-tool.postman_environment.json)
* See configuration instructions: [docs/environment.md](docs/environment.md)
* See configuration instructions: [docs/environment.md](docs/environment.md)
* Download the [JWT Authentication Collection](postman/Adobe%20IO%20Token.postman_collection.json)
8 changes: 0 additions & 8 deletions example.config.yml

This file was deleted.

12 changes: 12 additions & 0 deletions example.oauth.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"ORG_ID": "xxxxxxxxxxxxxxxxxxxxx@AdobeOrg",
"CLIENT_SECRETS": [ "xxxxxxxxxxxxxxxxxxxxx" ],
"CLIENT_ID": "xxxxxxxxxxxxxxxxxxxxx",
"SCOPES": [
"xxxxxxxxx",
"xxxxxxxxx",
"xxxxxxxxx"
],
"TECHNICAL_ACCOUNT_ID": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com",
"TECHNICAL_ACCOUNT_EMAIL": "xxxxxxxxxxxxxxxxxxxxx@techacct.adobe.com"
}
65 changes: 36 additions & 29 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const packageInfo = require("./package.json");
const minimist = require("minimist");
const args = minimist(process.argv.slice(2));
//https://www.npmjs.com/package/debug
//Mac: DEBUG=* aep-tag-tool....
//WIN: set DEBUG=* & aep-tag-tool....
const debug = require("debug");
const debugDryRun = require("debug")("dryrun");
const debugArgs = require("debug")("args");
Expand All @@ -19,13 +21,16 @@ const modes = {
import: "import",
delete: "delete"
};

function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchStrParam){
//TODO reimplement as a JSON input
function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchStrParam, titleParam, authMethod){
let mode = "";
if(modeParam && modeParam.toLowerCase() == modes.export || args.export || args.e) mode = modes.export;
if(modeParam && modeParam.toLowerCase() == modes.import || args.import || args.i) mode = modes.import;
if(modeParam && modeParam.toLowerCase() == modes.delete || args.delete || args.d) mode = modes.delete;
if(modeParam?.toLowerCase() == modes.export || args.export || args.e) mode = modes.export;
if(modeParam?.toLowerCase() == modes.import || args.import || args.i) mode = modes.import;
if(modeParam?.toLowerCase() == modes.delete || args.delete || args.d) mode = modes.delete;
let argsEnv = envParam || args.config || args.c;
let argsAuth = authMethod?.toLowerCase() || launch.auth.oauth; //default is oauth
if(args.jwt) argsAuth = launch.auth.jwt;
if(args.oauth) argsAuth = launch.auth.oauth;

const argsVersion = args.v || args.version;
const argsHelp = args.h || args.help;
Expand Down Expand Up @@ -65,7 +70,7 @@ function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchS
// aep-tag-tool -c ./myCSV.csv --delete "2023"

//create AuthObj from config.yml
let authObj = launch.createAuthObjSync(argsEnv);
let authObj = launch.createAuthObjSync(argsEnv, argsAuth);
debugDryRun(JSON.stringify(authObj, null, 2));
if(!authObj) {
console.log("Authentication not properly configured. Make sure your config file has the required Auth values.");
Expand Down Expand Up @@ -101,7 +106,8 @@ function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchS
});
}
} else if(mode == modes.import){ //IMPORT
const importPID = pidParam || args.pid || args.p || "";
let importPID = pidParam || args.pid || args.p || "";
let importTitle = titleParam || args.title || args.t || "";

//importFile. --file, -f first priority, --import, -i second priority
let propertiesFile = dataParam || args.file || args.f || args.import || args.i;
Expand All @@ -117,10 +123,15 @@ function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchS
return;
// }
} else {
propsToImport[propertiesFile] = importPID;
//single property to import
let propertyObj = launch.createLaunchObjSync(propertiesFile);
propertyObj.propertyName = importTitle;
propertyObj.propID = importPID;
propsToImport[propertiesFile] = propertyObj;
}

debugDryRun(JSON.stringify(propsToImport,null,2));
debugDryRun(propsToImport);

recursiveImport(authObj, propsToImport);

} else if(mode == modes.delete){ //DELETE
Expand Down Expand Up @@ -151,37 +162,33 @@ function init(envParam, modeParam, dataParam, pidParam, workingDirParam, searchS
}

//TODO Recursively importing files results in messages being mixed. Need to review before enabling.
function recursiveImport(authObj, propertyFilesToImport){
function recursiveImport(authObj, propertyObjsToImport){
//Grab the first file and remove it from propertyFilesToImport
let nextPropertyFile = Object.keys(propertyFilesToImport)[0];
let nextPID = propertyFilesToImport[nextPropertyFile];
delete propertyFilesToImport[nextPropertyFile];

if(nextPropertyFile) {
console.log("Importing: " + nextPropertyFile);
let propertyFile = Object.keys(propertyObjsToImport)[0];
let propertyObj = Object.values(propertyObjsToImport)[0];
delete propertyObjsToImport[propertyFile];

let propertyObj = launch.createLaunchObjSync(nextPropertyFile);
if(!propertyObj){
console.log("Cannot parse or it DNE: " + nextPropertyFile);
if(propertyFile) {
console.log("Importing: " + propertyFile);
if(!propertyFile){
console.log("Cannot parse or it DNE: " + propertyFile);
console.log("Skipping...");
} else {
let actions = newman.getImportActions(args.C, args.E, args.D, args.R, args.L, args.P);
if(!actions.includes("C") && !nextPID){
if(!actions.includes("C")){
console.log("A PID (-p) is required when importing without creating a new property");
console.log("Skipping..");
} else {
if(debug.enabled("dryrun")){
// var f2 = function (k, v) { return k && v && typeof v !== "number" ? "" + v : v; };
debugDryRun("Importing: " + nextPropertyFile);
// debugDryRun(JSON.stringify(propertyObj, f2, 2));
debugDryRun("PID: " + nextPID);
debugDryRun("Actions: " + actions);
debugDryRun("Auth: " + authObj);
return recursiveImport(authObj, propertyFilesToImport);
debugDryRun("Importing: " + propertyFile + "\n" +
"PID: " + propertyObj.propID + "\n" +
"Actions: " + actions + "\n" +
"Auth: " + authObj);
return recursiveImport(authObj, propertyObjsToImport);
} else {
//TODO decide on global inclusion
return newman.importTag(authObj, propertyObj, actions, nextPID, "")
.then(() => recursiveImport(authObj, propertyFilesToImport));
return newman.importTag(authObj, propertyObj, actions, "")
.then(() => recursiveImport(authObj, propertyObjsToImport));
}
}
}
Expand Down
Loading

0 comments on commit 7aa6064

Please sign in to comment.