auth0-deploy-cli 8.30.0 → 8.32.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/.circleci/config.yml +17 -5
- package/AGENTS.md +21 -1
- package/CHANGELOG.md +48 -415
- package/CLAUDE.md +3 -0
- package/lib/args.d.ts +5 -0
- package/lib/args.js +23 -0
- package/lib/commands/export.js +5 -1
- package/lib/commands/import.js +40 -3
- package/lib/context/directory/handlers/actions.js +1 -2
- package/lib/context/directory/handlers/clients.js +14 -0
- package/lib/context/directory/handlers/connections.js +1 -0
- package/lib/context/directory/handlers/guardianFactors.js +8 -6
- package/lib/context/directory/handlers/tenant.d.ts +1 -6
- package/lib/context/directory/handlers/tenant.js +0 -1
- package/lib/context/directory/handlers/triggers.js +1 -3
- package/lib/context/index.js +5 -0
- package/lib/context/yaml/handlers/clients.js +12 -0
- package/lib/context/yaml/handlers/connections.js +1 -17
- package/lib/context/yaml/handlers/guardianFactors.js +3 -1
- package/lib/context/yaml/handlers/tenant.js +1 -1
- package/lib/context/yaml/index.js +2 -1
- package/lib/sessionDurationsToMinutes.d.ts +9 -7
- package/lib/sessionDurationsToMinutes.js +11 -10
- package/lib/tools/auth0/handlers/actions.d.ts +1 -0
- package/lib/tools/auth0/handlers/actions.js +36 -0
- package/lib/tools/auth0/handlers/attackProtection.js +15 -0
- package/lib/tools/auth0/handlers/branding.js +7 -0
- package/lib/tools/auth0/handlers/clientGrants.js +32 -4
- package/lib/tools/auth0/handlers/clients.d.ts +52 -2
- package/lib/tools/auth0/handlers/clients.js +175 -22
- package/lib/tools/auth0/handlers/connections.d.ts +1 -1
- package/lib/tools/auth0/handlers/connections.js +42 -6
- package/lib/tools/auth0/handlers/customDomains.d.ts +5 -0
- package/lib/tools/auth0/handlers/customDomains.js +26 -0
- package/lib/tools/auth0/handlers/databases.d.ts +91 -0
- package/lib/tools/auth0/handlers/databases.js +162 -2
- package/lib/tools/auth0/handlers/default.d.ts +4 -0
- package/lib/tools/auth0/handlers/default.js +82 -4
- package/lib/tools/auth0/handlers/emailProvider.js +8 -0
- package/lib/tools/auth0/handlers/emailTemplates.js +7 -0
- package/lib/tools/auth0/handlers/flowVaultConnections.js +6 -1
- package/lib/tools/auth0/handlers/flows.js +13 -4
- package/lib/tools/auth0/handlers/forms.js +10 -1
- package/lib/tools/auth0/handlers/guardianFactors.js +7 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorMessageTypes.js +8 -1
- package/lib/tools/auth0/handlers/guardianPhoneFactorSelectedProvider.js +8 -1
- package/lib/tools/auth0/handlers/guardianPolicies.js +7 -0
- package/lib/tools/auth0/handlers/hooks.d.ts +1 -0
- package/lib/tools/auth0/handlers/hooks.js +10 -0
- package/lib/tools/auth0/handlers/organizations.d.ts +11 -1
- package/lib/tools/auth0/handlers/organizations.js +48 -52
- package/lib/tools/auth0/handlers/pages.js +7 -0
- package/lib/tools/auth0/handlers/phoneProvider.js +7 -0
- package/lib/tools/auth0/handlers/prompts.js +7 -0
- package/lib/tools/auth0/handlers/resourceServers.d.ts +1 -2
- package/lib/tools/auth0/handlers/resourceServers.js +9 -25
- package/lib/tools/auth0/handlers/roles.js +7 -0
- package/lib/tools/auth0/handlers/rules.d.ts +1 -0
- package/lib/tools/auth0/handlers/rules.js +9 -0
- package/lib/tools/auth0/handlers/rulesConfigs.d.ts +1 -0
- package/lib/tools/auth0/handlers/rulesConfigs.js +3 -0
- package/lib/tools/auth0/handlers/scimHandler.js +8 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.d.ts +1 -1
- package/lib/tools/auth0/handlers/selfServiceProfiles.js +10 -17
- package/lib/tools/auth0/handlers/tenant.d.ts +13 -1
- package/lib/tools/auth0/handlers/tenant.js +48 -4
- package/lib/tools/auth0/handlers/themes.js +11 -2
- package/lib/tools/auth0/handlers/triggers.js +6 -0
- package/lib/tools/auth0/handlers/userAttributeProfiles.js +16 -10
- package/lib/tools/auth0/index.d.ts +3 -0
- package/lib/tools/auth0/index.js +204 -0
- package/lib/tools/calculateChanges.d.ts +2 -2
- package/lib/tools/calculateChanges.js +21 -6
- package/lib/tools/calculateDryRunChanges.d.ts +71 -0
- package/lib/tools/calculateDryRunChanges.js +492 -0
- package/lib/tools/deploy.js +25 -1
- package/lib/tools/utils.d.ts +5 -0
- package/lib/tools/utils.js +64 -3
- package/lib/types.d.ts +17 -0
- package/lib/utils.d.ts +7 -0
- package/lib/utils.js +46 -2
- package/package.json +10 -6
|
@@ -45,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
45
45
|
exports.schema = void 0;
|
|
46
46
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
47
47
|
const default_1 = __importStar(require("./default"));
|
|
48
|
+
const utils_1 = require("../../utils");
|
|
48
49
|
/**
|
|
49
50
|
* Schema
|
|
50
51
|
*/
|
|
@@ -480,11 +481,19 @@ class ThemesHandler extends default_1.default {
|
|
|
480
481
|
if (!themes) {
|
|
481
482
|
return;
|
|
482
483
|
}
|
|
484
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
485
|
+
const { del, update } = await this.calcChanges(assets);
|
|
486
|
+
if (update.length === 0 && del.length === 0) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
483
490
|
// Empty array means themes should be deleted
|
|
484
491
|
if (themes.length === 0) {
|
|
485
|
-
|
|
492
|
+
await this.deleteThemes();
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
await this.updateThemes(themes);
|
|
486
496
|
}
|
|
487
|
-
return this.updateThemes(themes);
|
|
488
497
|
}
|
|
489
498
|
async deleteThemes() {
|
|
490
499
|
if (!this.config('AUTH0_ALLOW_DELETE')) {
|
|
@@ -122,6 +122,12 @@ class TriggersHandler extends default_1.default {
|
|
|
122
122
|
// Do nothing if not set
|
|
123
123
|
if (!triggers)
|
|
124
124
|
return;
|
|
125
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
126
|
+
const { update } = await this.calcChanges(assets);
|
|
127
|
+
if (update.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
125
131
|
await (0, utils_1.sleep)(2000); // Delay to allow newly-deployed actions to register in backend
|
|
126
132
|
await Promise.all(Object.entries(triggers).map(async ([name, data]) => {
|
|
127
133
|
const bindings = data.map((binding) => ({
|
|
@@ -46,7 +46,7 @@ exports.getUserAttributeProfiles = exports.schema = void 0;
|
|
|
46
46
|
const default_1 = __importStar(require("./default"));
|
|
47
47
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
48
48
|
const client_1 = require("../client");
|
|
49
|
-
const
|
|
49
|
+
const utils_1 = require("../../utils");
|
|
50
50
|
const strategies = ['pingfederate', 'ad', 'adfs', 'waad', 'google-apps', 'okta', 'oidc', 'samlp'];
|
|
51
51
|
const strategyOverrides = {
|
|
52
52
|
type: 'object',
|
|
@@ -147,7 +147,6 @@ exports.schema = {
|
|
|
147
147
|
},
|
|
148
148
|
scim_mapping: {
|
|
149
149
|
type: 'string',
|
|
150
|
-
default: 'externalId',
|
|
151
150
|
},
|
|
152
151
|
},
|
|
153
152
|
},
|
|
@@ -269,14 +268,21 @@ class UserAttributeProfilesHandler extends default_1.default {
|
|
|
269
268
|
// Do nothing if not set
|
|
270
269
|
if (!userAttributeProfiles)
|
|
271
270
|
return;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
271
|
+
const { del, update, create, conflicts } = await this.calcChanges(assets);
|
|
272
|
+
if ((0, utils_1.isDryRun)(this.config)) {
|
|
273
|
+
if (create.length === 0 &&
|
|
274
|
+
update.length === 0 &&
|
|
275
|
+
del.length === 0 &&
|
|
276
|
+
conflicts.length === 0) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const changes = {
|
|
281
|
+
del,
|
|
282
|
+
update,
|
|
283
|
+
create,
|
|
284
|
+
conflicts,
|
|
285
|
+
};
|
|
280
286
|
logger_1.default.debug(`Start processChanges for userAttributeProfile [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
|
|
281
287
|
await super.processChanges(assets, changes);
|
|
282
288
|
}
|
package/lib/tools/auth0/index.js
CHANGED
|
@@ -4,9 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const ajv_1 = __importDefault(require("ajv/lib/ajv"));
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompts_1 = require("@clack/prompts");
|
|
10
|
+
const promise_pool_executor_1 = require("promise-pool-executor");
|
|
7
11
|
const client_1 = __importDefault(require("./client"));
|
|
8
12
|
const schema_1 = __importDefault(require("./schema"));
|
|
9
13
|
const handlers_1 = __importDefault(require("./handlers"));
|
|
14
|
+
const calculateDryRunChanges_1 = require("../calculateDryRunChanges");
|
|
15
|
+
const utils_1 = require("../../utils");
|
|
16
|
+
const utils_2 = require("../utils");
|
|
10
17
|
/**
|
|
11
18
|
* Sorts handlers by their @order decorator metadata for a given stage.
|
|
12
19
|
* Handlers are sorted in ascending order (lower values execute first).
|
|
@@ -29,6 +36,30 @@ function sortByOrder(toSort, stage) {
|
|
|
29
36
|
});
|
|
30
37
|
return sorted;
|
|
31
38
|
}
|
|
39
|
+
function buildTableBorder(left, middle, right, widths) {
|
|
40
|
+
return `${left}${widths.map((width) => '─'.repeat(width + 2)).join(middle)}${right}`;
|
|
41
|
+
}
|
|
42
|
+
function formatStatusCell(status, width) {
|
|
43
|
+
let statusColor;
|
|
44
|
+
const baseStatus = status.replace(' *', '');
|
|
45
|
+
switch (baseStatus) {
|
|
46
|
+
case 'CREATE':
|
|
47
|
+
statusColor = chalk_1.default.green;
|
|
48
|
+
break;
|
|
49
|
+
case 'UPDATE':
|
|
50
|
+
statusColor = chalk_1.default.yellow;
|
|
51
|
+
break;
|
|
52
|
+
case 'DELETE':
|
|
53
|
+
statusColor = chalk_1.default.red;
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
statusColor = chalk_1.default.white;
|
|
57
|
+
}
|
|
58
|
+
if (status.includes(' *')) {
|
|
59
|
+
return `${statusColor(baseStatus.padEnd(width - 2))}${chalk_1.default.dim(' *')}`;
|
|
60
|
+
}
|
|
61
|
+
return statusColor(baseStatus.padEnd(width));
|
|
62
|
+
}
|
|
32
63
|
class Auth0 {
|
|
33
64
|
constructor(client, assets, config) {
|
|
34
65
|
this.client = (0, client_1.default)(client);
|
|
@@ -91,5 +122,178 @@ class Auth0 {
|
|
|
91
122
|
async processChanges() {
|
|
92
123
|
await this.runStage('processChanges');
|
|
93
124
|
}
|
|
125
|
+
async dryRun(opts = {}) {
|
|
126
|
+
const isDebug = process.env.AUTH0_DEBUG === 'true';
|
|
127
|
+
const s = (0, prompts_1.spinner)();
|
|
128
|
+
if (isDebug) {
|
|
129
|
+
prompts_1.log.info('Preparing dry run preview...\n');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
s.start('Preparing dry run preview...');
|
|
133
|
+
}
|
|
134
|
+
// Collect changes from all handlers
|
|
135
|
+
const allChanges = {};
|
|
136
|
+
const savedAssets = this.assets;
|
|
137
|
+
this.assets = await (0, calculateDryRunChanges_1.dryRunFormatAssets)((0, lodash_1.cloneDeep)(this.assets), this.client);
|
|
138
|
+
const dryRunPool = new promise_pool_executor_1.PromisePoolExecutor({
|
|
139
|
+
concurrencyLimit: 2,
|
|
140
|
+
frequencyLimit: 8,
|
|
141
|
+
frequencyWindow: 1000, // 1 sec
|
|
142
|
+
});
|
|
143
|
+
await dryRunPool
|
|
144
|
+
.addEachTask({
|
|
145
|
+
data: this.handlers,
|
|
146
|
+
generator: (handler) => (async () => {
|
|
147
|
+
try {
|
|
148
|
+
const detailedChanges = [];
|
|
149
|
+
let created = 0;
|
|
150
|
+
let updated = 0;
|
|
151
|
+
let deleted = 0;
|
|
152
|
+
if (isDebug) {
|
|
153
|
+
prompts_1.log.info(`Calculating dry run changes for ${handler.type}...`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
s.message(`Calculating dry run changes for ${handler.type}...`);
|
|
157
|
+
}
|
|
158
|
+
const changes = await handler.dryRunChanges(this.assets);
|
|
159
|
+
if (changes.create) {
|
|
160
|
+
changes.create.forEach((item) => {
|
|
161
|
+
detailedChanges.push({
|
|
162
|
+
action: 'CREATE',
|
|
163
|
+
identifier: handler.getResourceName(item),
|
|
164
|
+
details: item,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
created = changes.create.length;
|
|
168
|
+
}
|
|
169
|
+
if (changes.update) {
|
|
170
|
+
changes.update.forEach((item) => {
|
|
171
|
+
detailedChanges.push({
|
|
172
|
+
action: 'UPDATE',
|
|
173
|
+
identifier: handler.getResourceName(item),
|
|
174
|
+
details: item,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
updated = changes.update.length;
|
|
178
|
+
}
|
|
179
|
+
if (changes.del) {
|
|
180
|
+
changes.del.forEach((item) => {
|
|
181
|
+
detailedChanges.push({
|
|
182
|
+
action: 'DELETE',
|
|
183
|
+
identifier: handler.getResourceName(item),
|
|
184
|
+
details: item,
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
deleted = changes.del.length;
|
|
188
|
+
}
|
|
189
|
+
allChanges[handler.type] = {
|
|
190
|
+
created,
|
|
191
|
+
updated,
|
|
192
|
+
deleted,
|
|
193
|
+
changes: detailedChanges,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
err.type = handler.type;
|
|
198
|
+
err.stage = 'dryRun';
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
})(),
|
|
202
|
+
})
|
|
203
|
+
.promise();
|
|
204
|
+
// Restore original assets so processChanges() works correctly after dryRun()
|
|
205
|
+
this.assets = savedAssets;
|
|
206
|
+
if (!isDebug) {
|
|
207
|
+
(0, utils_2.printCLIMessage)('');
|
|
208
|
+
s.stop('Done\n');
|
|
209
|
+
}
|
|
210
|
+
// Build formatted table output
|
|
211
|
+
const allowDelete = this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true;
|
|
212
|
+
const tenantDomain = this.config('AUTH0_DOMAIN');
|
|
213
|
+
const inputPath = this.config('AUTH0_INPUT_FILE') || './tenant-config-directory/';
|
|
214
|
+
let output = '\n';
|
|
215
|
+
output += chalk_1.default.bold('Auth0 Deploy CLI - Dry Run Preview\n\n');
|
|
216
|
+
output += chalk_1.default.bold('Tenant:') + ` ${tenantDomain}\n`;
|
|
217
|
+
output += chalk_1.default.bold('Input:') + ` ${inputPath}\n\n`;
|
|
218
|
+
output += 'Simulating deployment... The following changes are proposed:\n\n';
|
|
219
|
+
const tableData = [];
|
|
220
|
+
const resourceTypes = Object.keys(allChanges).sort();
|
|
221
|
+
resourceTypes.forEach((type) => {
|
|
222
|
+
const typeChanges = allChanges[type];
|
|
223
|
+
if (typeChanges.changes.length > 0) {
|
|
224
|
+
const typeName = type.charAt(0).toUpperCase() + type.slice(1);
|
|
225
|
+
typeChanges.changes.forEach((change, index) => {
|
|
226
|
+
const deleteNote = change.action === 'DELETE' && !allowDelete ? ' *' : '';
|
|
227
|
+
tableData.push({
|
|
228
|
+
resource: index === 0 ? typeName : '',
|
|
229
|
+
status: change.action + deleteNote,
|
|
230
|
+
name: change.identifier,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
if (tableData.length > 0) {
|
|
236
|
+
const resourceWidth = Math.max(10, ...tableData.map((d) => d.resource.length));
|
|
237
|
+
const statusWidth = Math.max(8, ...tableData.map((d) => d.status.length));
|
|
238
|
+
const nameWidth = Math.max(25, ...tableData.map((d) => d.name.length));
|
|
239
|
+
const widths = [resourceWidth, statusWidth, nameWidth];
|
|
240
|
+
const topBorder = buildTableBorder('┌', '┬', '┐', widths);
|
|
241
|
+
const headerSeparator = buildTableBorder('├', '┼', '┤', widths);
|
|
242
|
+
const bottomBorder = buildTableBorder('└', '┴', '┘', widths);
|
|
243
|
+
output += `${topBorder}\n`;
|
|
244
|
+
output += `│ ${chalk_1.default.bold('Resource'.padEnd(resourceWidth))} │ ${chalk_1.default.bold('Status'.padEnd(statusWidth))} │ ${chalk_1.default.bold('Name / Identifier'.padEnd(nameWidth))} │\n`;
|
|
245
|
+
output += `${headerSeparator}\n`;
|
|
246
|
+
tableData.forEach((row) => {
|
|
247
|
+
output += `│ ${row.resource.padEnd(resourceWidth)} │ ${formatStatusCell(row.status, statusWidth)} │ ${row.name.padEnd(nameWidth)} │\n`;
|
|
248
|
+
});
|
|
249
|
+
output += `${bottomBorder}\n`;
|
|
250
|
+
if (!allowDelete && tableData.some((d) => d.status.includes('*'))) {
|
|
251
|
+
output += '\n' + chalk_1.default.dim('* Requires AUTH0_ALLOW_DELETE to be enabled');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
output += chalk_1.default.dim('No changes detected.');
|
|
256
|
+
}
|
|
257
|
+
output += '\n\n';
|
|
258
|
+
output += chalk_1.default.green('Dry Run completed successfully.') + '\n';
|
|
259
|
+
(0, utils_2.printCLIMessage)(output);
|
|
260
|
+
const hasChanges = tableData.length > 0;
|
|
261
|
+
const shouldApplyAfterPreview = (0, utils_1.isTruthy)(this.config('AUTH0_DRY_RUN_APPLY'));
|
|
262
|
+
if (opts.interactive && process.stdout.isTTY && !shouldApplyAfterPreview && hasChanges) {
|
|
263
|
+
(0, prompts_1.intro)(chalk_1.default.inverse(' dry-run '));
|
|
264
|
+
const selectedType = await (0, prompts_1.select)({
|
|
265
|
+
message: 'What would you like to do?',
|
|
266
|
+
options: [
|
|
267
|
+
{ value: 'dry-run-apply', label: 'Apply changes' },
|
|
268
|
+
{
|
|
269
|
+
value: 'dry-run-export',
|
|
270
|
+
label: 'Export changes in a file',
|
|
271
|
+
hint: 'No Apply',
|
|
272
|
+
},
|
|
273
|
+
{ value: 'dry-run-exit', label: 'Exit', hint: '' },
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
switch (selectedType) {
|
|
277
|
+
case 'dry-run-apply':
|
|
278
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.green('Applying changes...') + '\n');
|
|
279
|
+
await this.processChanges();
|
|
280
|
+
break;
|
|
281
|
+
case 'dry-run-export': {
|
|
282
|
+
const fileName = 'dry-run-diff-log.json';
|
|
283
|
+
await (0, calculateDryRunChanges_1.exportDiffLog)(fileName);
|
|
284
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.cyan(`Exported on ./${fileName}`) + '\n');
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case 'dry-run-exit':
|
|
288
|
+
(0, utils_2.printCLIMessage)('\n' + chalk_1.default.yellow('Deployment cancelled. No changes were made.') + '\n');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
break;
|
|
291
|
+
default:
|
|
292
|
+
(0, utils_2.printCLIMessage)(chalk_1.default.red('Invalid option selected.'));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return hasChanges;
|
|
297
|
+
}
|
|
94
298
|
}
|
|
95
299
|
exports.default = Auth0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import APIHandler from '
|
|
1
|
+
import APIHandler from './auth0/handlers/default';
|
|
2
2
|
import { Asset, CalculatedChanges } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* @template T
|
|
@@ -20,7 +20,7 @@ export declare function processChangedObjectFields({ handler, desiredAssetState,
|
|
|
20
20
|
export declare function calculateChanges({ handler, assets, existing, identifiers, allowDelete, }: {
|
|
21
21
|
handler: APIHandler;
|
|
22
22
|
assets: Asset[];
|
|
23
|
-
existing: Asset[] | null;
|
|
23
|
+
existing: Asset[] | Asset | null;
|
|
24
24
|
identifiers: string[];
|
|
25
25
|
allowDelete: boolean;
|
|
26
26
|
}): CalculatedChanges;
|
|
@@ -67,8 +67,14 @@ function processChangedObjectFields({ handler, desiredAssetState, currentAssetSt
|
|
|
67
67
|
function calculateChanges({ handler, assets, existing, identifiers = ['id', 'name'], allowDelete, }) {
|
|
68
68
|
// Calculate the changes required between two sets of assets.
|
|
69
69
|
const update = [];
|
|
70
|
-
let del = [
|
|
71
|
-
|
|
70
|
+
let del = [];
|
|
71
|
+
if (existing) {
|
|
72
|
+
del = Array.isArray(existing) ? [...existing] : [existing];
|
|
73
|
+
}
|
|
74
|
+
let create = [];
|
|
75
|
+
if (assets) {
|
|
76
|
+
create = Array.isArray(assets) ? [...assets] : [assets];
|
|
77
|
+
}
|
|
72
78
|
const conflicts = [];
|
|
73
79
|
const findByKeyValue = (key, value, arr) => arr.find((e) => {
|
|
74
80
|
if (Array.isArray(key)) {
|
|
@@ -124,10 +130,9 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
|
|
|
124
130
|
// Loop through identifiers (in order) to try match assets to existing
|
|
125
131
|
// If existing then update if not create
|
|
126
132
|
// The remainder will be deleted
|
|
127
|
-
|
|
128
|
-
// eslint-disable-line
|
|
133
|
+
identifiers.forEach((id) => {
|
|
129
134
|
processAssets(id, [...create]);
|
|
130
|
-
}
|
|
135
|
+
});
|
|
131
136
|
// Check if there are assets with names that will conflict with existing names during the update process
|
|
132
137
|
// This will rename those assets to a temp random name first
|
|
133
138
|
// This assumes the first identifiers is the unique identifier
|
|
@@ -138,7 +143,17 @@ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'nam
|
|
|
138
143
|
// If the conflicting item is going to be deleted then skip
|
|
139
144
|
const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
|
|
140
145
|
if (!inDeleted) {
|
|
141
|
-
|
|
146
|
+
let existingArray;
|
|
147
|
+
if (Array.isArray(existing)) {
|
|
148
|
+
existingArray = existing;
|
|
149
|
+
}
|
|
150
|
+
else if (existing) {
|
|
151
|
+
existingArray = [existing];
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
existingArray = [];
|
|
155
|
+
}
|
|
156
|
+
const conflict = existingArray.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
|
|
142
157
|
if (conflict) {
|
|
143
158
|
const temp = Math.random().toString(36).substr(2, 5);
|
|
144
159
|
conflicts.push({
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dry-run change calculation utilities.
|
|
3
|
+
*
|
|
4
|
+
* This module handles all logic specific to the --dry-run mode:
|
|
5
|
+
* - Comparing local vs. remote assets to detect creates, updates, and deletes
|
|
6
|
+
* - Normalizing asset representations (session durations, client name→ID, profiles)
|
|
7
|
+
* - Tracking and exporting a structured diff log
|
|
8
|
+
*
|
|
9
|
+
* The core deploy-time change calculation lives in calculateChanges.ts.
|
|
10
|
+
*/
|
|
11
|
+
import { Asset, Assets, Auth0APIClient, CalculatedChanges } from '../types';
|
|
12
|
+
export declare const getDiffLog: (resourceTypeName?: string) => string[] | {
|
|
13
|
+
[key: string]: string[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Writes the accumulated diff log to a JSON file on disk.
|
|
17
|
+
* Useful for CI pipelines that want a machine-readable dry-run report.
|
|
18
|
+
*/
|
|
19
|
+
export declare const exportDiffLog: (fileName: string, resourceTypeName?: string) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Compares two objects and returns an array of human-readable difference strings.
|
|
22
|
+
* Only considers keys present in `localObj` — extra keys in `remoteObj` are ignored.
|
|
23
|
+
*
|
|
24
|
+
* @param localObj - Local (desired) state
|
|
25
|
+
* @param remoteObj - Remote (current) state
|
|
26
|
+
* @param keyObjPath - Dot-separated JSON path used for nested tracking
|
|
27
|
+
* @param resourceTypeName - Resource type label used in log output
|
|
28
|
+
* @param ignoreDryRunFields - Field paths to skip during comparison
|
|
29
|
+
* @returns Array of difference descriptions, empty if objects are equivalent
|
|
30
|
+
*/
|
|
31
|
+
export declare function getObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if `localObj` and `remoteObj` differ in any field present in
|
|
34
|
+
* `localObj`. Side-effect: appends found differences to the diff log.
|
|
35
|
+
*/
|
|
36
|
+
export declare function hasObjectDifferences(localObj: Record<string, any>, remoteObj: Record<string, any>, keyObjPath?: string, resourceTypeName?: string, ignoreDryRunFields?: string[]): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Calculates the changes required between local and remote asset sets for dry-run operations.
|
|
39
|
+
*
|
|
40
|
+
* Unlike `calculateChanges`, this function does not mutate assets for API consumption.
|
|
41
|
+
* It is purely for reporting: determining which assets would be created, updated, or deleted.
|
|
42
|
+
*
|
|
43
|
+
* @param params.type - Resource type label used for logging
|
|
44
|
+
* @param params.assets - Local assets to be deployed
|
|
45
|
+
* @param params.existing - Remote assets currently on the tenant, or null
|
|
46
|
+
* @param params.identifiers - Fields used to match local↔remote assets (default: ['id', 'name'])
|
|
47
|
+
* @param params.ignoreDryRunFields - Fields to exclude from diff comparisons
|
|
48
|
+
*/
|
|
49
|
+
export declare function calculateDryRunChanges({ type, assets, existing, identifiers, ignoreDryRunFields, }: {
|
|
50
|
+
type: string;
|
|
51
|
+
assets: Asset[] | Asset;
|
|
52
|
+
existing: Asset[] | Asset | null;
|
|
53
|
+
identifiers: string[];
|
|
54
|
+
ignoreDryRunFields: string[];
|
|
55
|
+
}): CalculatedChanges;
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes local assets so they can be accurately compared against remote assets.
|
|
58
|
+
*
|
|
59
|
+
* Transformations applied:
|
|
60
|
+
* - Converts client name references to client IDs (clientGrants, databases, connections, resourceServers)
|
|
61
|
+
* - Resolves connection profile and user attribute profile names to IDs (express_configuration)
|
|
62
|
+
* - Decodes base64-encoded SAML certificates to PEM strings
|
|
63
|
+
* - Resolves organization connection names to connection IDs
|
|
64
|
+
* - Normalizes tenant session duration fields
|
|
65
|
+
* - Removes empty branding templates (avoids false diff against remote)
|
|
66
|
+
*
|
|
67
|
+
* @param localAssets - Local assets object (will be mutated)
|
|
68
|
+
* @param authAPIclient - Auth0 management API client for fetching remote reference data
|
|
69
|
+
* @returns The mutated assets object with normalized values
|
|
70
|
+
*/
|
|
71
|
+
export declare function dryRunFormatAssets(localAssets: Assets, authAPIclient: Auth0APIClient): Promise<Assets>;
|