From 6be5517d65b6f24a090157c4d25c54cc815563d1 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 18 Dec 2024 13:36:14 -0500 Subject: [PATCH] fix context to be able to save to file --- packages/aws-cdk/lib/cli.ts | 1 - packages/aws-cdk/lib/commands/context.ts | 11 +-- .../lib/parse-command-line-arguments.ts | 2 + packages/aws-cdk/lib/settings.ts | 34 +++++-- .../test/commands/context-command.test.ts | 98 +++++++++---------- packages/aws-cdk/test/notices.test.ts | 8 +- packages/aws-cdk/test/settings.test.ts | 8 +- 7 files changed, 89 insertions(+), 73 deletions(-) diff --git a/packages/aws-cdk/lib/cli.ts b/packages/aws-cdk/lib/cli.ts index aa07fcb178415..300f94ef85c9d 100644 --- a/packages/aws-cdk/lib/cli.ts +++ b/packages/aws-cdk/lib/cli.ts @@ -200,7 +200,6 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise { if (options.clear) { options.context.clear(); - await options.projectContext.save(PROJECT_CONTEXT); + await options.context.save(PROJECT_CONTEXT); print('All context values cleared.'); } else if (options.reset) { invalidateContext(options.context, options.reset, options.force ?? false); - await options.projectContext.save(PROJECT_CONTEXT); + await options.context.save(PROJECT_CONTEXT); } else { // List -- support '--json' flag if (options.json) { diff --git a/packages/aws-cdk/lib/parse-command-line-arguments.ts b/packages/aws-cdk/lib/parse-command-line-arguments.ts index c7e4050d9f1e6..d5a9312fe55cc 100644 --- a/packages/aws-cdk/lib/parse-command-line-arguments.ts +++ b/packages/aws-cdk/lib/parse-command-line-arguments.ts @@ -742,6 +742,7 @@ export function parseCommandLineArguments(args: Array): any { desc: 'The context key (or its index) to reset', type: 'string', requiresArg: true, + default: undefined, }) .option('force', { alias: 'f', @@ -752,6 +753,7 @@ export function parseCommandLineArguments(args: Array): any { .option('clear', { desc: 'Clear all context', type: 'boolean', + default: false, }) ) .command(['docs', 'doc'], 'Opens the reference documentation in a browser', (yargs: Argv) => diff --git a/packages/aws-cdk/lib/settings.ts b/packages/aws-cdk/lib/settings.ts index 9d763ee87b6fa..8c96f59570dce 100644 --- a/packages/aws-cdk/lib/settings.ts +++ b/packages/aws-cdk/lib/settings.ts @@ -121,12 +121,12 @@ export class Configuration { } const contextSources = [ - this.commandLineContext, - this.projectConfig.subSettings([CONTEXT_KEY]).makeReadOnly(), - this.projectContext, + { bag: this.commandLineContext }, + { name: PROJECT_CONFIG, bag: this.projectConfig.subSettings([CONTEXT_KEY]).makeReadOnly() }, + { name: PROJECT_CONTEXT, bag: this.projectContext }, ]; if (readUserContext) { - contextSources.push(userConfig.subSettings([CONTEXT_KEY]).makeReadOnly()); + contextSources.push({ name: USER_DEFAULTS, bag: userConfig.subSettings([CONTEXT_KEY]).makeReadOnly() }); } this.context = new Context(...contextSources); @@ -176,9 +176,11 @@ async function loadAndLog(fileName: string): Promise { */ export class Context { private readonly bags: Settings[]; + private readonly fileNames: (string|undefined)[]; - constructor(...bags: Settings[]) { - this.bags = bags.length > 0 ? bags : [new Settings()]; + constructor(...bags: ({name?: string; bag: Settings})[]) { + this.bags = bags.length > 0 ? bags.map(b => b.bag) : [new Settings()]; + this.fileNames = bags.length > 0 ? bags.map(b => b.name) : ['default']; } public get keys(): string[] { @@ -227,6 +229,26 @@ export class Context { this.unset(key); } } + + /** + * Save a specific context file + */ + public async save(fileName: string): Promise { + const index = this.fileNames.indexOf(fileName); + + // File not found, don't do anything in this scenario + if (index === -1) { + return this; + } + + const bag = this.bags[index]; + if (bag.readOnly) { + throw new Error(`Context file ${fileName} is read only!`); + } + + await bag.save(fileName); + return this; + } } /** diff --git a/packages/aws-cdk/test/commands/context-command.test.ts b/packages/aws-cdk/test/commands/context-command.test.ts index 71c24dda4e638..9d45cf03b3101 100644 --- a/packages/aws-cdk/test/commands/context-command.test.ts +++ b/packages/aws-cdk/test/commands/context-command.test.ts @@ -1,5 +1,5 @@ /* eslint-disable import/order */ -import { realHandler } from '../../lib/commands/context'; +import { context } from '../../lib/commands/context'; import { Configuration, Settings, Context } from '../../lib/settings'; describe('context --list', () => { @@ -13,10 +13,9 @@ describe('context --list', () => { }); // WHEN - await realHandler({ - configuration, - args: {}, - } as any); + await context({ + context: configuration.context, + }); }); }); @@ -33,10 +32,10 @@ describe('context --reset', () => { }); // WHEN - await realHandler({ - configuration, - args: { reset: 'foo' }, - } as any); + await context({ + context: configuration.context, + reset: 'foo', + }); // THEN expect(configuration.context.all).toEqual({ @@ -56,10 +55,10 @@ describe('context --reset', () => { }); // WHEN - await realHandler({ - configuration, - args: { reset: '1' }, - } as any); + await context({ + context: configuration.context, + reset: '1', + }); // THEN expect(configuration.context.all).toEqual({ @@ -81,10 +80,10 @@ describe('context --reset', () => { }); // WHEN - await realHandler({ - configuration, - args: { reset: 'match-*' }, - } as any); + await context({ + context: configuration.context, + reset: 'match-*', + }); // THEN expect(configuration.context.all).toEqual({ @@ -104,10 +103,10 @@ describe('context --reset', () => { }); // WHEN - await realHandler({ - configuration, - args: { reset: 'fo*' }, - } as any); + await context({ + context: configuration.context, + reset: 'fo*', + }); // THEN expect(configuration.context.all).toEqual({ @@ -122,14 +121,14 @@ describe('context --reset', () => { 'foo': 'bar', 'match-a': 'baz', }, true); - configuration.context = new Context(readOnlySettings, new Settings()); + configuration.context = new Context({ bag: readOnlySettings }, { bag: new Settings() }); configuration.context.set('match-b', 'quux'); // When - await expect(realHandler({ - configuration, - args: { reset: 'match-*' }, - } as any)); + await expect(context({ + context: configuration.context, + reset: 'match-*', + })); // Then expect(configuration.context.all).toEqual({ @@ -148,10 +147,10 @@ describe('context --reset', () => { }); // THEN - await expect(realHandler({ - configuration, - args: { reset: 'baz' }, - } as any)).rejects.toThrow(/No context value matching key/); + await expect(context({ + context: configuration.context, + reset: 'baz', + })).rejects.toThrow(/No context value matching key/); }); test('Doesn\'t throw when key not found and --force is set', async () => { @@ -164,10 +163,11 @@ describe('context --reset', () => { }); // THEN - await expect(realHandler({ - configuration, - args: { reset: 'baz', force: true }, - } as any)); + await expect(context({ + context: configuration.context, + reset: 'baz', + force: true, + })); }); test('throws when no key of index found', async () => { @@ -180,10 +180,10 @@ describe('context --reset', () => { }); // THEN - await expect(realHandler({ - configuration, - args: { reset: '2' }, - } as any)).rejects.toThrow(/No context key with number/); + await expect(context({ + context: configuration.context, + reset: '2', + })).rejects.toThrow(/No context key with number/); }); test('throws when resetting read-only values', async () => { @@ -192,17 +192,17 @@ describe('context --reset', () => { const readOnlySettings = new Settings({ foo: 'bar', }, true); - configuration.context = new Context(readOnlySettings); + configuration.context = new Context({ bag: readOnlySettings }); expect(configuration.context.all).toEqual({ foo: 'bar', }); // THEN - await expect(realHandler({ - configuration, - args: { reset: 'foo' }, - } as any)).rejects.toThrow(/Cannot reset readonly context value with key/); + await expect(context({ + context: configuration.context, + reset: 'foo', + })).rejects.toThrow(/Cannot reset readonly context value with key/); }); test('throws when no matches could be reset', async () => { @@ -213,7 +213,7 @@ describe('context --reset', () => { 'match-a': 'baz', 'match-b': 'quux', }, true); - configuration.context = new Context(readOnlySettings); + configuration.context = new Context({ bag: readOnlySettings }); expect(configuration.context.all).toEqual({ 'foo': 'bar', @@ -222,11 +222,9 @@ describe('context --reset', () => { }); // THEN - await expect(realHandler({ - configuration, - args: { reset: 'match-*' }, - } as any)).rejects.toThrow(/None of the matched context values could be reset/); + await expect(context({ + context: configuration.context, + reset: 'match-*', + })).rejects.toThrow(/None of the matched context values could be reset/); }); - }); - diff --git a/packages/aws-cdk/test/notices.test.ts b/packages/aws-cdk/test/notices.test.ts index 9c6f1d0786fb0..8a70067573251 100644 --- a/packages/aws-cdk/test/notices.test.ts +++ b/packages/aws-cdk/test/notices.test.ts @@ -608,7 +608,7 @@ describe(Notices, () => { // within the affected version range of both notices jest.spyOn(version, 'versionNumber').mockImplementation(() => '1.126.0'); - const context = new Context(new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] })); + const context = new Context({ bag: new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] }) }); const notices = Notices.create({ context }); await notices.refresh({ @@ -626,7 +626,7 @@ describe(Notices, () => { // within the affected version range of both notices jest.spyOn(version, 'versionNumber').mockImplementation(() => '1.126.0'); - const context = new Context(new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] })); + const context = new Context({ bag: new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] }) }); const notices = Notices.create({ context, includeAcknowledged: true }); await notices.refresh({ @@ -749,7 +749,7 @@ describe(Notices, () => { // within the affected version range of both notices jest.spyOn(version, 'versionNumber').mockImplementation(() => '1.126.0'); - const context = new Context(new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] })); + const context = new Context({ bag: new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] }) }); const notices = Notices.create({ context }); await notices.refresh({ @@ -766,7 +766,7 @@ describe(Notices, () => { // within the affected version range of both notices jest.spyOn(version, 'versionNumber').mockImplementation(() => '1.126.0'); - const context = new Context(new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] })); + const context = new Context({ bag: new Settings({ 'acknowledged-issue-numbers': [MULTIPLE_AFFECTED_VERSIONS_NOTICE.issueNumber] }) }); const notices = Notices.create({ context, includeAcknowledged: true }); await notices.refresh({ dataSource: { fetch: async () => [BASIC_NOTICE, MULTIPLE_AFFECTED_VERSIONS_NOTICE] }, diff --git a/packages/aws-cdk/test/settings.test.ts b/packages/aws-cdk/test/settings.test.ts index fde5c1c74665c..7edc2e9b487a2 100644 --- a/packages/aws-cdk/test/settings.test.ts +++ b/packages/aws-cdk/test/settings.test.ts @@ -6,7 +6,7 @@ test('can delete values from Context object', () => { // GIVEN const settings1 = new Settings({ foo: 'bar' }); const settings2 = new Settings({ boo: 'baz' }); - const context = new Context(settings1, settings2); + const context = new Context({ bag: settings1 }, { bag: settings2 }); // WHEN context.unset('foo'); @@ -21,7 +21,7 @@ test('can set values in Context object', () => { // GIVEN const settings1 = new Settings(); const settings2 = new Settings(); - const context = new Context(settings1, settings2); + const context = new Context({ bag: settings1 }, { bag: settings2 }); // WHEN context.set('foo', 'bar'); @@ -36,7 +36,7 @@ test('can set values in Context object if first is immutable', () => { // GIVEN const settings1 = new Settings(); const settings2 = new Settings(); - const context = new Context(settings1.makeReadOnly(), settings2); + const context = new Context({ bag: settings1.makeReadOnly() }, { bag: settings2 }); // WHEN context.set('foo', 'bar'); @@ -51,7 +51,7 @@ test('can clear all values in all objects', () => { // GIVEN const settings1 = new Settings({ foo: 'bar' }); const settings2 = new Settings({ foo: 'snar', boo: 'gar' }); - const context = new Context(settings1, settings2); + const context = new Context({ bag: settings1 }, { bag: settings2 }); // WHEN context.clear();