From 1abf5c56dd712bf01cdf919a76b91dedfdb87865 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Wed, 16 Mar 2022 06:46:18 -0500 Subject: [PATCH 1/3] WIP #15. Add initial support for mixins --- package-lock.json | 3 ++- src/common/JSONImporter.js | 34 ++++++++++++++++++++++++++++- src/common/README.md | 1 + test/common/JSONImporter.spec.js | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4064a9d..c23fe05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "webgme-json-importer", - "version": "1.0.0", + "version": "1.2.0", "devDependencies": { "chai": "^4.3.4", "mocha": "^5.2.0", @@ -14035,6 +14035,7 @@ }, "webgme-rust-components": { "version": "git+ssh://git@github.com/webgme/webgme-rust-components.git#ad446234b6c02fd722e7e454015857ee523fb172", + "integrity": "sha512-jS1rMUMLb7hXMhBheFxiTA+yLw068o3cheiFIH0vszWN8xGIjGXZn8YrzOmK2hkAu3HLF4lT7nuTYJ+uSZUtpA==", "dev": true, "from": "webgme-rust-components@github:webgme/webgme-rust-components" }, diff --git a/src/common/JSONImporter.js b/src/common/JSONImporter.js index 7387454..8944a36 100644 --- a/src/common/JSONImporter.js +++ b/src/common/JSONImporter.js @@ -34,6 +34,7 @@ define([ attribute_meta: {}, pointers: {}, pointer_meta: {}, + mixins: [], registry: {}, sets: {}, member_attributes: {}, @@ -61,6 +62,7 @@ define([ })); const baseNode = this.core.getBase(node); json.pointers.base = baseNode && this.core.getGuid(baseNode); + json.mixins = Object.values(this.core.getMixinNodes(node)).map(node => this.core.getGuid(node)); asyncTasks.push(...this.core.getOwnValidPointerNames(node).map(async name => { const ptr_meta = this.core.getPointerMeta(node, name); @@ -71,7 +73,9 @@ define([ json.registry[name] = this.core.getRegistry(node, name); }); - asyncTasks.push(...this.core.getOwnSetNames(node).map(async name => { + asyncTasks.push(...this.core.getOwnSetNames(node) + .filter(name => name !== '_mixins') + .map(async name => { const paths = this.core.getMemberPaths(node, name); const members = await Promise.all(paths.map(path => this.core.loadByPath(this.rootNode, path))); const memberGuids = members.map(member => this.core.getGuid(member)); @@ -136,6 +140,7 @@ define([ 'children_meta', 'pointer_meta', 'pointers', + 'mixins', 'sets', 'member_attributes', 'member_registry', @@ -319,6 +324,25 @@ define([ } } + Importer.prototype._put.mixins = async function(node, change, resolvedSelectors) { + const [, index] = change.key; + const oldMixinPath = this.core.getMixinPaths(node)[index]; + if (oldMixinPath) { + console.log('deleting old mixin', oldMixinPath); + this.core.delMixin(node, oldMixinPath); + } + + const mixinId = change.value; + const mixinPath = await this.getNodeId(node, mixinId, resolvedSelectors); + if (this.core.canSetAsMixin(node, mixinPath)) { + console.log('adding mixin', mixinPath); + this.core.addMixin(node, mixinPath); + console.log('mixin paths:', this.core.getMixinPaths(node)); + } else { + throw new Error(`Cannot set ${mixinId} as mixin for ${this.core.getPath(node)}`); + } + }; + Importer.prototype._put.attributes = function(node, change) { assert( change.key.length === 2, @@ -439,6 +463,14 @@ define([ } }; + Importer.prototype._delete.mixins = async function(node, change, resolvedSelectors) { + // TODO: look up the mixin at the given index. Is getMixinPaths stable? + const [, index] = change.key; + const mixinPath = this.core.getMixinPaths(node)[index]; + console.log('deleting mixin', mixinPath); + this.core.delMixin(node, mixinPath); + }; + Importer.prototype._put.children_meta = async function(node, change, resolvedSelectors) { const [/*"children_meta"*/, idOrUndef] = change.key; const isAddingContainment = !idOrUndef; diff --git a/src/common/README.md b/src/common/README.md index 95d5979..ac1b7db 100644 --- a/src/common/README.md +++ b/src/common/README.md @@ -18,6 +18,7 @@ An example JSON is as follows: name: idOrPath, base: idOrPath, }, + mixins: [idOrPath, ...], pointer_meta: { name: { idOrPath: {min=-1, max=1}, // max=-1 if it defines a set diff --git a/test/common/JSONImporter.spec.js b/test/common/JSONImporter.spec.js index 9549fc9..b8b3dce 100644 --- a/test/common/JSONImporter.spec.js +++ b/test/common/JSONImporter.spec.js @@ -460,6 +460,43 @@ describe('JSONImporter', function () { }); }); + describe('mixins', function() { + it('should add mixin', async function() { + const nodeId = core.getPath(node); + original2.mixins.push(nodeId); + await importer.apply(node2, original2); + const mixins = Object.keys(core.getMixinNodes(node2)); + assert(mixins.includes(nodeId)); + assert.equal(mixins.length, 1); + }); + + it('should remove mixin', async function() { + const nodeId = core.getPath(node); + core.addMixin(node2, nodeId); + await importer.apply(node2, original2); + const mixins = Object.keys(core.getMixinNodes(node2)); + assert.equal(mixins.length, 0); + }); + + it.only('should change mixin', async function() { + const nodeId = core.getPath(node); + core.addMixin(node2, nodeId); + original2.mixins.push(core.getPath(node3)); + await importer.apply(node2, original2); + const mixins = Object.keys(core.getMixinNodes(node2)); + assert.deepEqual(mixins, original2.mixins); + }); + + it('should add mixin using ID', async function() { + const nodeId = core.getPath(node); + original2.mixins.push(nodeId); + await importer.apply(node2, original2); + const mixins = Object.keys(core.getMixinNodes(node2)); + assert(mixins.includes(nodeId)); + assert.equal(mixins.length, 1); + }); + }); + describe('sets', function() { const setName = 'someSet'; let node4; From 4a06df8d7c36b8ce20e7103d0d876904a88794a2 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Wed, 16 Mar 2022 06:59:35 -0500 Subject: [PATCH 2/3] Fixed canSetAsMixin check --- src/common/JSONImporter.js | 10 +++------ test/common/JSONImporter.spec.js | 37 ++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/common/JSONImporter.js b/src/common/JSONImporter.js index 8944a36..b79d996 100644 --- a/src/common/JSONImporter.js +++ b/src/common/JSONImporter.js @@ -328,18 +328,16 @@ define([ const [, index] = change.key; const oldMixinPath = this.core.getMixinPaths(node)[index]; if (oldMixinPath) { - console.log('deleting old mixin', oldMixinPath); this.core.delMixin(node, oldMixinPath); } const mixinId = change.value; const mixinPath = await this.getNodeId(node, mixinId, resolvedSelectors); - if (this.core.canSetAsMixin(node, mixinPath)) { - console.log('adding mixin', mixinPath); + const canSet = this.core.canSetAsMixin(node, mixinPath); + if (canSet.isOk) { this.core.addMixin(node, mixinPath); - console.log('mixin paths:', this.core.getMixinPaths(node)); } else { - throw new Error(`Cannot set ${mixinId} as mixin for ${this.core.getPath(node)}`); + throw new Error(`Cannot set ${mixinId} as mixin for ${this.core.getPath(node)}: ${canSet.reason}`); } }; @@ -464,10 +462,8 @@ define([ }; Importer.prototype._delete.mixins = async function(node, change, resolvedSelectors) { - // TODO: look up the mixin at the given index. Is getMixinPaths stable? const [, index] = change.key; const mixinPath = this.core.getMixinPaths(node)[index]; - console.log('deleting mixin', mixinPath); this.core.delMixin(node, mixinPath); }; diff --git a/test/common/JSONImporter.spec.js b/test/common/JSONImporter.spec.js index b8b3dce..ae9c66f 100644 --- a/test/common/JSONImporter.spec.js +++ b/test/common/JSONImporter.spec.js @@ -461,37 +461,52 @@ describe('JSONImporter', function () { }); describe('mixins', function() { + let node4; + + beforeEach(() => { + const base = node; + const parent = root; + node4 = core.createNode({base, parent}); + core.setAttribute(node4, 'name', 'Node4'); + core.addMember(root, 'MetaAspectSet', node4); + + }); + it('should add mixin', async function() { - const nodeId = core.getPath(node); + const nodeId = core.getPath(node4); original2.mixins.push(nodeId); await importer.apply(node2, original2); - const mixins = Object.keys(core.getMixinNodes(node2)); + const mixins = core.getMixinPaths(node2); assert(mixins.includes(nodeId)); assert.equal(mixins.length, 1); }); it('should remove mixin', async function() { - const nodeId = core.getPath(node); + const nodeId = core.getPath(node4); core.addMixin(node2, nodeId); await importer.apply(node2, original2); - const mixins = Object.keys(core.getMixinNodes(node2)); + const mixins = core.getMixinPaths(node2); assert.equal(mixins.length, 0); }); - it.only('should change mixin', async function() { - const nodeId = core.getPath(node); + it('should change mixin', async function() { + const node5 = core.createNode({base: node, parent: root}); + core.setAttribute(node5, 'name', 'Node5'); + core.addMember(root, 'MetaAspectSet', node5); + + const nodeId = core.getPath(node4); core.addMixin(node2, nodeId); - original2.mixins.push(core.getPath(node3)); + original2.mixins.push(core.getPath(node5)); await importer.apply(node2, original2); - const mixins = Object.keys(core.getMixinNodes(node2)); + const mixins = core.getMixinPaths(node2); assert.deepEqual(mixins, original2.mixins); }); it('should add mixin using ID', async function() { - const nodeId = core.getPath(node); - original2.mixins.push(nodeId); + original2.mixins.push('@meta:Node4'); await importer.apply(node2, original2); - const mixins = Object.keys(core.getMixinNodes(node2)); + const mixins = core.getMixinPaths(node2); + const nodeId = core.getPath(node4); assert(mixins.includes(nodeId)); assert.equal(mixins.length, 1); }); From 7f3349200828d37a9af6b37e77f6a1b551239f56 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Wed, 16 Mar 2022 07:02:35 -0500 Subject: [PATCH 3/3] Ensure error thrown for invalid mixin --- test/common/JSONImporter.spec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/common/JSONImporter.spec.js b/test/common/JSONImporter.spec.js index ae9c66f..ddf21c6 100644 --- a/test/common/JSONImporter.spec.js +++ b/test/common/JSONImporter.spec.js @@ -510,6 +510,14 @@ describe('JSONImporter', function () { assert(mixins.includes(nodeId)); assert.equal(mixins.length, 1); }); + + it('should throw error on invalid mixin', async function() { + original2.mixins.push('@meta:FCO'); + await assert.rejects( + () => importer.apply(node2, original2), + /Cannot set .* as mixin/ + ); + }); }); describe('sets', function() {