Skip to content

Commit

Permalink
feat: methods for read/write sfProjectJson.plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
mshanemc committed Jun 24, 2024
1 parent 0c92793 commit dd2c26a
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 4 deletions.
56 changes: 52 additions & 4 deletions src/sfProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ export type ProjectJson = ConfigContents & ProjectJsonSchema;
* The sfdx-project.json config object. This file determines if a folder is a valid sfdx project.
*
* *Note:* Any non-standard (not owned by Salesforce) properties stored in sfdx-project.json should
* be in a top level property that represents your project or plugin.
* be in a top level property that represents your project. Plugins should store their configuration via getPluginConfiguration and setPluginConfiguration
*
* @example reading a standard property
* ```
* const project = await SfProject.resolve();
* const projectJson = await project.resolveProjectConfig();
* const myPluginProperties = projectJson.get('myplugin') || {};
* myPluginProperties.myprop = 'someValue';
* projectJson.set('myplugin', myPluginProperties);
* const namespace = projectJson.get('namespace');
* ```
*
* ```
* @example writing
* const project = await SfProject.resolve();
* const projectJson = await project.resolveProjectConfig();
* projectJson.set('namespace', 'new');
* await projectJson.write();
* ```
*
Expand Down Expand Up @@ -712,6 +718,48 @@ export class SfProject {
.filter(([, value]) => value?.startsWith(id))
.map(([key]) => key);
}

/**
* retrieve the configuration for a named plugin from sfdx-project.json.plugins.pluginName
*
* @example
* ```
* const project = await SfProject.resolve();
* const pluginConfig = await project.getPluginConfiguration('myPlugin');
* ```
*
* optionally pass a type parameter for your plugin configuration's schema
* */
public async getPluginConfiguration<T extends Record<string, unknown>>(pluginName: string): Promise<Readonly<T>> {
await this.retrieveSfProjectJson();
const plugins = this.sfProjectJson.get('plugins');
if (!plugins) {
throw new SfError('No plugins defined in sfdx-project.json', 'NoPluginsDefined');
}
if (!plugins[pluginName]) {
throw new SfError(`No configuration defined in sfdx-project.json for plugin ${pluginName}`, 'PluginNotFound');
}
return plugins[pluginName] as T;
}

/**
* set the configuration for a named plugin from sfdx-project.json.plugins.pluginName, overwriting existing configuration
*
* @example
* ```
* const project = await SfProject.resolve();
* const pluginConfig = await project.setPluginConfiguration('myPlugin', {foo: 'bar', myLimit: 25});
* ```
*
* optionally pass a type parameter for your plugin configuration's schema
* */
public async setPluginConfiguration<T extends Record<string, unknown>>(pluginName: string, config: T): Promise<void> {
await this.retrieveSfProjectJson();
const plugins = this.getSfProjectJson().get('plugins') ?? {};
const modified = { ...plugins, [pluginName]: config };
this.sfProjectJson.set('plugins', modified);
this.sfProjectJson.writeSync();
}
}

/** differentiate between the Base PackageDir (path, maybe default) and the Packaging version (package and maybe a LOT of other fields) by whether is has the `package` property */
Expand Down
75 changes: 75 additions & 0 deletions test/unit/projectTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -890,4 +890,79 @@ describe('SfProject', () => {
expect(foundPkg?.default).to.be.false;
});
});

describe('plugins', () => {
describe('read', () => {
it('throws on read when no existing plugins', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: {},
});
const project = SfProject.getInstance();
try {
await project.getPluginConfiguration('fooPlugin');
assert.fail('Expected error to be thrown');
} catch (e) {
assert(e instanceof SfError);
expect(e.name).to.equal('NoPluginsDefined');
}
});
it('throws on read when no existing plugin when named', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: {
plugins: { someOtherPlugin: {} },
},
});
const project = SfProject.getInstance();
try {
await project.getPluginConfiguration('fooPlugin');
assert.fail('Expected error to be thrown');
} catch (e) {
assert(e instanceof SfError);
expect(e.name).to.equal('PluginNotFound');
}
});
it('read returns valid data', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: {
plugins: { someOtherPlugin: {}, fooPlugin: { foo: 'bar' } },
},
});
const project = SfProject.getInstance();
const config = await project.getPluginConfiguration('fooPlugin');
expect(config).to.deep.equal({ foo: 'bar' });
});
});
describe('write', () => {
it('write when no existing plugins', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: {},
});
const project = SfProject.getInstance();
await project.setPluginConfiguration('fooPlugin', { foo: 'bar' });
expect($$.getConfigStubContents('SfProjectJson').plugins).to.deep.equal({ fooPlugin: { foo: 'bar' } });
});
it('write new plugin', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: { plugins: { otherPlugin: {} } },
});
const project = SfProject.getInstance();
await project.setPluginConfiguration('fooPlugin', { foo: 'bar' });
expect($$.getConfigStubContents('SfProjectJson').plugins).to.deep.equal({
otherPlugin: {},
fooPlugin: { foo: 'bar' },
});
});
it('update existing plugin', async () => {
$$.setConfigStubContents('SfProjectJson', {
contents: { plugins: { otherPlugin: {}, fooPlugin: { foo: 'bat', removeMe: 0 } } },
});
const project = SfProject.getInstance();
await project.setPluginConfiguration('fooPlugin', { foo: 'bar' });
expect($$.getConfigStubContents('SfProjectJson').plugins).to.deep.equal({
otherPlugin: {},
fooPlugin: { foo: 'bar' },
});
});
});
});
});

2 comments on commit dd2c26a

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

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

Logger Benchmarks - ubuntu-latest

Benchmark suite Current: dd2c26a Previous: 2bb6f98 Ratio
Child logger creation 465398 ops/sec (±2.58%) 474649 ops/sec (±1.59%) 1.02
Logging a string on root logger 801916 ops/sec (±6.94%) 767091 ops/sec (±7.42%) 0.96
Logging an object on root logger 578698 ops/sec (±5.87%) 632734 ops/sec (±5.75%) 1.09
Logging an object with a message on root logger 8738 ops/sec (±201.77%) 5367 ops/sec (±211.45%) 0.61
Logging an object with a redacted prop on root logger 394911 ops/sec (±14.84%) 511730 ops/sec (±12.37%) 1.30
Logging a nested 3-level object on root logger 353790 ops/sec (±7.74%) 369777 ops/sec (±8.09%) 1.05

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

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

Logger Benchmarks - windows-latest

Benchmark suite Current: dd2c26a Previous: 2bb6f98 Ratio
Child logger creation 333138 ops/sec (±0.82%) 328142 ops/sec (±0.89%) 0.99
Logging a string on root logger 715336 ops/sec (±5.84%) 823574 ops/sec (±6.57%) 1.15
Logging an object on root logger 592041 ops/sec (±5.96%) 602671 ops/sec (±7.42%) 1.02
Logging an object with a message on root logger 7596 ops/sec (±200.84%) 2945 ops/sec (±223.89%) 0.39
Logging an object with a redacted prop on root logger 448133 ops/sec (±7.67%) 448898 ops/sec (±9.89%) 1.00
Logging a nested 3-level object on root logger 320866 ops/sec (±4.81%) 321049 ops/sec (±5.88%) 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.