Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DXCDT-370: Check prerequisites for keyword preservation #754

Merged
merged 23 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2eb7e09
Renaming load to loadAssetsFromLocal
willvedd Feb 21, 2023
74d55d1
Renaming load to loadAssetsFromAuth0
willvedd Feb 21, 2023
93aae5a
Merge branch 'master' into DXCDT-375-rename-load-to-loadAssetsFromLocal
willvedd Feb 21, 2023
95b4bed
Merge branch 'master' into DXCDT-375-rename-load-to-loadAssetsFromLocal
willvedd Feb 21, 2023
3c82769
Merge branch 'DXCDT-375-rename-load-to-loadAssetsFromLocal' into DXCD…
willvedd Feb 21, 2023
90f8758
integrating into export process
willvedd Feb 22, 2023
4bea2cd
Adding config interpreter
willvedd Feb 22, 2023
8fc8498
Disabling keyword replacement in certain cases
willvedd Feb 22, 2023
e8c7b98
Resolving conflicts with master
willvedd Feb 22, 2023
df66ab0
Fixing directory load
willvedd Feb 22, 2023
4db97a1
More forgiving lookup logic if properties and/or addresses do not exist
willvedd Feb 22, 2023
f1f6a40
Fixing directory load again
willvedd Feb 22, 2023
6149064
Merge branch 'DXCDT-383-integrate-preserve-keywords-function-into-exp…
willvedd Feb 22, 2023
f5c7596
Adding case that would have previously tripped up process
willvedd Feb 22, 2023
a6153f0
Adding e2e tests for keyword preservation
willvedd Feb 22, 2023
ce3eba0
Removing old tests, updating recordings, removing console. logs
willvedd Feb 22, 2023
39ab7a7
Commenting-out test to fix later on
willvedd Feb 22, 2023
1b9a065
Fixing workdirectory for e2e tests
willvedd Feb 22, 2023
2212901
Adding eventual cases for auxillary files
willvedd Feb 22, 2023
32a7da8
Adding TODO
willvedd Feb 22, 2023
7387b88
Adding preqrequisite checks
willvedd Feb 23, 2023
a88ca39
Merging with master
willvedd Feb 23, 2023
8e7e7cd
Fixing test
willvedd Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default async function exportCMD(params: ExportParams) {
nconf.overrides(overrides);

// Setup context and load
const context = await setupContext(nconf.get());
const context = await setupContext(nconf.get(), 'export');
await context.dump();
log.info('Export Successful');
}
2 changes: 1 addition & 1 deletion src/commands/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default async function importCMD(params: ImportParams) {
nconf.overrides(overrides);

// Setup context and load
const context = await setupContext(nconf.get());
const context = await setupContext(nconf.get(), 'import');
await context.loadAssetsFromLocal();

const config = configFactory();
Expand Down
42 changes: 41 additions & 1 deletion src/context/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { lstatSync, readdirSync, readFileSync, existsSync } from 'fs';
import path from 'path';
import { AuthenticationClient, ManagementClient } from 'auth0';
import YAMLContext from './yaml';
Expand Down Expand Up @@ -25,7 +26,10 @@ const nonPrimitiveProps: (keyof Config)[] = [
'INCLUDED_PROPS',
];

export const setupContext = async (config: Config): Promise<DirectoryContext | YAMLContext> => {
export const setupContext = async (
config: Config,
command: 'import' | 'export'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was necessary for me to introduce this command argument to exclude the prerequisite checks if an import was being run.

): Promise<DirectoryContext | YAMLContext> => {
const missingParams: ('AUTH0_DOMAIN' | 'AUTH0_CLIENT_ID' | 'AUTH0_CLIENT_SECRET')[] = [];

if (!config.AUTH0_DOMAIN) missingParams.push('AUTH0_DOMAIN');
Expand Down Expand Up @@ -62,6 +66,42 @@ export const setupContext = async (config: Config): Promise<DirectoryContext | Y
}
})(config);

((config: Config) => {
if (command === 'import') return;

const shouldPreserveKeywords =
//@ts-ignore because the string=>boolean conversion may not have happened if passed-in as env var
config.AUTH0_PRESERVE_KEYWORDS === 'true' || config.AUTH0_PRESERVE_KEYWORDS === true;

if (!shouldPreserveKeywords) return;

const isKeywordMappingsEmpty =
config.AUTH0_KEYWORD_REPLACE_MAPPINGS === undefined ||
Object.keys(config.AUTH0_KEYWORD_REPLACE_MAPPINGS).length === 0;

if (isKeywordMappingsEmpty) {
throw new Error(
'Attempting to preserve keywords without defining keyword mappings. Doing so could result in unintentional overwriting of resource configurations. Either define keyword mappings via AUTH0_KEYWORD_REPLACE_MAPPINGS or disable AUTH0_PRESERVE_KEYWORDS.'
);
}

const doLocalFilesExist = (() => {
if (!existsSync(config.AUTH0_INPUT_FILE)) return false;

const isDirectory = lstatSync(config.AUTH0_INPUT_FILE).isDirectory();
if (isDirectory) {
return readdirSync(config.AUTH0_INPUT_FILE).length > 0;
}
return existsSync(config.AUTH0_INPUT_FILE);
})();

if (!doLocalFilesExist) {
throw new Error(
'Attempting to preserve keywords for local resource configuration files that do not exist. Ensure that there are resource files in the output directory or disable AUTH0_PRESERVE_KEYWORDS.'
);
}
})(config);

((config: Config) => {
// Detect and warn on usage of deprecated exclusion params. See: https://github.com/auth0/auth0-deploy-cli/issues/451#user-content-deprecated-exclusion-props
const deprecatedExclusionParams: (keyof Config)[] = [
Expand Down
2 changes: 2 additions & 0 deletions src/keywordPreservation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ export const preserveKeywords = (
remoteAssets: object,
keywordMappings: KeywordMappings
): object => {
if (Object.keys(keywordMappings).length === 0) return remoteAssets;

const addresses = getPreservableFieldsFromAssets(localAssets, keywordMappings, '');

let updatedRemoteAssets = cloneDeep(remoteAssets);
Expand Down
66 changes: 39 additions & 27 deletions test/context/context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ const config = {

describe('#context loader validation', async () => {
it('should error on bad file', async () => {
await expect(setupContext(config)).to.be.eventually.rejectedWith(Error);
await expect(setupContext(config), 'import').to.be.eventually.rejectedWith(Error);
});

it('should load directory context', async () => {
/* Create empty directory */
const dir = path.resolve(testDataDir, 'context');
cleanThenMkdir(dir);
const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: dir });
const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: dir }, 'import');
expect(loaded).to.be.an.instanceof(directoryContext);
});

Expand All @@ -42,10 +42,10 @@ describe('#context loader validation', async () => {
const yml = path.join(dir, 'empty.yml');
fs.writeFileSync(yml, '');

const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: yaml });
const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: yaml }, 'import');
expect(loaded).to.be.an.instanceof(yamlContext);

const loaded2 = await setupContext({ ...config, AUTH0_INPUT_FILE: yml });
const loaded2 = await setupContext({ ...config, AUTH0_INPUT_FILE: yml }, 'import');
expect(loaded2).to.be.an.instanceof(yamlContext);
});

Expand All @@ -57,7 +57,7 @@ describe('#context loader validation', async () => {
const yaml = path.join(dir, 'empty.yaml');
fs.writeFileSync(yaml, '');

const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: yaml });
const loaded = await setupContext({ ...config, AUTH0_INPUT_FILE: yaml }, 'import');
expect(loaded).to.be.an.instanceof(yamlContext);

const userAgent =
Expand All @@ -75,18 +75,21 @@ describe('#context loader validation', async () => {
fs.writeFileSync(yaml, '');

const loggerSpy = sinon.spy(logger, 'warn');
await setupContext({
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_EXCLUDED_CLIENTS: ['connection-1', 'connection-2'],
});
await setupContext(
{
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_EXCLUDED_CLIENTS: ['connection-1', 'connection-2'],
},
'import'
);

expect(loggerSpy).to.have.been.calledWith(
'Usage of the AUTH0_EXCLUDED_CLIENTS exclusion param is deprecated and may be removed from future major versions. See: https://github.com/auth0/auth0-deploy-cli/issues/451#user-content-deprecated-exclusion-props for details.'
);

loggerSpy.resetHistory(); // Reset history for following case
await setupContext({ ...config, AUTH0_INPUT_FILE: yaml });
await setupContext({ ...config, AUTH0_INPUT_FILE: yaml }, 'import');
// eslint-disable-next-line no-unused-expressions
expect(loggerSpy).to.not.have.been.called;
});
Expand All @@ -99,22 +102,28 @@ describe('#context loader validation', async () => {
fs.writeFileSync(yaml, '');

await expect(
setupContext({
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_EXCLUDED: ['actions', 'rules'],
AUTH0_INCLUDED_ONLY: ['tenant'],
})
setupContext(
{
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_EXCLUDED: ['actions', 'rules'],
AUTH0_INCLUDED_ONLY: ['tenant'],
},
'import'
)
).to.be.rejectedWith(
'Both AUTH0_EXCLUDED and AUTH0_INCLUDED_ONLY configuration values are defined'
);

await expect(
setupContext({
...config,
AUTH0_INCLUDED_ONLY: ['tenant'],
AUTH0_INPUT_FILE: yaml,
})
setupContext(
{
...config,
AUTH0_INCLUDED_ONLY: ['tenant'],
AUTH0_INPUT_FILE: yaml,
},
'import'
)
).to.be.not.rejected;
});

Expand All @@ -126,11 +135,14 @@ describe('#context loader validation', async () => {
fs.writeFileSync(yaml, '');

await expect(
setupContext({
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_INCLUDED_ONLY: [],
})
setupContext(
{
...config,
AUTH0_INPUT_FILE: yaml,
AUTH0_INCLUDED_ONLY: [],
},
'import'
)
).to.be.rejectedWith(
'Need to define at least one resource type in AUTH0_INCLUDED_ONLY configuration. See: https://github.com/auth0/auth0-deploy-cli/blob/master/docs/configuring-the-deploy-cli.md#auth0_included_only'
);
Expand Down
50 changes: 50 additions & 0 deletions test/e2e/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,4 +438,54 @@ describe('keyword preservation', () => {

recordingDone();
});

it('should throw if attempting to preserve keywords but no keywords defined', async function () {
const workDirectory = testNameToWorkingDirectory(this.test?.title);
try {
//@ts-ignore
delete config['AUTH0_KEYWORD_REPLACE_MAPPINGS'];
await dump({
output_folder: workDirectory,
format: 'directory',
config,
});
} catch (err) {
expect(err.message).to.contain(
'Attempting to preserve keywords without defining keyword mappings. Doing so could result in unintentional overwriting of resource configurations. Either define keyword mappings via AUTH0_KEYWORD_REPLACE_MAPPINGS or disable AUTH0_PRESERVE_KEYWORDS.'
);
return;
}
throw new Error("The above should've thrown an exception");
});

it('should throw if attempting to preserve keywords without having local configuration files', async function () {
const workDirectory = testNameToWorkingDirectory(this.test?.title);

try {
await dump({
output_folder: workDirectory,
format: 'directory',
config,
});
throw new Error("The above should've thrown an exception");
} catch (err) {
expect(err.message).to.contain(
'Attempting to preserve keywords without defining keyword mappings. Doing so could result in unintentional overwriting of resource configurations. Either define keyword mappings via AUTH0_KEYWORD_REPLACE_MAPPINGS or disable AUTH0_PRESERVE_KEYWORDS.'
);
}

try {
await dump({
output_folder: workDirectory,
format: 'yaml',
config,
});
} catch (err) {
expect(err.message).to.contain(
'Attempting to preserve keywords without defining keyword mappings. Doing so could result in unintentional overwriting of resource configurations. Either define keyword mappings via AUTH0_KEYWORD_REPLACE_MAPPINGS or disable AUTH0_PRESERVE_KEYWORDS.'
);
return;
}
throw new Error("The above should've thrown an exception");
});
});