From 8c09564ecf668c4c7d05eeaf7a7e2bd773c0bb93 Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 5 Jun 2019 00:15:02 +0200 Subject: [PATCH 1/3] fix some test assertion calls Signed-off-by: pacien --- test/test_config.ts | 6 +++--- test/test_discordmessageprocessor.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_config.ts b/test/test_config.ts index 0979897b..26b037ef 100644 --- a/test/test_config.ts +++ b/test/test_config.ts @@ -38,13 +38,13 @@ describe("DiscordBridgeConfig.ApplyConfig", () => { console: "warn", }, }); - expect(config.bridge.homeserverUrl, "blah"); + expect(config.bridge.homeserverUrl).to.equal("blah"); expect(config.bridge.disableTypingNotifications).to.be.true; expect(config.bridge.disableDiscordMentions).to.be.false; expect(config.bridge.disableDeletionForwarding).to.be.true; expect(config.bridge.enableSelfServiceBridging).to.be.false; expect(config.bridge.disableJoinLeaveNotifications).to.be.true; - expect(config.logging.console, "warn"); + expect(config.logging.console).to.equal("warn"); }); it("should merge logging.files correctly", () => { const config = new DiscordBridgeConfig(); @@ -58,6 +58,6 @@ describe("DiscordBridgeConfig.ApplyConfig", () => { ], }, }); - expect(config.logging.files[0].file, "./bacon.log"); + expect(config.logging.files[0].file).to.equal("./bacon.log"); }); }); diff --git a/test/test_discordmessageprocessor.ts b/test/test_discordmessageprocessor.ts index f78d66ab..d66dd949 100644 --- a/test/test_discordmessageprocessor.ts +++ b/test/test_discordmessageprocessor.ts @@ -58,8 +58,8 @@ describe("DiscordMessageProcessor", () => { msg.embeds = []; msg.content = "Hello World!"; const result = await processor.FormatMessage(msg); - Chai.assert(result.body, "Hello World!"); - Chai.assert(result.formattedBody, "Hello World!"); + Chai.assert.equal(result.body, "Hello World!"); + Chai.assert.equal(result.formattedBody, "Hello World!"); }); it("processes markdown messages correctly.", async () => { const processor = new DiscordMessageProcessor( From 45d2bf8f45b923a13035efa10d996e7e22484e0e Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 5 Jun 2019 00:29:30 +0200 Subject: [PATCH 2/3] add config override using env variables Signed-off-by: pacien --- src/config.ts | 48 ++++++++++++++++++++++++++++++++++++++------- src/discordas.ts | 3 ++- test/test_config.ts | 29 ++++++++++++++++++++++++--- tools/chanfix.ts | 3 ++- tools/ghostfix.ts | 3 ++- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/config.ts b/src/config.ts index 16660431..2f0eb762 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +const ENV_PREFIX = "APPSERVICE_DISCORD"; +const ENV_KEY_SEPARATOR = "_"; +const ENV_VAL_SEPARATOR = ","; + /** Type annotations for config/config.schema.yaml */ export class DiscordBridgeConfig { public bridge: DiscordBridgeConfigBridge = new DiscordBridgeConfigBridge(); @@ -27,18 +31,48 @@ export class DiscordBridgeConfig { /** * Apply a set of keys and values over the default config. - * @param _config Config keys + * @param newConfig Config keys * @param configLayer Private parameter */ // tslint:disable-next-line no-any - public ApplyConfig(newConfig: {[key: string]: any}, configLayer: any = this) { + public applyConfig(newConfig: {[key: string]: any}, configLayer: {[key: string]: any} = this) { Object.keys(newConfig).forEach((key) => { - if ( typeof(configLayer[key]) === "object" && - !Array.isArray(configLayer[key])) { - this.ApplyConfig(newConfig[key], this[key]); - return; + if (configLayer[key] instanceof Object && !(configLayer[key] instanceof Array)) { + this.applyConfig(newConfig[key], configLayer[key]); + } else { + configLayer[key] = newConfig[key]; + } + }); + } + + /** + * Override configuration keys defined in the supplied environment dictionary. + * @param environment environment variable dictionary + * @param path private parameter: config layer path determining the environment key prefix + * @param configLayer private parameter: current layer of configuration to alter recursively + */ + public applyEnvironmentOverrides( + // tslint:disable-next-line no-any + environment: {[key: string]: any}, + path: string[] = [ENV_PREFIX], + // tslint:disable-next-line no-any + configLayer: {[key: string]: any} = this, + ) { + Object.keys(configLayer).forEach((key) => { + // camelCase to THICK_SNAKE + const attributeKey = key.replace(/[A-Z]/g, (prefix) => `${ENV_KEY_SEPARATOR}${prefix}`).toUpperCase(); + const attributePath = path.concat([attributeKey]); + + if (configLayer[key] instanceof Object && !(configLayer[key] instanceof Array)) { + this.applyEnvironmentOverrides(environment, attributePath, configLayer[key]); + } else { + const lookupKey = attributePath.join(ENV_KEY_SEPARATOR); + if (lookupKey in environment) { + configLayer[key] = (configLayer[key] instanceof Array) + ? environment[lookupKey].split(ENV_VAL_SEPARATOR) + : environment[lookupKey]; + } } - configLayer[key] = newConfig[key]; }); } } diff --git a/src/discordas.ts b/src/discordas.ts index 0f4b5e11..1f350c38 100644 --- a/src/discordas.ts +++ b/src/discordas.ts @@ -59,7 +59,8 @@ type callbackFn = (...args: any[]) => Promise; async function run(port: number, fileConfig: DiscordBridgeConfig) { const config = new DiscordBridgeConfig(); - config.ApplyConfig(fileConfig); + config.applyConfig(fileConfig); + config.applyEnvironmentOverrides(process.env); Log.Configure(config.logging); log.info("Starting Discord AS"); const yamlConfig = yaml.safeLoad(fs.readFileSync(cli.opts.registrationPath, "utf8")); diff --git a/test/test_config.ts b/test/test_config.ts index 26b037ef..badb662a 100644 --- a/test/test_config.ts +++ b/test/test_config.ts @@ -22,10 +22,10 @@ import { DiscordBridgeConfig } from "../src/config"; const expect = Chai.expect; -describe("DiscordBridgeConfig.ApplyConfig", () => { +describe("DiscordBridgeConfig.applyConfig", () => { it("should merge configs correctly", () => { const config = new DiscordBridgeConfig(); - config.ApplyConfig({ + config.applyConfig({ bridge: { disableDeletionForwarding: true, disableDiscordMentions: false, @@ -46,9 +46,32 @@ describe("DiscordBridgeConfig.ApplyConfig", () => { expect(config.bridge.disableJoinLeaveNotifications).to.be.true; expect(config.logging.console).to.equal("warn"); }); + it("should merge environment overrides correctly", () => { + const config = new DiscordBridgeConfig(); + config.applyConfig({ + bridge: { + disableDeletionForwarding: true, + disableDiscordMentions: false, + homeserverUrl: "blah", + }, + logging: { + console: "warn", + }, + }); + config.applyEnvironmentOverrides({ + APPSERVICE_DISCORD_BRIDGE_DISABLE_DELETION_FORWARDING: false, + APPSERVICE_DISCORD_BRIDGE_DISABLE_JOIN_LEAVE_NOTIFICATIONS: true, + APPSERVICE_DISCORD_LOGGING_CONSOLE: "debug", + }); + expect(config.bridge.disableJoinLeaveNotifications).to.be.true; + expect(config.bridge.disableDeletionForwarding).to.be.false; + expect(config.bridge.disableDiscordMentions).to.be.false; + expect(config.bridge.homeserverUrl).to.equal("blah"); + expect(config.logging.console).to.equal("debug"); + }); it("should merge logging.files correctly", () => { const config = new DiscordBridgeConfig(); - config.ApplyConfig({ + config.applyConfig({ logging: { console: "silent", files: [ diff --git a/tools/chanfix.ts b/tools/chanfix.ts index 8d4f4abf..a3f50952 100644 --- a/tools/chanfix.ts +++ b/tools/chanfix.ts @@ -67,7 +67,8 @@ if (options.help) { const yamlConfig = yaml.safeLoad(fs.readFileSync("./discord-registration.yaml", "utf8")); const registration = AppServiceRegistration.fromObject(yamlConfig); const config = new DiscordBridgeConfig(); -config.ApplyConfig(yaml.safeLoad(fs.readFileSync(options.config, "utf8")) as DiscordBridgeConfig); +config.applyConfig(yaml.safeLoad(fs.readFileSync(options.config, "utf8")) as DiscordBridgeConfig); +config.applyEnvironmentOverrides(process.env); if (registration === null) { throw new Error("Failed to parse registration file"); diff --git a/tools/ghostfix.ts b/tools/ghostfix.ts index 9ce61d6d..b4e99e9d 100644 --- a/tools/ghostfix.ts +++ b/tools/ghostfix.ts @@ -76,7 +76,8 @@ if (options.help) { const yamlConfig = yaml.safeLoad(fs.readFileSync("./discord-registration.yaml", "utf8")); const registration = AppServiceRegistration.fromObject(yamlConfig); const config = new DiscordBridgeConfig(); -config.ApplyConfig(yaml.safeLoad(fs.readFileSync(options.config, "utf8")) as DiscordBridgeConfig); +config.applyConfig(yaml.safeLoad(fs.readFileSync(options.config, "utf8")) as DiscordBridgeConfig); +config.applyEnvironmentOverrides(process.env); if (registration === null) { throw new Error("Failed to parse registration file"); From 9827b980aaf95f167788588c29b27b509127ef70 Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 5 Jun 2019 14:16:47 +0200 Subject: [PATCH 3/3] add note for config override using env vars Signed-off-by: pacien --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c1ce1e7a..ddfead1d 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ should show up in the network list on Riot and other clients. * For the bot to appear online on Discord you need to run the bridge itself. * ``npm start`` +* Particular configuration keys can be overridden by defining corresponding environment variables. For instance, `auth.botToken` can be set with `APPSERVICE_DISCORD_AUTH_BOT_TOKEN`. [Howto](./docs/howto.md)