Skip to content

Commit

Permalink
fix context to be able to save to file
Browse files Browse the repository at this point in the history
  • Loading branch information
kaizencc committed Dec 18, 2024
1 parent 47fbe22 commit 6be5517
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 73 deletions.
1 change: 0 additions & 1 deletion packages/aws-cdk/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
case 'context':
return context({
context: configuration.context,
projectContext: configuration.projectContext,
clear: configuration.settings.get(['clear']),
json: configuration.settings.get(['json']),
force: configuration.settings.get(['force']),
Expand Down
11 changes: 3 additions & 8 deletions packages/aws-cdk/lib/commands/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as chalk from 'chalk';
import { minimatch } from 'minimatch';
import * as version from '../../lib/version';
import { print, error, warning } from '../logging';
import { Context, PROJECT_CONFIG, PROJECT_CONTEXT, Settings, USER_DEFAULTS } from '../settings';
import { Context, PROJECT_CONFIG, PROJECT_CONTEXT, USER_DEFAULTS } from '../settings';
import { renderTable } from '../util';

/**
Expand All @@ -14,11 +14,6 @@ export interface ContextOptions {
*/
context: Context;

/**
* Context object specific to cdk.context.json
*/
projectContext: Settings;

/**
* The context key (or its index) to reset
*
Expand Down Expand Up @@ -51,11 +46,11 @@ export interface ContextOptions {
export async function context(options: ContextOptions): Promise<number> {
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) {
Expand Down
2 changes: 2 additions & 0 deletions packages/aws-cdk/lib/parse-command-line-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ export function parseCommandLineArguments(args: Array<string>): any {
desc: 'The context key (or its index) to reset',
type: 'string',
requiresArg: true,
default: undefined,
})
.option('force', {
alias: 'f',
Expand All @@ -752,6 +753,7 @@ export function parseCommandLineArguments(args: Array<string>): any {
.option('clear', {
desc: 'Clear all context',
type: 'boolean',
default: false,
})
)
.command(['docs', 'doc'], 'Opens the reference documentation in a browser', (yargs: Argv) =>
Expand Down
34 changes: 28 additions & 6 deletions packages/aws-cdk/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -176,9 +176,11 @@ async function loadAndLog(fileName: string): Promise<Settings> {
*/
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[] {
Expand Down Expand Up @@ -227,6 +229,26 @@ export class Context {
this.unset(key);
}
}

/**
* Save a specific context file
*/
public async save(fileName: string): Promise<this> {
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;
}
}

/**
Expand Down
98 changes: 48 additions & 50 deletions packages/aws-cdk/test/commands/context-command.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -13,10 +13,9 @@ describe('context --list', () => {
});

// WHEN
await realHandler({
configuration,
args: {},
} as any);
await context({
context: configuration.context,
});
});
});

Expand All @@ -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({
Expand All @@ -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({
Expand All @@ -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({
Expand All @@ -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({
Expand All @@ -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({
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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',
Expand All @@ -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/);
});

});

8 changes: 4 additions & 4 deletions packages/aws-cdk/test/notices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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({
Expand Down Expand Up @@ -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({
Expand All @@ -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] },
Expand Down
8 changes: 4 additions & 4 deletions packages/aws-cdk/test/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -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();
Expand Down

0 comments on commit 6be5517

Please sign in to comment.