campaign-cli 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +242 -0
- package/bin/acc +19 -0
- package/config/acc.config.json +339 -0
- package/package.json +49 -0
- package/src/CampaignAuth.js +131 -0
- package/src/CampaignError.js +43 -0
- package/src/CampaignInstance.js +386 -0
- package/src/main.js +205 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import CampaignError from "./CampaignError.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Campaign CLI class for managing ACC (Campaign Classic) instances.
|
|
5
|
+
* Provides authentication, instance management, and connection capabilities.
|
|
6
|
+
*
|
|
7
|
+
* @class CampaignAuth
|
|
8
|
+
* @classdesc Main class for interacting with ACC instances
|
|
9
|
+
*/
|
|
10
|
+
class CampaignAuth {
|
|
11
|
+
/**
|
|
12
|
+
* Configuration key for storing instances
|
|
13
|
+
* @type {string}
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
INSTANCES_KEY = "instances";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new CampaignAuth instance.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} sdk - ACC JS SDK instance
|
|
22
|
+
* @param {Object} config - Configstore instance for persistent storage
|
|
23
|
+
* @throws {CampaignError} Throws if SDK or config parameters are missing
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const auth = new CampaignAuth(sdk, config);
|
|
27
|
+
*/
|
|
28
|
+
constructor(sdk, config) {
|
|
29
|
+
if (!sdk || !config) {
|
|
30
|
+
throw new CampaignError(
|
|
31
|
+
"SDK and Configstore instances are required to initialize CampaignAuth.",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
this.sdk = sdk;
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.instances = config.get(this.INSTANCES_KEY) || {};
|
|
37
|
+
this.instanceIds = Object.keys(this.instances);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async ip(){
|
|
41
|
+
console.log(`Fetching IP address...`);
|
|
42
|
+
const ip = await this.sdk.ip();
|
|
43
|
+
console.log(ip);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initializes a new ACC instance with the provided credentials.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} options - Initialization options
|
|
50
|
+
* @param {string} options.alias - Local alias for this instance (e.g., 'prod', 'staging')
|
|
51
|
+
* @param {string} options.host - URL of ACC root (e.g., 'http://localhost:8080')
|
|
52
|
+
* @param {string} options.user - Operator username
|
|
53
|
+
* @param {string} options.password - Operator password
|
|
54
|
+
* @returns {Promise<void>} Resolves when instance is initialized and logged in
|
|
55
|
+
* @throws {CampaignError} Throws if instance with alias already exists
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* await auth.init({
|
|
59
|
+
* alias: 'prod',
|
|
60
|
+
* host: 'http://localhost:8080',
|
|
61
|
+
* user: 'admin',
|
|
62
|
+
* password: 'password'
|
|
63
|
+
* });
|
|
64
|
+
*/
|
|
65
|
+
async init(options) {
|
|
66
|
+
if (this.instanceIds.includes(options.alias)) {
|
|
67
|
+
throw new CampaignError(
|
|
68
|
+
`Instance with alias ${options.alias} already exists. Please choose a different alias.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const { alias, host, user, password } = options;
|
|
72
|
+
this.config.set(`${this.INSTANCES_KEY}.${alias}`, { host, user, password });
|
|
73
|
+
console.log(`✅ Instance ${alias} added successfully.`);
|
|
74
|
+
return this.login(options);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Logs in to an existing ACC instance.
|
|
79
|
+
*
|
|
80
|
+
* @param {Object} options - Login options
|
|
81
|
+
* @param {string} options.alias - Alias of the instance to log in to
|
|
82
|
+
* @returns {Promise<Object>} Resolves with the authenticated client
|
|
83
|
+
* @throws {CampaignError} Throws if instance doesn't exist or login fails
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const client = await auth.login({ alias: 'prod' });
|
|
87
|
+
*/
|
|
88
|
+
async login(options) {
|
|
89
|
+
const { host, user, password } =
|
|
90
|
+
this.config.get(`instances.${options.alias}`) || {};
|
|
91
|
+
if (!host || !user || !password) {
|
|
92
|
+
throw new CampaignError(
|
|
93
|
+
`Authentication with alias "${options.alias}" doesn't exist. Use "acc auth list" to see all configured instances.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
console.log(`↔️ Connecting ${user}@${host}...`);
|
|
97
|
+
const connectionParameters =
|
|
98
|
+
this.sdk.ConnectionParameters.ofUserAndPassword(host, user, password);
|
|
99
|
+
const client = await this.sdk.init(connectionParameters);
|
|
100
|
+
await client.logon();
|
|
101
|
+
const serverInfo = client.getSessionInfo().serverInfo;
|
|
102
|
+
if (!serverInfo) {
|
|
103
|
+
throw new CampaignError(`Unable to get server info.`);
|
|
104
|
+
}
|
|
105
|
+
console.log(
|
|
106
|
+
`✅ Logged in to ${serverInfo.instanceName} (${serverInfo.releaseName} build ${serverInfo.buildNumber}) successfully.`,
|
|
107
|
+
);
|
|
108
|
+
return client;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Lists all configured ACC instances.
|
|
113
|
+
*
|
|
114
|
+
* @returns {void} Outputs list of instances to console
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* auth.list(); // Lists all configured instances
|
|
118
|
+
*/
|
|
119
|
+
list() {
|
|
120
|
+
console.log(`📚 Reading ${this.instanceIds.length} instance(s)`);
|
|
121
|
+
if(this.instanceIds.length === 0) {
|
|
122
|
+
console.log(` No instances configured yet. Use "campaign auth init" to add an instance.`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const [key, value] of Object.entries(this.instances)) {
|
|
126
|
+
console.log(` - "${key}": ${value.user}@${value.host}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default CampaignAuth;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for Campaign CLI operations.
|
|
3
|
+
* Extends the standard JavaScript Error class.
|
|
4
|
+
*
|
|
5
|
+
* @class CampaignError
|
|
6
|
+
* @extends Error
|
|
7
|
+
* @classdesc Custom error class for handling ACC-related errors
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* throw new CampaignError("Instance already exists");
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* try {
|
|
14
|
+
* await auth.login({ alias: "nonexistent" });
|
|
15
|
+
* } catch (err) {
|
|
16
|
+
* if (err instanceof CampaignError) {
|
|
17
|
+
* console.error("Campaign error:", err.message);
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
class CampaignError extends Error {
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new CampaignError instance.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} [message] - Error message
|
|
26
|
+
* @param {Object} [options] - Error options
|
|
27
|
+
* @param {string} [options.cause] - Underlying cause of the error
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Simple error
|
|
31
|
+
* throw new CampaignError("Instance not found");
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Error with cause
|
|
35
|
+
* throw new CampaignError("Login failed", { cause: originalError });
|
|
36
|
+
*/
|
|
37
|
+
constructor(message, options) {
|
|
38
|
+
super(message, options);
|
|
39
|
+
this.name = "CampaignError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default CampaignError;
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
// npm
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
// sdk
|
|
6
|
+
import sdk from "@adobe/acc-js-sdk";
|
|
7
|
+
const { DomUtil } = sdk;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Campaign Instance class for interacting with ACC instances.
|
|
11
|
+
* Handles data checking, pulling, and downloading from ACC schemas.
|
|
12
|
+
* - pull():
|
|
13
|
+
* - paginates by batch of 10 (startLine, lineCount)
|
|
14
|
+
* - download()
|
|
15
|
+
* - sdk.xml.xtkQueryDef.create(schema)
|
|
16
|
+
* - sdk.xml.xtkQueryDef.selectAll()
|
|
17
|
+
* - sdk.xml.xtkQueryDef.executeQuery()
|
|
18
|
+
* - for each XML record:
|
|
19
|
+
* - parse()
|
|
20
|
+
*
|
|
21
|
+
* @class CampaignInstance
|
|
22
|
+
* @classdesc Class for managing data operations with ACC instances
|
|
23
|
+
*/
|
|
24
|
+
class CampaignInstance {
|
|
25
|
+
REGEX_CONFIG_ATTRIBUTE = /{(.+?)}/g;
|
|
26
|
+
CONFIG_XPATH_SEP = "/";
|
|
27
|
+
CONFIG_XPATH_ATTR = "@";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates a new CampaignInstance.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} client - Authenticated ACC client
|
|
33
|
+
* @param {Object} accConfig - Configuration object defining schemas and download options
|
|
34
|
+
* @param {Object} [accConfig.*] - Schema-specific configurations
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const instance = new CampaignInstance(client, { schemas: [
|
|
38
|
+
* { schemaId: "nms:recipient", filename: "recipient_%name%.xml" }
|
|
39
|
+
* ]});
|
|
40
|
+
*/
|
|
41
|
+
constructor(client, accConfig, options = { verbose: false }) {
|
|
42
|
+
this.client = client;
|
|
43
|
+
this.accConfig = accConfig;
|
|
44
|
+
this.verbose = options.verbose;
|
|
45
|
+
this.downloadPath = options.path;
|
|
46
|
+
/**
|
|
47
|
+
* Array of schema names to process (excluding default config)
|
|
48
|
+
* @type {string[]}
|
|
49
|
+
*/
|
|
50
|
+
this.schemas = Object.keys(this.accConfig);
|
|
51
|
+
|
|
52
|
+
this.client.registerObserver({
|
|
53
|
+
onSOAPCall: (soapCall, safeRequestData) => {
|
|
54
|
+
// this.saveArchiveRequest(soapCall.request.data);
|
|
55
|
+
},
|
|
56
|
+
onSOAPCallSuccess: (soapCall, safeResponseData) => {
|
|
57
|
+
// this.saveArchiveResponse(soapCall.response);
|
|
58
|
+
},
|
|
59
|
+
onSOAPCallFailure: (soapCall, error) => {
|
|
60
|
+
// this.saveArchiveResponse(soapCall.response);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets query definition for a specific schema, merging with default config.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} schema - Schema name (e.g., 'nms:recipient')
|
|
69
|
+
* @param {Object} baseQueryDef - Base query definition
|
|
70
|
+
* @returns {Object} Merged query definition
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* const queryDef = instance._getQueryDefForSchema('nms:recipient', {
|
|
74
|
+
* schema: 'nms:recipient',
|
|
75
|
+
* operation: 'count'
|
|
76
|
+
* });
|
|
77
|
+
*/
|
|
78
|
+
_getQueryDefForSchema(schemaConfig, baseQueryDef) {
|
|
79
|
+
const configQueryDef = schemaConfig.queryDef ? schemaConfig.queryDef : {};
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...baseQueryDef,
|
|
83
|
+
...configQueryDef,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Pulls data from all schemas in the ACC instance.
|
|
89
|
+
* Implements pagination to handle large datasets.
|
|
90
|
+
*
|
|
91
|
+
* @returns {Promise<void>} Resolves when pull operation is complete
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* await instance.pull('/path/to/download');
|
|
95
|
+
*/
|
|
96
|
+
async pull(isPreview) {
|
|
97
|
+
this.log(
|
|
98
|
+
`✨ ${isPreview ? "Previewing" : "Pulling"} data to ${this.downloadPath}`,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
for (const schemaConfig of this.accConfig.schemas) {
|
|
102
|
+
const lineCount = schemaConfig.queryDef?.lineCount || 10;
|
|
103
|
+
let startLine = 1;
|
|
104
|
+
let recordsLengthTotal = 0;
|
|
105
|
+
let recordsLengthCurrent = 0;
|
|
106
|
+
do {
|
|
107
|
+
if (this.verbose) {
|
|
108
|
+
this.log(
|
|
109
|
+
` Querying instance for records from ${startLine} to ${startLine + lineCount - 1}...`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
recordsLengthCurrent = await this.downloadAndParse(
|
|
113
|
+
schemaConfig,
|
|
114
|
+
startLine,
|
|
115
|
+
lineCount,
|
|
116
|
+
isPreview,
|
|
117
|
+
);
|
|
118
|
+
startLine += lineCount;
|
|
119
|
+
recordsLengthTotal += recordsLengthCurrent;
|
|
120
|
+
} while (recordsLengthCurrent >= lineCount);
|
|
121
|
+
this.log(
|
|
122
|
+
`✅ ${schemaConfig.filename}: ${chalk.bgCyan(schemaConfig.schemaId)} ${recordsLengthTotal} records`,
|
|
123
|
+
);
|
|
124
|
+
// new line when verbose
|
|
125
|
+
if (this.verbose) {
|
|
126
|
+
this.log("");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Downloads records from a specific schema and saves them as XML files.
|
|
133
|
+
*
|
|
134
|
+
* @param {Object} schemaConfig - Schema download config
|
|
135
|
+
* @param {number} startLine - Starting line number for pagination
|
|
136
|
+
* @param {number} lineCount - Size of pagination
|
|
137
|
+
* @returns {Promise<number>} Number of records downloaded
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const count = await instance.download('nms:recipient', '/path/to/save', 1);
|
|
141
|
+
*/
|
|
142
|
+
async downloadAndParse(schemaConfig, startLine, lineCount, isPreview) {
|
|
143
|
+
const { schemaId } = schemaConfig;
|
|
144
|
+
const baseQueryDef = {
|
|
145
|
+
schema: schemaId,
|
|
146
|
+
operation: "select",
|
|
147
|
+
select: {
|
|
148
|
+
node: [{ expr: "data" }],
|
|
149
|
+
},
|
|
150
|
+
startLine: startLine,
|
|
151
|
+
lineCount: lineCount,
|
|
152
|
+
};
|
|
153
|
+
const queryDef = this._getQueryDefForSchema(schemaConfig, baseQueryDef);
|
|
154
|
+
// console.log("queryDef", JSON.stringify(queryDef));
|
|
155
|
+
const queryDefXml = DomUtil.fromJSON("queryDef", queryDef, "SimpleJson");
|
|
156
|
+
// console.log("queryDefXml", DomUtil.toXMLString(queryDefXml));
|
|
157
|
+
|
|
158
|
+
const query = this.client.NLWS.xml.xtkQueryDef.create(queryDefXml);
|
|
159
|
+
|
|
160
|
+
let message = "";
|
|
161
|
+
var recordsLength = 0;
|
|
162
|
+
try {
|
|
163
|
+
await query.selectAll(false); // @see https://opensource.adobe.com/acc-js-sdk/xtkQueryDef.html
|
|
164
|
+
const records = await query.executeQuery(); // DOMElement <srcSchema-collection><srcSchema></srcSchema>...
|
|
165
|
+
// console.log("records", DomUtil.toXMLString(records));
|
|
166
|
+
if (this.verbose) {
|
|
167
|
+
this.log(
|
|
168
|
+
`Parsing XML Response with ${records.childElementCount} children`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
var child = DomUtil.getFirstChildElement(records); // @see https://opensource.adobe.com/acc-js-sdk/domHelper.html
|
|
172
|
+
while (child) {
|
|
173
|
+
recordsLength++;
|
|
174
|
+
|
|
175
|
+
this.parse(child, schemaConfig, isPreview);
|
|
176
|
+
|
|
177
|
+
child = DomUtil.getNextSiblingElement(child);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
message = `${recordsLength} saved.`;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
message = `⚠️ Error executing query: ${err.message}.`;
|
|
183
|
+
} finally {
|
|
184
|
+
if (this.verbose) {
|
|
185
|
+
this.log(` => ${message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return recordsLength;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* manual xpath to return the last Element
|
|
193
|
+
* - abc/def => def
|
|
194
|
+
* - abc/def/@ghi => def
|
|
195
|
+
*
|
|
196
|
+
* @param {Element} element
|
|
197
|
+
* @param {string} xpath
|
|
198
|
+
* @return Element
|
|
199
|
+
*/
|
|
200
|
+
_getLastElement(element, xpath) {
|
|
201
|
+
let childTraverse = element;
|
|
202
|
+
xpath.split(this.CONFIG_XPATH_SEP).forEach((xp) => {
|
|
203
|
+
if (xp.startsWith(this.CONFIG_XPATH_ATTR)) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
childTraverse = DomUtil.getFirstChildElement(childTraverse, xp);
|
|
207
|
+
});
|
|
208
|
+
return childTraverse;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
parse(childElement, schemaConfig, isPreview) {
|
|
212
|
+
// console.log(`>>> parse with isPreview:${isPreview}`);
|
|
213
|
+
const { filename, decompose, excludeXPaths } = schemaConfig;
|
|
214
|
+
const configAttributes = this._getAttributesFromSchemaConfig(schemaConfig); // [ '@name', '@namespace' ]
|
|
215
|
+
|
|
216
|
+
const computedFilename = this._computeFilename(
|
|
217
|
+
filename,
|
|
218
|
+
configAttributes,
|
|
219
|
+
childElement,
|
|
220
|
+
);
|
|
221
|
+
const filenameOnly = path.basename(computedFilename);
|
|
222
|
+
const datapath = path.join(this.downloadPath, computedFilename);
|
|
223
|
+
|
|
224
|
+
// prepare XML by removing excluded attributes
|
|
225
|
+
if (excludeXPaths) {
|
|
226
|
+
for (let xpath of excludeXPaths) {
|
|
227
|
+
const chunks = xpath.split(this.CONFIG_XPATH_SEP);
|
|
228
|
+
const childTraverse = this._getLastElement(childElement, xpath);
|
|
229
|
+
|
|
230
|
+
// remove attribute
|
|
231
|
+
if (xpath.includes(this.CONFIG_XPATH_ATTR)) {
|
|
232
|
+
const attribute = chunks[chunks.length - 1];
|
|
233
|
+
const attributeName = attribute.replace(this.CONFIG_XPATH_ATTR, "");
|
|
234
|
+
if (!childTraverse.hasAttribute(attributeName)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
childTraverse.setAttribute(attributeName, "");
|
|
238
|
+
}
|
|
239
|
+
// remove element
|
|
240
|
+
else {
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// no decomposition: save raw XML
|
|
246
|
+
if (!decompose) {
|
|
247
|
+
const raw = DomUtil.toXMLString(childElement);
|
|
248
|
+
if (!isPreview) {
|
|
249
|
+
fs.outputFileSync(datapath, raw);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// with decomposition: save each xpath, then save the clean meta
|
|
253
|
+
else {
|
|
254
|
+
// 1. save each xpath + removeElement
|
|
255
|
+
for (const [xpath, filenameTemplate] of Object.entries(decompose)) {
|
|
256
|
+
try {
|
|
257
|
+
// compute filename
|
|
258
|
+
const decomposedFilename = this._computeFilename(
|
|
259
|
+
filenameTemplate,
|
|
260
|
+
configAttributes,
|
|
261
|
+
childElement,
|
|
262
|
+
);
|
|
263
|
+
// then traverse xpath
|
|
264
|
+
let childTraverse = this._getLastElement(childElement, xpath);
|
|
265
|
+
const elementValue = DomUtil.elementValue(childTraverse);
|
|
266
|
+
// save to file
|
|
267
|
+
const datapath = path.join(this.downloadPath, decomposedFilename);
|
|
268
|
+
if (!isPreview) {
|
|
269
|
+
fs.outputFileSync(datapath, elementValue);
|
|
270
|
+
}
|
|
271
|
+
const decomposedFilenameOnly = path.basename(decomposedFilename);
|
|
272
|
+
if (this.verbose) {
|
|
273
|
+
this.log(`${chalk.underline(decomposedFilenameOnly)} `, false);
|
|
274
|
+
}
|
|
275
|
+
// removeElement
|
|
276
|
+
if (childTraverse) {
|
|
277
|
+
childTraverse.textContent = ""; // @since 0.5.1, instead of removeChild that removed attributes
|
|
278
|
+
}
|
|
279
|
+
} catch (err) {
|
|
280
|
+
this.log(`(⚠️ warning:parse ${err.message})`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// 2. save meta
|
|
284
|
+
const metaContent = DomUtil.toXMLString(childElement);
|
|
285
|
+
if (!isPreview) {
|
|
286
|
+
fs.outputFileSync(datapath, metaContent);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.verbose) {
|
|
291
|
+
this.log(`${chalk.underline(filenameOnly)} `, false);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
_getAttributesFromSchemaConfig(schemaConfig) {
|
|
296
|
+
const configAttributesRe = schemaConfig.filename.matchAll(
|
|
297
|
+
this.REGEX_CONFIG_ATTRIBUTE,
|
|
298
|
+
); // [object RegExp String Iterator]
|
|
299
|
+
const configAttributesArr = Array.from(configAttributesRe); // [ [ '@name', '@name' ], ... ]
|
|
300
|
+
return configAttributesArr.map((attr) => attr[1]); // [ '@name', '@namespace' ]
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
_computeFilename(configFilename, configAttributes, record) {
|
|
304
|
+
var filename = configFilename;
|
|
305
|
+
for (let configAttribute of configAttributes) {
|
|
306
|
+
const value = DomUtil.getAttributeAsString(
|
|
307
|
+
record,
|
|
308
|
+
configAttribute.replace(this.CONFIG_XPATH_ATTR, ""),
|
|
309
|
+
);
|
|
310
|
+
filename = filename.replace(`{${configAttribute}}`, value);
|
|
311
|
+
}
|
|
312
|
+
return filename;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Saves SOAP request to archive file with timestamp.
|
|
317
|
+
*
|
|
318
|
+
* @param {string} rawRequest - Raw SOAP request XML
|
|
319
|
+
* @returns {void}
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* instance.saveArchiveRequest('<soap:Envelope>...</soap:Envelope>');
|
|
323
|
+
*/
|
|
324
|
+
saveArchiveRequest(rawRequest) {
|
|
325
|
+
const archiveRequest =
|
|
326
|
+
"archives/" + this.getArchiveDate() + "-CampaignInstance-request.xml";
|
|
327
|
+
fs.outputFileSync(archiveRequest, rawRequest, function (errFs) {
|
|
328
|
+
throw errFs;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Saves SOAP response to archive file with timestamp.
|
|
334
|
+
*
|
|
335
|
+
* @param {string} rawResponse - Raw SOAP response XML
|
|
336
|
+
* @returns {void}
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* instance.saveArchiveResponse('<soap:Envelope>...</soap:Envelope>');
|
|
340
|
+
*/
|
|
341
|
+
saveArchiveResponse(rawResponse) {
|
|
342
|
+
const archiveResponse =
|
|
343
|
+
"archives/" + this.getArchiveDate() + "-CampaignInstance-response.xml";
|
|
344
|
+
fs.outputFileSync(archiveResponse, rawResponse, function (errFs) {
|
|
345
|
+
throw errFs;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Generates timestamp string for archive files in format: YYYY/MM/DD/HH-mm-ss_ms
|
|
351
|
+
*
|
|
352
|
+
* @returns {string} Formatted timestamp string
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* const timestamp = instance.getArchiveDate(); // "2023/01/15/14-30-45_123"
|
|
356
|
+
*/
|
|
357
|
+
getArchiveDate() {
|
|
358
|
+
var ts_hms = new Date();
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
ts_hms.getFullYear() +
|
|
362
|
+
"/" +
|
|
363
|
+
("0" + (ts_hms.getMonth() + 1)).slice(-2) +
|
|
364
|
+
"/" +
|
|
365
|
+
("0" + ts_hms.getDate()).slice(-2) +
|
|
366
|
+
"/" +
|
|
367
|
+
("0" + ts_hms.getHours()).slice(-2) +
|
|
368
|
+
"-" +
|
|
369
|
+
("0" + ts_hms.getMinutes()).slice(-2) +
|
|
370
|
+
"-" +
|
|
371
|
+
("0" + ts_hms.getSeconds()).slice(-2) +
|
|
372
|
+
"_" +
|
|
373
|
+
ts_hms.getMilliseconds()
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
log(text, newLine = true) {
|
|
378
|
+
if (newLine) {
|
|
379
|
+
console.log(text);
|
|
380
|
+
} else {
|
|
381
|
+
process.stdout.write(text);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export default CampaignInstance;
|