@rockcarver/frodo-lib 0.11.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 +32 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/README.md +121 -0
- package/.github/workflows/pipeline.yml +287 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +512 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/docs/CONTRIBUTE.md +96 -0
- package/docs/PIPELINE.md +169 -0
- package/docs/images/npm_versioning_guidelines.png +0 -0
- package/docs/images/release_pipeline.png +0 -0
- package/jsconfig.json +6 -0
- package/package.json +95 -0
- package/resources/sampleEntitiesFile.json +8 -0
- package/resources/sampleEnvFile.env +2 -0
- package/src/api/AuthenticateApi.js +33 -0
- package/src/api/BaseApi.js +242 -0
- package/src/api/CirclesOfTrustApi.js +87 -0
- package/src/api/EmailTemplateApi.js +37 -0
- package/src/api/IdmConfigApi.js +88 -0
- package/src/api/LogApi.js +45 -0
- package/src/api/ManagedObjectApi.js +62 -0
- package/src/api/OAuth2ClientApi.js +69 -0
- package/src/api/OAuth2OIDCApi.js +73 -0
- package/src/api/OAuth2ProviderApi.js +32 -0
- package/src/api/RealmApi.js +99 -0
- package/src/api/Saml2Api.js +176 -0
- package/src/api/ScriptApi.js +84 -0
- package/src/api/SecretsApi.js +151 -0
- package/src/api/ServerInfoApi.js +41 -0
- package/src/api/SocialIdentityProvidersApi.js +114 -0
- package/src/api/StartupApi.js +45 -0
- package/src/api/ThemeApi.js +181 -0
- package/src/api/TreeApi.js +207 -0
- package/src/api/VariablesApi.js +104 -0
- package/src/api/utils/ApiUtils.js +77 -0
- package/src/api/utils/ApiUtils.test.js +96 -0
- package/src/api/utils/Base64.js +62 -0
- package/src/index.js +32 -0
- package/src/index.test.js +13 -0
- package/src/ops/AdminOps.js +901 -0
- package/src/ops/AuthenticateOps.js +342 -0
- package/src/ops/CirclesOfTrustOps.js +350 -0
- package/src/ops/ConnectionProfileOps.js +254 -0
- package/src/ops/EmailTemplateOps.js +326 -0
- package/src/ops/IdmOps.js +227 -0
- package/src/ops/IdpOps.js +342 -0
- package/src/ops/JourneyOps.js +2026 -0
- package/src/ops/LogOps.js +357 -0
- package/src/ops/ManagedObjectOps.js +34 -0
- package/src/ops/OAuth2ClientOps.js +151 -0
- package/src/ops/OrganizationOps.js +85 -0
- package/src/ops/RealmOps.js +139 -0
- package/src/ops/SamlOps.js +541 -0
- package/src/ops/ScriptOps.js +211 -0
- package/src/ops/SecretsOps.js +288 -0
- package/src/ops/StartupOps.js +114 -0
- package/src/ops/ThemeOps.js +379 -0
- package/src/ops/VariablesOps.js +185 -0
- package/src/ops/templates/OAuth2ClientTemplate.json +270 -0
- package/src/ops/templates/OrgModelUserAttributesTemplate.json +149 -0
- package/src/ops/templates/cloud/GenericExtensionAttributesTemplate.json +392 -0
- package/src/ops/templates/cloud/managed.json +4119 -0
- package/src/ops/utils/Console.js +434 -0
- package/src/ops/utils/DataProtection.js +92 -0
- package/src/ops/utils/DataProtection.test.js +28 -0
- package/src/ops/utils/ExportImportUtils.js +146 -0
- package/src/ops/utils/ExportImportUtils.test.js +119 -0
- package/src/ops/utils/OpsUtils.js +76 -0
- package/src/ops/utils/Wordwrap.js +11 -0
- package/src/storage/SessionStorage.js +45 -0
- package/src/storage/StaticStorage.js +15 -0
- package/test/e2e/journey/baseline/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/baseline/Login.journey.json +205 -0
- package/test/e2e/journey/baseline/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/baseline/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/baseline/Registration.journey.json +249 -0
- package/test/e2e/journey/baseline/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/baseline/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/baseline/allAlphaJourneys.journeys.json +1520 -0
- package/test/e2e/journey/delete/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/delete/Login.journey.json +205 -0
- package/test/e2e/journey/delete/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/delete/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/delete/Registration.journey.json +249 -0
- package/test/e2e/journey/delete/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/delete/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/delete/deleteMe.journey.json +230 -0
- package/test/e2e/journey/list/Disabled.journey.json +43 -0
- package/test/e2e/journey/list/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/list/Login.journey.json +205 -0
- package/test/e2e/journey/list/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/list/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/list/Registration.journey.json +249 -0
- package/test/e2e/journey/list/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/list/UpdatePassword.journey.json +323 -0
- package/test/e2e/setup.js +107 -0
- package/test/e2e/theme/baseline/Contrast.theme.json +95 -0
- package/test/e2e/theme/baseline/Highlander.theme.json +95 -0
- package/test/e2e/theme/baseline/Robroy.theme.json +95 -0
- package/test/e2e/theme/baseline/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/baseline/Zardoz.theme.json +95 -0
- package/test/e2e/theme/import/Contrast.theme.json +95 -0
- package/test/e2e/theme/import/Highlander.theme.json +95 -0
- package/test/e2e/theme/import/Robroy.theme.json +95 -0
- package/test/e2e/theme/import/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/import/Zardoz.default.theme.json +95 -0
- package/test/fs_tmp/.gitkeep +2 -0
- package/test/global/setup.js +65 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import storage from '../storage/SessionStorage.js';
|
|
4
|
+
import DataProtection from './utils/DataProtection.js';
|
|
5
|
+
import {
|
|
6
|
+
createObjectTable,
|
|
7
|
+
createTable,
|
|
8
|
+
printMessage,
|
|
9
|
+
} from './utils/Console.js';
|
|
10
|
+
|
|
11
|
+
const dataProtection = new DataProtection();
|
|
12
|
+
|
|
13
|
+
const fileOptions = {
|
|
14
|
+
options: 'utf8',
|
|
15
|
+
indentation: 4,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getConnectionProfilesFolder = () => `${os.homedir()}/.frodo`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get connection profiles file name
|
|
22
|
+
* @returns {String} connection profiles file name
|
|
23
|
+
*/
|
|
24
|
+
export function getConnectionProfilesFileName() {
|
|
25
|
+
return `${os.homedir()}/.frodo/.frodorc`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Find connection profile
|
|
30
|
+
* @param {Object} connectionProfiles connection profile object
|
|
31
|
+
* @param {String} host tenant host url or unique substring
|
|
32
|
+
* @returns {Object} connection profile object or null
|
|
33
|
+
*/
|
|
34
|
+
function findConnectionProfile(connectionProfiles, host) {
|
|
35
|
+
for (const tenant in connectionProfiles) {
|
|
36
|
+
if (tenant.includes(host)) {
|
|
37
|
+
const profile = connectionProfiles[tenant];
|
|
38
|
+
profile.tenant = tenant;
|
|
39
|
+
return profile;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* List connection profiles
|
|
47
|
+
* @param {boolean} long Long list format with details
|
|
48
|
+
*/
|
|
49
|
+
export function listConnectionProfiles(long = false) {
|
|
50
|
+
const filename = getConnectionProfilesFileName();
|
|
51
|
+
try {
|
|
52
|
+
const data = fs.readFileSync(filename, 'utf8');
|
|
53
|
+
const connectionsData = JSON.parse(data);
|
|
54
|
+
if (long) {
|
|
55
|
+
const table = createTable(['Host', 'Username', 'Log API Key']);
|
|
56
|
+
Object.keys(connectionsData).forEach((c) => {
|
|
57
|
+
table.push([
|
|
58
|
+
c,
|
|
59
|
+
connectionsData[c].username,
|
|
60
|
+
connectionsData[c].logApiKey,
|
|
61
|
+
]);
|
|
62
|
+
});
|
|
63
|
+
printMessage(table.toString(), 'data');
|
|
64
|
+
} else {
|
|
65
|
+
Object.keys(connectionsData).forEach((c) => {
|
|
66
|
+
printMessage(`${c}`, 'data');
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
printMessage(
|
|
70
|
+
'Any unique substring of a saved host can be used as the value for host parameter in all commands',
|
|
71
|
+
'info'
|
|
72
|
+
);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
printMessage(`No connections found in ${filename} (${e.message})`, 'error');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Initialize connection profiles
|
|
80
|
+
*/
|
|
81
|
+
export function initConnectionProfiles() {
|
|
82
|
+
// create connections.json file if it doesn't exist
|
|
83
|
+
const folderName = getConnectionProfilesFolder();
|
|
84
|
+
const filename = getConnectionProfilesFileName();
|
|
85
|
+
if (!fs.existsSync(folderName)) {
|
|
86
|
+
fs.mkdirSync(folderName, { recursive: true });
|
|
87
|
+
if (!fs.existsSync(filename)) {
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
filename,
|
|
90
|
+
JSON.stringify({}, null, fileOptions.indentation)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// encrypt the password from clear text to aes-256-GCM
|
|
95
|
+
else {
|
|
96
|
+
const data = fs.readFileSync(filename, fileOptions.options);
|
|
97
|
+
const connectionsData = JSON.parse(data);
|
|
98
|
+
let convert = false;
|
|
99
|
+
Object.keys(connectionsData).forEach(async (conn) => {
|
|
100
|
+
if (connectionsData[conn].password) {
|
|
101
|
+
convert = true;
|
|
102
|
+
connectionsData[conn].encodedPassword = await dataProtection.encrypt(
|
|
103
|
+
connectionsData[conn].password
|
|
104
|
+
); // Buffer.from(connectionsData[conn].password).toString('base64');
|
|
105
|
+
delete connectionsData[conn].password;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (convert) {
|
|
109
|
+
fs.writeFileSync(
|
|
110
|
+
filename,
|
|
111
|
+
JSON.stringify(connectionsData, null, fileOptions.indentation)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get connection profile by host
|
|
119
|
+
* @param {String} host host tenant host url or unique substring
|
|
120
|
+
* @returns {Object} connection profile or null
|
|
121
|
+
*/
|
|
122
|
+
export async function getConnectionProfileByHost(host) {
|
|
123
|
+
try {
|
|
124
|
+
const filename = getConnectionProfilesFileName();
|
|
125
|
+
const connectionsData = JSON.parse(
|
|
126
|
+
fs.readFileSync(filename, fileOptions.options)
|
|
127
|
+
);
|
|
128
|
+
const profile = findConnectionProfile(connectionsData, host);
|
|
129
|
+
if (!profile) {
|
|
130
|
+
printMessage(
|
|
131
|
+
`Profile for ${host} not found. Please specify credentials on command line`,
|
|
132
|
+
'error'
|
|
133
|
+
);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
tenant: profile.tenant,
|
|
138
|
+
username: profile.username ? profile.username : null,
|
|
139
|
+
password: profile.encodedPassword
|
|
140
|
+
? await dataProtection.decrypt(profile.encodedPassword)
|
|
141
|
+
: null,
|
|
142
|
+
key: profile.logApiKey ? profile.logApiKey : null,
|
|
143
|
+
secret: profile.logApiSecret ? profile.logApiSecret : null,
|
|
144
|
+
};
|
|
145
|
+
} catch (e) {
|
|
146
|
+
printMessage(
|
|
147
|
+
`Can not read saved connection info, please specify credentials on command line: ${e}`,
|
|
148
|
+
'error'
|
|
149
|
+
);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get connection profile
|
|
156
|
+
* @returns {Object} connection profile or null
|
|
157
|
+
*/
|
|
158
|
+
export async function getConnectionProfile() {
|
|
159
|
+
return getConnectionProfileByHost(storage.session.getTenant());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Save connection profile
|
|
164
|
+
*/
|
|
165
|
+
export async function saveConnectionProfile() {
|
|
166
|
+
const filename = getConnectionProfilesFileName();
|
|
167
|
+
printMessage(`Saving creds in ${filename}...`);
|
|
168
|
+
let connectionsData = {};
|
|
169
|
+
let existingData = {};
|
|
170
|
+
try {
|
|
171
|
+
fs.statSync(filename);
|
|
172
|
+
const data = fs.readFileSync(filename, 'utf8');
|
|
173
|
+
connectionsData = JSON.parse(data);
|
|
174
|
+
if (connectionsData[storage.session.getTenant()]) {
|
|
175
|
+
existingData = connectionsData[storage.session.getTenant()];
|
|
176
|
+
printMessage(
|
|
177
|
+
`Updating existing connection profile ${storage.session.getTenant()}`
|
|
178
|
+
);
|
|
179
|
+
} else
|
|
180
|
+
printMessage(`Adding connection profile ${storage.session.getTenant()}`);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
printMessage(
|
|
183
|
+
`Creating connection profile file ${filename} with ${storage.session.getTenant()}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (storage.session.getUsername())
|
|
187
|
+
existingData.username = storage.session.getUsername();
|
|
188
|
+
if (storage.session.getPassword())
|
|
189
|
+
existingData.encodedPassword = await dataProtection.encrypt(
|
|
190
|
+
storage.session.getPassword()
|
|
191
|
+
); // Buffer.from(storage.session.getPassword()).toString('base64');
|
|
192
|
+
if (storage.session.getLogApiKey())
|
|
193
|
+
existingData.logApiKey = storage.session.getLogApiKey();
|
|
194
|
+
if (storage.session.getLogApiSecret())
|
|
195
|
+
existingData.logApiSecret = storage.session.getLogApiSecret();
|
|
196
|
+
connectionsData[storage.session.getTenant()] = existingData;
|
|
197
|
+
|
|
198
|
+
fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Delete connection profile
|
|
203
|
+
* @param {String} host host tenant host url or unique substring
|
|
204
|
+
*/
|
|
205
|
+
export function deleteConnectionProfile(host) {
|
|
206
|
+
const filename = getConnectionProfilesFileName();
|
|
207
|
+
let connectionsData = {};
|
|
208
|
+
fs.stat(filename, (err) => {
|
|
209
|
+
if (err == null) {
|
|
210
|
+
const data = fs.readFileSync(filename, 'utf8');
|
|
211
|
+
connectionsData = JSON.parse(data);
|
|
212
|
+
const profile = findConnectionProfile(connectionsData, host);
|
|
213
|
+
if (profile) {
|
|
214
|
+
printMessage(`Deleting connection profile ${profile.tenant}`);
|
|
215
|
+
delete connectionsData[profile.tenant];
|
|
216
|
+
fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));
|
|
217
|
+
} else {
|
|
218
|
+
printMessage(`No connection profile ${host} found`);
|
|
219
|
+
}
|
|
220
|
+
} else if (err.code === 'ENOENT') {
|
|
221
|
+
printMessage(`Connection profile file ${filename} not found`);
|
|
222
|
+
} else {
|
|
223
|
+
printMessage(
|
|
224
|
+
`Error in deleting connection profile: ${err.code}`,
|
|
225
|
+
'error'
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function describeConnectionProfile(host, showSecrets) {
|
|
232
|
+
const profile = await getConnectionProfileByHost(host);
|
|
233
|
+
if (profile) {
|
|
234
|
+
if (!showSecrets) {
|
|
235
|
+
delete profile.password;
|
|
236
|
+
delete profile.secret;
|
|
237
|
+
}
|
|
238
|
+
if (!profile.key) {
|
|
239
|
+
delete profile.key;
|
|
240
|
+
delete profile.secret;
|
|
241
|
+
}
|
|
242
|
+
const keyMap = {
|
|
243
|
+
tenant: 'Host',
|
|
244
|
+
username: 'Username',
|
|
245
|
+
password: 'Password',
|
|
246
|
+
key: 'Log API Key',
|
|
247
|
+
secret: 'Log API Secret',
|
|
248
|
+
};
|
|
249
|
+
const table = createObjectTable(profile, keyMap);
|
|
250
|
+
printMessage(table.toString(), 'data');
|
|
251
|
+
} else {
|
|
252
|
+
printMessage(`No connection profile ${host} found`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import {
|
|
3
|
+
EMAIL_TEMPLATE_TYPE,
|
|
4
|
+
getEmailTemplate,
|
|
5
|
+
getEmailTemplates,
|
|
6
|
+
putEmailTemplate,
|
|
7
|
+
} from '../api/EmailTemplateApi.js';
|
|
8
|
+
import {
|
|
9
|
+
createProgressBar,
|
|
10
|
+
createTable,
|
|
11
|
+
printMessage,
|
|
12
|
+
stopProgressBar,
|
|
13
|
+
updateProgressBar,
|
|
14
|
+
} from './utils/Console.js';
|
|
15
|
+
import {
|
|
16
|
+
getTypedFilename,
|
|
17
|
+
saveJsonToFile,
|
|
18
|
+
validateImport,
|
|
19
|
+
} from './utils/ExportImportUtils.js';
|
|
20
|
+
import wordwrap from './utils/Wordwrap.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Maintain the file type centrally
|
|
24
|
+
*/
|
|
25
|
+
const EMAIL_TEMPLATE_FILE_TYPE = 'template.email';
|
|
26
|
+
|
|
27
|
+
// use a function vs a template variable to avoid problems in loops
|
|
28
|
+
function getFileDataTemplate() {
|
|
29
|
+
return {
|
|
30
|
+
meta: {},
|
|
31
|
+
emailTemplate: {},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* List email templates
|
|
37
|
+
* @param {boolean} long Long list format with details
|
|
38
|
+
*/
|
|
39
|
+
export async function listEmailTemplates(long = false) {
|
|
40
|
+
let emailTemplates = [];
|
|
41
|
+
try {
|
|
42
|
+
emailTemplates = (await getEmailTemplates()).data.result;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
printMessage(`${error.message}`, 'error');
|
|
45
|
+
printMessage(error.response.data, 'error');
|
|
46
|
+
}
|
|
47
|
+
emailTemplates.sort((a, b) => a._id.localeCompare(b._id));
|
|
48
|
+
if (!long) {
|
|
49
|
+
for (const [, emailTemplate] of emailTemplates.entries()) {
|
|
50
|
+
printMessage(
|
|
51
|
+
`${emailTemplate._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '')}`,
|
|
52
|
+
'data'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
const table = createTable([
|
|
57
|
+
'Id'.brightCyan,
|
|
58
|
+
'Name'.brightCyan,
|
|
59
|
+
'Status'.brightCyan,
|
|
60
|
+
'Locale(s)'.brightCyan,
|
|
61
|
+
'From'.brightCyan,
|
|
62
|
+
'Subject'.brightCyan,
|
|
63
|
+
]);
|
|
64
|
+
emailTemplates.forEach((emailTemplate) => {
|
|
65
|
+
table.push([
|
|
66
|
+
// Id
|
|
67
|
+
`${emailTemplate._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '')}`,
|
|
68
|
+
// Name
|
|
69
|
+
`${emailTemplate.displayName ? emailTemplate.displayName : ''}`,
|
|
70
|
+
// Status
|
|
71
|
+
emailTemplate.enabled === false
|
|
72
|
+
? 'disabled'.brightRed
|
|
73
|
+
: 'enabled'.brightGreen,
|
|
74
|
+
// Locale(s)
|
|
75
|
+
`${emailTemplate.defaultLocale}${
|
|
76
|
+
Object.keys(emailTemplate.subject).length > 1
|
|
77
|
+
? ` (${Object.keys(emailTemplate.subject)
|
|
78
|
+
.filter((locale) => locale !== emailTemplate.defaultLocale)
|
|
79
|
+
.join(',')})`
|
|
80
|
+
: ''
|
|
81
|
+
}`,
|
|
82
|
+
// From
|
|
83
|
+
`${emailTemplate.from ? emailTemplate.from : ''}`,
|
|
84
|
+
// Subject
|
|
85
|
+
wordwrap(emailTemplate.subject[emailTemplate.defaultLocale], 40),
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
printMessage(table.toString(), 'data');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Export a single email template to file
|
|
94
|
+
* @param {String} templateId email template id
|
|
95
|
+
* @param {String} file optional filename
|
|
96
|
+
*/
|
|
97
|
+
export async function exportEmailTemplateToFile(templateId, file) {
|
|
98
|
+
let fileName = file;
|
|
99
|
+
if (!fileName) {
|
|
100
|
+
fileName = getTypedFilename(templateId, EMAIL_TEMPLATE_FILE_TYPE);
|
|
101
|
+
}
|
|
102
|
+
createProgressBar(1, `Exporting ${templateId}`);
|
|
103
|
+
getEmailTemplate(templateId)
|
|
104
|
+
.then(async (response) => {
|
|
105
|
+
const templateData = response.data;
|
|
106
|
+
updateProgressBar(`Writing file ${fileName}`);
|
|
107
|
+
const fileData = getFileDataTemplate();
|
|
108
|
+
fileData.emailTemplate[templateId] = templateData;
|
|
109
|
+
saveJsonToFile(fileData, fileName);
|
|
110
|
+
stopProgressBar(
|
|
111
|
+
`Exported ${templateId.brightCyan} to ${fileName.brightCyan}.`
|
|
112
|
+
);
|
|
113
|
+
})
|
|
114
|
+
.catch((err) => {
|
|
115
|
+
stopProgressBar(`${err}`);
|
|
116
|
+
printMessage(err, 'error');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Export all email templates to file
|
|
122
|
+
* @param {String} file optional filename
|
|
123
|
+
*/
|
|
124
|
+
export async function exportEmailTemplatesToFile(file) {
|
|
125
|
+
let fileName = file;
|
|
126
|
+
if (!fileName) {
|
|
127
|
+
fileName = getTypedFilename(`allEmailTemplates`, EMAIL_TEMPLATE_FILE_TYPE);
|
|
128
|
+
}
|
|
129
|
+
const fileData = getFileDataTemplate();
|
|
130
|
+
getEmailTemplates()
|
|
131
|
+
.then((response) => {
|
|
132
|
+
const templates = response.data.result;
|
|
133
|
+
createProgressBar(response.data.resultCount, 'Exporting email templates');
|
|
134
|
+
for (const template of templates) {
|
|
135
|
+
const templateId = template._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '');
|
|
136
|
+
updateProgressBar(`Exporting ${templateId}`);
|
|
137
|
+
fileData.emailTemplate[templateId] = template;
|
|
138
|
+
}
|
|
139
|
+
saveJsonToFile(fileData, fileName);
|
|
140
|
+
stopProgressBar(
|
|
141
|
+
`${response.data.resultCount} templates exported to ${fileName}.`
|
|
142
|
+
);
|
|
143
|
+
})
|
|
144
|
+
.catch((err) => {
|
|
145
|
+
stopProgressBar(`${err}`);
|
|
146
|
+
printMessage(err, 'error');
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Export all email templates to separate files
|
|
152
|
+
*/
|
|
153
|
+
export async function exportEmailTemplatesToFiles() {
|
|
154
|
+
getEmailTemplates()
|
|
155
|
+
.then((response) => {
|
|
156
|
+
const templates = response.data.result;
|
|
157
|
+
createProgressBar(response.data.resultCount, 'Exporting email templates');
|
|
158
|
+
for (const template of templates) {
|
|
159
|
+
const templateId = template._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '');
|
|
160
|
+
const fileName = getTypedFilename(templateId, EMAIL_TEMPLATE_FILE_TYPE);
|
|
161
|
+
const fileData = getFileDataTemplate();
|
|
162
|
+
updateProgressBar(`Exporting ${templateId}`);
|
|
163
|
+
fileData.emailTemplate[templateId] = template;
|
|
164
|
+
saveJsonToFile(fileData, fileName);
|
|
165
|
+
}
|
|
166
|
+
stopProgressBar(`${response.data.resultCount} templates exported.`);
|
|
167
|
+
})
|
|
168
|
+
.catch((err) => {
|
|
169
|
+
stopProgressBar(`${err}`);
|
|
170
|
+
printMessage(err, 'error');
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Import email template by id from file
|
|
176
|
+
* @param {String} templateId email template id
|
|
177
|
+
* @param {String} file optional filename
|
|
178
|
+
*/
|
|
179
|
+
export async function importEmailTemplateFromFile(templateId, file) {
|
|
180
|
+
// eslint-disable-next-line no-param-reassign
|
|
181
|
+
templateId = templateId.replaceAll(`${EMAIL_TEMPLATE_TYPE}/`, '');
|
|
182
|
+
fs.readFile(file, 'utf8', (err, data) => {
|
|
183
|
+
if (err) throw err;
|
|
184
|
+
const fileData = JSON.parse(data);
|
|
185
|
+
if (validateImport(fileData.meta)) {
|
|
186
|
+
createProgressBar(1, `Importing ${templateId}`);
|
|
187
|
+
if (fileData.emailTemplate[templateId]) {
|
|
188
|
+
putEmailTemplate(templateId, fileData.emailTemplate[templateId])
|
|
189
|
+
.then(() => {
|
|
190
|
+
updateProgressBar(`Importing ${templateId}`);
|
|
191
|
+
stopProgressBar(`Imported ${templateId}`);
|
|
192
|
+
})
|
|
193
|
+
.catch((putEmailTemplateError) => {
|
|
194
|
+
stopProgressBar(`${putEmailTemplateError}`);
|
|
195
|
+
printMessage(putEmailTemplateError, 'error');
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
stopProgressBar(
|
|
199
|
+
`Email template ${templateId.brightCyan} not found in ${file.brightCyan}!`
|
|
200
|
+
);
|
|
201
|
+
printMessage(
|
|
202
|
+
`Email template ${templateId.brightCyan} not found in ${file.brightCyan}!`,
|
|
203
|
+
'error'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
printMessage('Import validation failed...', 'error');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Import all email templates from file
|
|
214
|
+
* @param {String} file optional filename
|
|
215
|
+
*/
|
|
216
|
+
export async function importEmailTemplatesFromFile(file) {
|
|
217
|
+
fs.readFile(file, 'utf8', async (err, data) => {
|
|
218
|
+
if (err) throw err;
|
|
219
|
+
const fileData = JSON.parse(data);
|
|
220
|
+
if (validateImport(fileData.meta)) {
|
|
221
|
+
createProgressBar(
|
|
222
|
+
Object.keys(fileData.emailTemplate).length,
|
|
223
|
+
`Importing email templates`
|
|
224
|
+
);
|
|
225
|
+
for (const id in fileData.emailTemplate) {
|
|
226
|
+
if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {
|
|
227
|
+
const templateId = id.replaceAll(`${EMAIL_TEMPLATE_TYPE}/`, '');
|
|
228
|
+
try {
|
|
229
|
+
// eslint-disable-next-line no-await-in-loop
|
|
230
|
+
await putEmailTemplate(
|
|
231
|
+
templateId,
|
|
232
|
+
fileData.emailTemplate[templateId]
|
|
233
|
+
);
|
|
234
|
+
updateProgressBar(`Imported ${templateId}`);
|
|
235
|
+
} catch (putEmailTemplateError) {
|
|
236
|
+
printMessage(`\nError importing ${templateId}`, 'error');
|
|
237
|
+
printMessage(putEmailTemplateError.response.data, 'error');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
stopProgressBar(`Done.`);
|
|
242
|
+
} else {
|
|
243
|
+
printMessage('Import validation failed...', 'error');
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Import all email templates from separate files
|
|
250
|
+
*/
|
|
251
|
+
export async function importEmailTemplatesFromFiles() {
|
|
252
|
+
const names = fs.readdirSync('.');
|
|
253
|
+
const jsonFiles = names.filter((name) =>
|
|
254
|
+
name.toLowerCase().endsWith(`${EMAIL_TEMPLATE_FILE_TYPE}.json`)
|
|
255
|
+
);
|
|
256
|
+
createProgressBar(jsonFiles.length, 'Importing email templates...');
|
|
257
|
+
let total = 0;
|
|
258
|
+
let totalErrors = 0;
|
|
259
|
+
for (const file of jsonFiles) {
|
|
260
|
+
const data = fs.readFileSync(file, 'utf8');
|
|
261
|
+
const fileData = JSON.parse(data);
|
|
262
|
+
if (validateImport(fileData.meta)) {
|
|
263
|
+
total += Object.keys(fileData.emailTemplate).length;
|
|
264
|
+
let errors = 0;
|
|
265
|
+
for (const id in fileData.emailTemplate) {
|
|
266
|
+
if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {
|
|
267
|
+
const templateId = id.replaceAll(`${EMAIL_TEMPLATE_TYPE}/`, '');
|
|
268
|
+
try {
|
|
269
|
+
// eslint-disable-next-line no-await-in-loop
|
|
270
|
+
await putEmailTemplate(
|
|
271
|
+
templateId,
|
|
272
|
+
fileData.emailTemplate[templateId]
|
|
273
|
+
);
|
|
274
|
+
} catch (putEmailTemplateError) {
|
|
275
|
+
errors += 1;
|
|
276
|
+
printMessage(`\nError importing ${templateId}`, 'error');
|
|
277
|
+
printMessage(putEmailTemplateError.response.data, 'error');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
totalErrors += errors;
|
|
282
|
+
updateProgressBar(`Imported ${file}`);
|
|
283
|
+
} else {
|
|
284
|
+
printMessage(`Validation of ${file} failed!`, 'error');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
stopProgressBar(
|
|
288
|
+
`Imported ${total - totalErrors} of ${total} email template(s) from ${
|
|
289
|
+
jsonFiles.length
|
|
290
|
+
} file(s).`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Import first email template from file
|
|
296
|
+
* @param {String} file optional filename
|
|
297
|
+
*/
|
|
298
|
+
export async function importFirstEmailTemplateFromFile(file) {
|
|
299
|
+
fs.readFile(file, 'utf8', (err, data) => {
|
|
300
|
+
if (err) throw err;
|
|
301
|
+
const fileData = JSON.parse(data);
|
|
302
|
+
if (validateImport(fileData.meta)) {
|
|
303
|
+
createProgressBar(1, `Importing first email template`);
|
|
304
|
+
for (const id in fileData.emailTemplate) {
|
|
305
|
+
if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {
|
|
306
|
+
putEmailTemplate(
|
|
307
|
+
id.replaceAll('emailTemplate/', ''),
|
|
308
|
+
fileData.emailTemplate[id]
|
|
309
|
+
)
|
|
310
|
+
.then(() => {
|
|
311
|
+
updateProgressBar(`Imported ${id}`);
|
|
312
|
+
stopProgressBar(`Imported ${id}`);
|
|
313
|
+
})
|
|
314
|
+
.catch((putEmailTemplateError) => {
|
|
315
|
+
stopProgressBar(`Error importing email template ${id}`);
|
|
316
|
+
printMessage(`\nError importing email template ${id}`, 'error');
|
|
317
|
+
printMessage(putEmailTemplateError.response.data, 'error');
|
|
318
|
+
});
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
printMessage('Import validation failed...', 'error');
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|