diff --git a/lib/extension/externalExtension.js b/lib/extension/externalExtension.js index 19f5a4b92..22ba8f31b 100644 --- a/lib/extension/externalExtension.js +++ b/lib/extension/externalExtension.js @@ -15,9 +15,9 @@ class ExternalExtension extends Extension { this.addExtension = addExtension; this.enableDisableExtension = enableDisableExtension; this.extensionsBaseDir = 'extension'; + this.loadExtension = this.loadExtension.bind(this); this.requestLookup = { 'save': this.saveExtension.bind(this), - 'read': this.readExtensionCode.bind(this), }; this.loadUserDefinedExtensions(); } @@ -28,13 +28,16 @@ class ExternalExtension extends Extension { getListOfUserDefinedExtensions() { const basePath = this.getExtensionsBasePath(); if (fs.existsSync(basePath)) { - return fs.readdirSync(basePath).filter((f) => f.endsWith('.js')); + return fs.readdirSync(basePath).filter((f) => f.endsWith('.js')).map((fileName) => { + const extensonFilePath = path.join(basePath, fileName); + return {'name': fileName, 'code': fs.readFileSync(extensonFilePath, 'utf-8')}; + }); } else { return []; } } - saveExtension({name, content}) { - const ModuleConstructor = utils.loadModuleFromText(content); + saveExtension({name, code}) { + const ModuleConstructor = utils.loadModuleFromText(code); this.loadExtension(ModuleConstructor); const basePath = this.getExtensionsBasePath(); /* istanbul ignore else */ @@ -42,15 +45,11 @@ class ExternalExtension extends Extension { fs.mkdirSync(basePath); } const extensonFilePath = path.join(basePath, name); - fs.writeFileSync(extensonFilePath, content); + fs.writeFileSync(extensonFilePath, code); this.publishExtensions(); return utils.getResponse(`Extension ${name} loaded`, {}, null); } - readExtensionCode({name}) { - const extensonFilePath = path.join(this.getExtensionsBasePath(), name); - const response = {name, content: fs.readFileSync(extensonFilePath, 'utf-8')}; - return utils.getResponse(`Extension ${name} code read`, response, null); - } + async onMQTTMessage(topic, message) { const match = topic.match(requestRegex); @@ -74,11 +73,9 @@ class ExternalExtension extends Extension { loadUserDefinedExtensions() { const extensions = this.getListOfUserDefinedExtensions(); - const extensionPath = this.getExtensionsBasePath(); - for (const extension of extensions) { - const Extension = utils.loadModuleFromFile(path.join(extensionPath, extension)); - this.loadExtension(Extension); - } + extensions + .map(({code}) => utils.loadModuleFromText(code)) + .map(this.loadExtension); } async onMQTTConnected() { this.publishExtensions(); diff --git a/test/externalExtension.test.js b/test/externalExtension.test.js index 191f26c33..5a3ab1eec 100644 --- a/test/externalExtension.test.js +++ b/test/externalExtension.test.js @@ -44,12 +44,7 @@ describe('User extensions', () => { await controller.start(); await flushPromises(); expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'test', { retain: false, qos: 0 }, expect.any(Function)); - expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify(['exampleExtension.js']), { retain: true, qos: 0 }, expect.any(Function)); - MQTT.publish.mockClear(); - MQTT.events.message('zigbee2mqtt/bridge/extension/request/read', stringify({"name": "exampleExtension.js"})); - await flushPromises(); - const expectedResponse = {"data": {"name": "exampleExtension.js", "content": extensionCode}, "status":"ok"}; - expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extension/response/read', stringify(expectedResponse), { retain: false, qos: 0 }, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([{"name": "exampleExtension.js", "code": extensionCode}]), { retain: true, qos: 0 }, expect.any(Function)); }); it('Load user extension from api call', async () => { @@ -59,9 +54,9 @@ describe('User extensions', () => { await controller.start(); await flushPromises(); MQTT.publish.mockClear(); - MQTT.events.message('zigbee2mqtt/bridge/extension/request/save', stringify({"name": "foo.js", "content": extensionCode})); + MQTT.events.message('zigbee2mqtt/bridge/extension/request/save', stringify({"name": "foo.js", "code": extensionCode})); await flushPromises(); - expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify(['foo.js']), { retain: true, qos: 0 }, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([{"name": "foo.js", "code": extensionCode}]), { retain: true, qos: 0 }, expect.any(Function)); expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', { retain: false, qos: 0 }, expect.any(Function)); expect(mkdirSyncSpy).toHaveBeenCalledWith(extensionPath); }); @@ -73,7 +68,7 @@ describe('User extensions', () => { await controller.start(); await flushPromises(); MQTT.publish.mockClear(); - MQTT.events.message('zigbee2mqtt/bridge/extension/request/save', stringify({"name": "foo.js", "content": extensionCode})); + MQTT.events.message('zigbee2mqtt/bridge/extension/request/save', stringify({"name": "foo.js", "code": extensionCode})); await flushPromises(); expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/extension/response/save', stringify({"data":{},"error":"Unexpected identifier","status":"error"}), { retain: false, qos: 0 }, expect.any(Function));