From 7eead6660b8c1bae44a3e727766ed0e735b09db6 Mon Sep 17 00:00:00 2001 From: Nate Wang Date: Thu, 23 Apr 2026 08:34:25 +0800 Subject: [PATCH] MUSE-1252 added MSP mgmt API --- workspace/packages/muse-core/lib/index.js | 7 +- .../packages/muse-core/lib/msp/addPreset.js | 51 ++++++ .../muse-core/lib/msp/addPreset.test.js | 89 +++++++++++ .../muse-core/lib/msp/deletePreset.js | 44 ++++++ .../muse-core/lib/msp/deletePreset.test.js | 92 +++++++++++ .../packages/muse-core/lib/msp/getMsp.js | 32 ++++ .../packages/muse-core/lib/msp/getMsp.test.js | 72 +++++++++ workspace/packages/muse-core/lib/msp/index.js | 13 ++ .../muse-core/lib/msp/updatePackages.js | 57 +++++++ .../muse-core/lib/msp/updatePackages.test.js | 146 ++++++++++++++++++ .../muse-core/lib/schemas/msp/addPreset.json | 21 +++ .../lib/schemas/msp/deletePreset.json | 18 +++ .../muse-core/lib/schemas/msp/getMsp.json | 7 + .../lib/schemas/msp/updatePackages.json | 18 +++ 14 files changed, 664 insertions(+), 3 deletions(-) create mode 100644 workspace/packages/muse-core/lib/msp/addPreset.js create mode 100644 workspace/packages/muse-core/lib/msp/addPreset.test.js create mode 100644 workspace/packages/muse-core/lib/msp/deletePreset.js create mode 100644 workspace/packages/muse-core/lib/msp/deletePreset.test.js create mode 100644 workspace/packages/muse-core/lib/msp/getMsp.js create mode 100644 workspace/packages/muse-core/lib/msp/getMsp.test.js create mode 100644 workspace/packages/muse-core/lib/msp/index.js create mode 100644 workspace/packages/muse-core/lib/msp/updatePackages.js create mode 100644 workspace/packages/muse-core/lib/msp/updatePackages.test.js create mode 100644 workspace/packages/muse-core/lib/schemas/msp/addPreset.json create mode 100644 workspace/packages/muse-core/lib/schemas/msp/deletePreset.json create mode 100644 workspace/packages/muse-core/lib/schemas/msp/getMsp.json create mode 100644 workspace/packages/muse-core/lib/schemas/msp/updatePackages.json diff --git a/workspace/packages/muse-core/lib/index.js b/workspace/packages/muse-core/lib/index.js index 6fdbcf00..326903b6 100644 --- a/workspace/packages/muse-core/lib/index.js +++ b/workspace/packages/muse-core/lib/index.js @@ -5,7 +5,7 @@ var originalRequire = Module.prototype.require; // Ensure @ebay/muse-core only has one instance const __museCoreSingleton = module.exports; -Module.prototype.require = function() { +Module.prototype.require = function () { if (arguments[0] === '@ebay/muse-core') return __museCoreSingleton; return originalRequire.apply(this, arguments); }; @@ -18,7 +18,7 @@ const plugin = require('js-plugin'); const envFile1 = path.join(process.cwd(), '.muse.env'); const envFile2 = path.join(os.homedir(), '.muse.env'); -[envFile1, envFile2].some(envFile => { +[envFile1, envFile2].some((envFile) => { if (fs.existsSync(envFile)) { require('dotenv').config({ path: envFile }); return true; @@ -29,7 +29,7 @@ const config = require('./config'); module.exports.config = config; module.exports.logger = require('./logger'); -module.exports.registerPlugin = p => { +module.exports.registerPlugin = (p) => { if (config.__pluginLoaded) { throw new Error( `You can only register a plugin before initialization. Usually you should register a plugin in the global scope in your code.`, @@ -47,6 +47,7 @@ module.exports.pm = require('./pm'); module.exports.req = require('./req'); module.exports.data = require('./data'); module.exports.storage = require('./storage'); +module.exports.msp = require('./msp'); module.exports.utils = require('./utils'); module.exports.plugin = plugin; diff --git a/workspace/packages/muse-core/lib/msp/addPreset.js b/workspace/packages/muse-core/lib/msp/addPreset.js new file mode 100644 index 00000000..1761654b --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/addPreset.js @@ -0,0 +1,51 @@ +const { asyncInvoke, osUsername, validate } = require('../utils'); +const { registry } = require('../storage'); +const getMsp = require('./getMsp'); +const schema = require('../schemas/msp/addPreset.json'); +const logger = require('../logger').createLogger('muse.msp.addPreset'); + +/** + * @module muse-core/msp/addPreset + */ + +/** + * @description Add a new preset to /msp.yaml. + * @param {object} params + * @param {string} params.name The preset name. + * @param {object} params.preset The preset object. + * @param {string} [params.preset.extends] Parent preset name. + * @param {object} [params.preset.versions] Package versions for this preset. + * @param {string} [params.author=osUsername] + * @param {string} [params.msg] Commit message. + * @returns {object} The created preset. + */ +module.exports = async (params = {}) => { + validate(schema, params); + const ctx = {}; + if (!params.author) params.author = osUsername; + const { name, preset, author, msg } = params; + logger.info(`Adding preset ${name}...`); + await asyncInvoke('museCore.msp.beforeAddPreset', ctx, params); + + let msp = await getMsp(); + if (!msp) msp = {}; + if (msp[name]) throw new Error(`Preset ${name} already exists.`); + + ctx.preset = { + creation: new Date().toJSON(), + ...preset, + }; + + try { + msp[name] = ctx.preset; + await asyncInvoke('museCore.msp.addPreset', ctx, params); + await registry.setYaml('/msp.yaml', msp, msg || `Add preset ${name} by ${author}`); + } catch (err) { + ctx.error = err; + await asyncInvoke('museCore.msp.failedAddPreset', ctx, params); + throw err; + } + await asyncInvoke('museCore.msp.afterAddPreset', ctx, params); + logger.info(`Add preset success: ${name}.`); + return ctx.preset; +}; diff --git a/workspace/packages/muse-core/lib/msp/addPreset.test.js b/workspace/packages/muse-core/lib/msp/addPreset.test.js new file mode 100644 index 00000000..96e52043 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/addPreset.test.js @@ -0,0 +1,89 @@ +const { vol } = require('memfs'); +const plugin = require('js-plugin'); +const muse = require('../'); +const { registry } = require('../storage'); + +const testJsPlugin = { + name: 'test-addPreset', + museCore: { + msp: { + addPreset: jest.fn(), + beforeAddPreset: jest.fn(), + afterAddPreset: jest.fn(), + }, + }, +}; +plugin.register(testJsPlugin); + +describe('addPreset tests.', () => { + beforeEach(() => { + vol.reset(); + }); + + it('should create msp.yaml and add preset when file does not exist', async () => { + const preset = await muse.msp.addPreset({ + name: 'default', + preset: { versions: { '@ebay/muse-core': '1.0.45' } }, + author: 'nate', + }); + + expect(preset).toMatchObject({ versions: { '@ebay/muse-core': '1.0.45' } }); + expect(preset.creation).toBeDefined(); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default).toMatchObject({ versions: { '@ebay/muse-core': '1.0.45' } }); + + expect(testJsPlugin.museCore.msp.addPreset).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.beforeAddPreset).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.afterAddPreset).toBeCalledTimes(1); + }); + + it('should add preset to existing msp.yaml', async () => { + await registry.set('/msp.yaml', 'default:\n versions:\n "@ebay/muse-core": "1.0.45"\n'); + + await muse.msp.addPreset({ + name: 'muse-react', + preset: { extends: 'default', versions: { '@ebay/muse-lib-react': '2.0.3' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(Object.keys(msp)).toEqual(['default', 'muse-react']); + expect(msp['muse-react'].extends).toBe('default'); + }); + + it('should throw if preset already exists', async () => { + await registry.set('/msp.yaml', 'default:\n versions: {}\n'); + + try { + await muse.msp.addPreset({ name: 'default', preset: {}, author: 'nate' }); + expect(true).toBe(false); + } catch (err) { + expect(err.message).toMatch('already exists'); + } + }); + + it('should throw and invoke failedAddPreset on error', async () => { + const testJsPluginFails = { + name: 'test-addPreset-fail', + museCore: { + msp: { + addPreset: jest.fn().mockRejectedValue(new Error('storage error')), + beforeAddPreset: jest.fn(), + afterAddPreset: jest.fn(), + failedAddPreset: jest.fn(), + }, + }, + }; + plugin.register(testJsPluginFails); + + try { + await muse.msp.addPreset({ name: 'new-preset', preset: {}, author: 'nate' }); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toEqual('storage error'); + } + expect(testJsPluginFails.museCore.msp.failedAddPreset).toBeCalledTimes(1); + expect(testJsPluginFails.museCore.msp.afterAddPreset).toBeCalledTimes(0); + }); +}); diff --git a/workspace/packages/muse-core/lib/msp/deletePreset.js b/workspace/packages/muse-core/lib/msp/deletePreset.js new file mode 100644 index 00000000..ff40d6a0 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/deletePreset.js @@ -0,0 +1,44 @@ +const { asyncInvoke, osUsername, validate } = require('../utils'); +const { registry } = require('../storage'); +const getMsp = require('./getMsp'); +const schema = require('../schemas/msp/deletePreset.json'); +const logger = require('../logger').createLogger('muse.msp.deletePreset'); + +/** + * @module muse-core/msp/deletePreset + */ + +/** + * @description Delete a preset from /msp.yaml. + * @param {object} params + * @param {string} params.name The preset name to delete. + * @param {string} [params.author=osUsername] + * @param {string} [params.msg] Commit message. + */ +module.exports = async (params = {}) => { + validate(schema, params); + const ctx = {}; + if (!params.author) params.author = osUsername; + const { name, author, msg } = params; + logger.info(`Deleting preset ${name}...`); + await asyncInvoke('museCore.msp.beforeDeletePreset', ctx, params); + + const msp = await getMsp(); + if (!msp) throw new Error('msp.yaml does not exist.'); + if (!msp[name]) throw new Error(`Preset ${name} does not exist.`); + + ctx.preset = msp[name]; + + try { + delete msp[name]; + await asyncInvoke('museCore.msp.deletePreset', ctx, params); + await registry.setYaml('/msp.yaml', msp, msg || `Delete preset ${name} by ${author}`); + } catch (err) { + ctx.error = err; + await asyncInvoke('museCore.msp.failedDeletePreset', ctx, params); + throw err; + } + await asyncInvoke('museCore.msp.afterDeletePreset', ctx, params); + logger.info(`Delete preset success: ${name}.`); + return ctx; +}; diff --git a/workspace/packages/muse-core/lib/msp/deletePreset.test.js b/workspace/packages/muse-core/lib/msp/deletePreset.test.js new file mode 100644 index 00000000..984e45eb --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/deletePreset.test.js @@ -0,0 +1,92 @@ +const { vol } = require('memfs'); +const plugin = require('js-plugin'); +const muse = require('../'); +const { registry } = require('../storage'); + +const testJsPlugin = { + name: 'test-deletePreset', + museCore: { + msp: { + deletePreset: jest.fn(), + beforeDeletePreset: jest.fn(), + afterDeletePreset: jest.fn(), + }, + }, +}; +plugin.register(testJsPlugin); + +describe('deletePreset tests.', () => { + beforeEach(() => { + vol.reset(); + }); + + it('should delete a preset from msp.yaml', async () => { + await registry.set( + '/msp.yaml', + ` +default: + versions: + "@ebay/muse-core": "1.0.45" +muse-react: + extends: default + versions: + "@ebay/muse-lib-react": "2.0.3" +`, + ); + + await muse.msp.deletePreset({ name: 'muse-react', author: 'nate' }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp['muse-react']).toBeUndefined(); + expect(msp.default).toBeDefined(); + + expect(testJsPlugin.museCore.msp.deletePreset).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.beforeDeletePreset).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.afterDeletePreset).toBeCalledTimes(1); + }); + + it('should throw if msp.yaml does not exist', async () => { + try { + await muse.msp.deletePreset({ name: 'default', author: 'nate' }); + expect(true).toBe(false); + } catch (err) { + expect(err.message).toMatch('msp.yaml does not exist'); + } + }); + + it('should throw if preset does not exist', async () => { + await registry.set('/msp.yaml', 'default:\n versions: {}\n'); + + try { + await muse.msp.deletePreset({ name: 'no-such-preset', author: 'nate' }); + expect(true).toBe(false); + } catch (err) { + expect(err.message).toMatch('does not exist'); + } + }); + + it('should throw and invoke failedDeletePreset on error', async () => { + const testJsPluginFails = { + name: 'test-deletePreset-fail', + museCore: { + msp: { + deletePreset: jest.fn().mockRejectedValue(new Error('storage error')), + beforeDeletePreset: jest.fn(), + afterDeletePreset: jest.fn(), + failedDeletePreset: jest.fn(), + }, + }, + }; + plugin.register(testJsPluginFails); + await registry.set('/msp.yaml', 'default:\n versions: {}\n'); + + try { + await muse.msp.deletePreset({ name: 'default', author: 'nate' }); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toEqual('storage error'); + } + expect(testJsPluginFails.museCore.msp.failedDeletePreset).toBeCalledTimes(1); + expect(testJsPluginFails.museCore.msp.afterDeletePreset).toBeCalledTimes(0); + }); +}); diff --git a/workspace/packages/muse-core/lib/msp/getMsp.js b/workspace/packages/muse-core/lib/msp/getMsp.js new file mode 100644 index 00000000..9c3223f2 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/getMsp.js @@ -0,0 +1,32 @@ +const { asyncInvoke, validate } = require('../utils'); +const { registry } = require('../storage'); +const schema = require('../schemas/msp/getMsp.json'); +const logger = require('../logger').createLogger('muse.msp.getMsp'); + +/** + * @module muse-core/msp/getMsp + */ + +/** + * @description Get all MSP presets as raw JSON from /msp.yaml. + * @param {object} [params={}] + * @returns {object|null} The parsed msp.yaml content, or null if it doesn't exist. + */ +module.exports = async (params = {}) => { + validate(schema, params); + const ctx = {}; + logger.verbose('Getting msp...'); + await asyncInvoke('museCore.msp.beforeGetMsp', ctx, params); + + try { + ctx.msp = await registry.getJsonByYaml('/msp.yaml'); + await asyncInvoke('museCore.msp.getMsp', ctx, params); + } catch (err) { + ctx.error = err; + await asyncInvoke('museCore.msp.failedGetMsp', ctx, params); + throw err; + } + await asyncInvoke('museCore.msp.afterGetMsp', ctx, params); + logger.verbose('Get msp success.'); + return ctx.msp; +}; diff --git a/workspace/packages/muse-core/lib/msp/getMsp.test.js b/workspace/packages/muse-core/lib/msp/getMsp.test.js new file mode 100644 index 00000000..3005b7a3 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/getMsp.test.js @@ -0,0 +1,72 @@ +const { vol } = require('memfs'); +const plugin = require('js-plugin'); +const muse = require('../'); +const { registry } = require('../storage'); + +const testJsPlugin = { + name: 'test-getMsp', + museCore: { + msp: { + getMsp: jest.fn(), + beforeGetMsp: jest.fn(), + afterGetMsp: jest.fn(), + }, + }, +}; +plugin.register(testJsPlugin); + +describe('getMsp tests.', () => { + beforeEach(() => { + vol.reset(); + }); + + it('should return null when msp.yaml does not exist', async () => { + const result = await muse.msp.getMsp(); + expect(result).toBeNull(); + }); + + it('should return parsed msp.yaml as json', async () => { + await registry.set( + '/msp.yaml', + ` +default: + description: Base preset + versions: + "@ebay/muse-core": "1.0.45" +`, + ); + const result = await muse.msp.getMsp(); + expect(result).toMatchObject({ + default: { description: 'Base preset', versions: { '@ebay/muse-core': '1.0.45' } }, + }); + expect(testJsPlugin.museCore.msp.getMsp).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.beforeGetMsp).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.afterGetMsp).toBeCalledTimes(1); + }); + + it('should throw and invoke failedGetMsp on error', async () => { + const testJsPluginFails = { + name: 'test-getMsp-fail', + museCore: { + msp: { + getMsp: jest.fn().mockRejectedValue(new Error('storage error')), + beforeGetMsp: jest.fn(), + afterGetMsp: jest.fn(), + failedGetMsp: jest.fn(), + }, + }, + }; + plugin.register(testJsPluginFails); + + await registry.set('/msp.yaml', 'default:\n versions: {}\n'); + + try { + await muse.msp.getMsp(); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toEqual('storage error'); + } + expect(testJsPluginFails.museCore.msp.failedGetMsp).toBeCalledTimes(1); + expect(testJsPluginFails.museCore.msp.afterGetMsp).toBeCalledTimes(0); + }); +}); diff --git a/workspace/packages/muse-core/lib/msp/index.js b/workspace/packages/muse-core/lib/msp/index.js new file mode 100644 index 00000000..49275951 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/index.js @@ -0,0 +1,13 @@ +/** + * @module muse-core/msp + */ +module.exports = { + /** @member {function} getMsp */ + getMsp: require('./getMsp'), + /** @member {function} addPreset */ + addPreset: require('./addPreset'), + /** @member {function} deletePreset */ + deletePreset: require('./deletePreset'), + /** @member {function} updatePackages */ + updatePackages: require('./updatePackages'), +}; diff --git a/workspace/packages/muse-core/lib/msp/updatePackages.js b/workspace/packages/muse-core/lib/msp/updatePackages.js new file mode 100644 index 00000000..dc085741 --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/updatePackages.js @@ -0,0 +1,57 @@ +const semver = require('semver'); +const { asyncInvoke, osUsername, validate } = require('../utils'); +const { registry } = require('../storage'); +const getMsp = require('./getMsp'); +const schema = require('../schemas/msp/updatePackages.json'); +const logger = require('../logger').createLogger('muse.msp.updatePackages'); + +/** + * @module muse-core/msp/updatePackages + */ + +/** + * @description Update package versions across all presets in /msp.yaml. + * For each preset, only updates a package when the new version shares the same major version. + * Skips pre-release versions unless allowPreRelease is true for that package. + * @param {object} params + * @param {object} params.pkgs Map of package name to { version, allowPreRelease }. + * @param {string} [params.author=osUsername] + * @param {string} [params.msg] Commit message. + * @returns {object} The updated msp object. + */ +module.exports = async (params = {}) => { + validate(schema, params); + const ctx = {}; + if (!params.author) params.author = osUsername; + const { pkgs, author, msg } = params; + logger.info('Updating packages in msp...'); + await asyncInvoke('museCore.msp.beforeUpdatePackages', ctx, params); + + const msp = await getMsp(); + if (!msp) throw new Error('msp.yaml does not exist.'); + + for (const preset of Object.values(msp)) { + if (!preset.versions) continue; + for (const [pkg, { version: newVersion, allowPreRelease = false }] of Object.entries(pkgs)) { + const current = preset.versions[pkg]; + if (!current) continue; + if (!allowPreRelease && semver.prerelease(newVersion)) continue; + if (semver.major(newVersion) !== semver.major(current)) continue; + preset.versions[pkg] = newVersion; + } + } + + ctx.msp = msp; + + try { + await asyncInvoke('museCore.msp.updatePackages', ctx, params); + await registry.setYaml('/msp.yaml', msp, msg || `Update packages in msp by ${author}`); + } catch (err) { + ctx.error = err; + await asyncInvoke('museCore.msp.failedUpdatePackages', ctx, params); + throw err; + } + await asyncInvoke('museCore.msp.afterUpdatePackages', ctx, params); + logger.info('Update packages in msp success.'); + return ctx.msp; +}; diff --git a/workspace/packages/muse-core/lib/msp/updatePackages.test.js b/workspace/packages/muse-core/lib/msp/updatePackages.test.js new file mode 100644 index 00000000..b6cffdff --- /dev/null +++ b/workspace/packages/muse-core/lib/msp/updatePackages.test.js @@ -0,0 +1,146 @@ +const { vol } = require('memfs'); +const plugin = require('js-plugin'); +const muse = require('../'); +const { registry } = require('../storage'); + +const testJsPlugin = { + name: 'test-updatePackages', + museCore: { + msp: { + updatePackages: jest.fn(), + beforeUpdatePackages: jest.fn(), + afterUpdatePackages: jest.fn(), + }, + }, +}; +plugin.register(testJsPlugin); + +const MSP_YAML = ` +default: + versions: + "@ebay/muse-core": "1.0.45" + "@ebay/muse-lib-react": "1.3.2" +muse-react: + extends: default + versions: + "@ebay/muse-lib-react": "2.0.3" +`; + +describe('updatePackages tests.', () => { + beforeEach(() => { + vol.reset(); + }); + + it('should update matching package versions across all presets', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-core': { version: '1.0.46' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default.versions['@ebay/muse-core']).toBe('1.0.46'); + + expect(testJsPlugin.museCore.msp.updatePackages).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.beforeUpdatePackages).toBeCalledTimes(1); + expect(testJsPlugin.museCore.msp.afterUpdatePackages).toBeCalledTimes(1); + }); + + it('should not update when major version differs', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-core': { version: '2.0.0' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default.versions['@ebay/muse-core']).toBe('1.0.45'); + }); + + it('should skip pre-release versions by default', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-core': { version: '1.0.46-beta.1' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default.versions['@ebay/muse-core']).toBe('1.0.45'); + }); + + it('should apply pre-release when allowPreRelease is true', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-core': { version: '1.0.46-beta.1', allowPreRelease: true } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default.versions['@ebay/muse-core']).toBe('1.0.46-beta.1'); + }); + + it('should skip packages not present in a preset', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-cli': { version: '1.0.35' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + expect(msp.default.versions['@ebay/muse-cli']).toBeUndefined(); + }); + + it('should update same package independently in each preset', async () => { + await registry.set('/msp.yaml', MSP_YAML); + + await muse.msp.updatePackages({ + pkgs: { '@ebay/muse-lib-react': { version: '1.3.9' } }, + author: 'nate', + }); + + const msp = await registry.getJsonByYaml('/msp.yaml'); + // default has 1.x — updated + expect(msp.default.versions['@ebay/muse-lib-react']).toBe('1.3.9'); + // muse-react has 2.x — major differs, not updated + expect(msp['muse-react'].versions['@ebay/muse-lib-react']).toBe('2.0.3'); + }); + + it('should throw if msp.yaml does not exist', async () => { + try { + await muse.msp.updatePackages({ pkgs: {}, author: 'nate' }); + expect(true).toBe(false); + } catch (err) { + expect(err.message).toMatch('msp.yaml does not exist'); + } + }); + + it('should throw and invoke failedUpdatePackages on error', async () => { + const testJsPluginFails = { + name: 'test-updatePackages-fail', + museCore: { + msp: { + updatePackages: jest.fn().mockRejectedValue(new Error('storage error')), + beforeUpdatePackages: jest.fn(), + afterUpdatePackages: jest.fn(), + failedUpdatePackages: jest.fn(), + }, + }, + }; + plugin.register(testJsPluginFails); + await registry.set('/msp.yaml', MSP_YAML); + + try { + await muse.msp.updatePackages({ pkgs: {}, author: 'nate' }); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toEqual('storage error'); + } + expect(testJsPluginFails.museCore.msp.failedUpdatePackages).toBeCalledTimes(1); + expect(testJsPluginFails.museCore.msp.afterUpdatePackages).toBeCalledTimes(0); + }); +}); diff --git a/workspace/packages/muse-core/lib/schemas/msp/addPreset.json b/workspace/packages/muse-core/lib/schemas/msp/addPreset.json new file mode 100644 index 00000000..b92c7ffc --- /dev/null +++ b/workspace/packages/muse-core/lib/schemas/msp/addPreset.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Add Preset params", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "preset": { + "type": "object" + }, + "author": { + "type": "string" + }, + "msg": { + "type": "string" + } + }, + "required": ["name", "preset"], + "additionalProperties": true +} diff --git a/workspace/packages/muse-core/lib/schemas/msp/deletePreset.json b/workspace/packages/muse-core/lib/schemas/msp/deletePreset.json new file mode 100644 index 00000000..db77c8cd --- /dev/null +++ b/workspace/packages/muse-core/lib/schemas/msp/deletePreset.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Delete Preset params", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "author": { + "type": "string" + }, + "msg": { + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": true +} diff --git a/workspace/packages/muse-core/lib/schemas/msp/getMsp.json b/workspace/packages/muse-core/lib/schemas/msp/getMsp.json new file mode 100644 index 00000000..9ea2fda8 --- /dev/null +++ b/workspace/packages/muse-core/lib/schemas/msp/getMsp.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Get Msp params", + "type": "object", + "properties": {}, + "additionalProperties": false +} diff --git a/workspace/packages/muse-core/lib/schemas/msp/updatePackages.json b/workspace/packages/muse-core/lib/schemas/msp/updatePackages.json new file mode 100644 index 00000000..de2d4960 --- /dev/null +++ b/workspace/packages/muse-core/lib/schemas/msp/updatePackages.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Update Packages params", + "type": "object", + "properties": { + "pkgs": { + "type": "object" + }, + "author": { + "type": "string" + }, + "msg": { + "type": "string" + } + }, + "required": ["pkgs"], + "additionalProperties": true +}