From 76fafe330519cfdec566f9c68878d63da8930eb2 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Fri, 15 Dec 2017 19:30:35 +0100 Subject: [PATCH] Add missing src/integration/impl/circleci for the CircleCI integration Signed-off-by: MTRNord --- src/integration/impl/circleci/CircleCiBot.js | 53 +++++++++ .../impl/circleci/CircleCiFactory.js | 20 ++++ .../impl/circleci/StubbedCircleCiBackbone.js | 63 ++++++++++ .../impl/circleci/VectorCircleCiBackbone.js | 108 ++++++++++++++++++ src/integration/impl/index.js | 2 + 5 files changed, 246 insertions(+) create mode 100644 src/integration/impl/circleci/CircleCiBot.js create mode 100644 src/integration/impl/circleci/CircleCiFactory.js create mode 100644 src/integration/impl/circleci/StubbedCircleCiBackbone.js create mode 100644 src/integration/impl/circleci/VectorCircleCiBackbone.js diff --git a/src/integration/impl/circleci/CircleCiBot.js b/src/integration/impl/circleci/CircleCiBot.js new file mode 100644 index 0000000..0a70bfd --- /dev/null +++ b/src/integration/impl/circleci/CircleCiBot.js @@ -0,0 +1,53 @@ +var ComplexBot = require("../../generic_types/ComplexBot"); + +/** + * Represents a CircleCI bot + */ +class CircleCiBot extends ComplexBot { + + /** + * Creates a new CircleCI bot + * @param botConfig the bot configuration + * @param backbone the backbone powering this bot + */ + constructor(botConfig, backbone) { + super(botConfig); + this._backbone = backbone; + } + + /*override*/ + getUserId() { + return this._backbone.getUserId(); + } + + /*override*/ + getState() { + var response = { + repoTemplates: [], + immutableRepoTemplates: [], + webhookUrl: "" + }; + return this._backbone.getRepos().then(templates => { + response.repoTemplates = templates; + return this._backbone.getImmutableRepos(); + }).then(immutable => { + response.immutableRepoTemplates = immutable; + return this._backbone.getWebhookUrl(); + }).then(url => { + response.webhookUrl = url; + return response; + }); + } + + /*override*/ + removeFromRoom(roomId) { + return this._backbone.removeFromRoom(roomId); + } + + /*override*/ + updateState(newState) { + return this._backbone.setRepos(newState.repoTemplates).then(() => this.getState()); + } +} + +module.exports = CircleCiBot; \ No newline at end of file diff --git a/src/integration/impl/circleci/CircleCiFactory.js b/src/integration/impl/circleci/CircleCiFactory.js new file mode 100644 index 0000000..c3e7fa7 --- /dev/null +++ b/src/integration/impl/circleci/CircleCiFactory.js @@ -0,0 +1,20 @@ +var CircleCiBot = require("./CircleCiBot"); +var VectorCircleCiBackbone = require("./VectorCircleCiBackbone"); +var UpstreamConfiguration = require("../../../UpstreamConfiguration"); + +var factory = (db, integrationConfig, roomId, scalarToken) => { + factory.validateConfig(integrationConfig); + + return db.getUpstreamToken(scalarToken).then(upstreamToken => { + var backbone = new VectorCircleCiBackbone(roomId, upstreamToken); + return new CircleCiBot(integrationConfig, backbone); + }); +}; + +factory.validateConfig = (integrationConfig) => { + if (!integrationConfig.upstream) throw new Error("Unsupported configuration"); + if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream"); + if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified"); +}; + +module.exports = factory; \ No newline at end of file diff --git a/src/integration/impl/circleci/StubbedCircleCiBackbone.js b/src/integration/impl/circleci/StubbedCircleCiBackbone.js new file mode 100644 index 0000000..d95255b --- /dev/null +++ b/src/integration/impl/circleci/StubbedCircleCiBackbone.js @@ -0,0 +1,63 @@ +/** + * Stubbed/placeholder CircleCI backbone + */ +class StubbedCircleCiBackbone { + + /** + * Creates a new stubbed RSS backbone + */ + constructor() { + } + + /** + * Gets the user ID for this backbone + * @returns {Promise} resolves to the user ID + */ + getUserId() { + throw new Error("Not implemented"); + } + + /** + * Gets the repository templates for this backbone + * @returns {Promise<{repoKey:string,template:string}[]>} resolves to the collection of repositories and their templates + */ + getRepos() { + throw new Error("Not implemented"); + } + + /** + * Gets the immutable repository templates for this backbone (set by other users) + * @returns {Promise<{repoKey:string,template:string,ownerId:string}[]>} resolves to the collection of repositories and their templates + */ + getImmutableRepos() { + throw new Error("Not implemented"); + } + + /** + * Sets the new repository templates for this backbone + * @param {{repoKey:string,template:string}[]} newRepos the new templates for the repositories + * @returns {Promise<>} resolves when complete + */ + setRepos(newRepos) { + throw new Error("Not implemented"); + } + + /** + * Gets the webhook url for this backbone + * @returns {Promise} resolves to the webhook URL + */ + getWebhookUrl() { + throw new Error("Not implemented"); + } + + /** + * Removes the bot from the given room + * @param {string} roomId the room ID to remove the bot from + * @returns {Promise<>} resolves when completed + */ + removeFromRoom(roomId) { + throw new Error("Not implemented"); + } +} + +module.exports = StubbedCircleCiBackbone; \ No newline at end of file diff --git a/src/integration/impl/circleci/VectorCircleCiBackbone.js b/src/integration/impl/circleci/VectorCircleCiBackbone.js new file mode 100644 index 0000000..307ea11 --- /dev/null +++ b/src/integration/impl/circleci/VectorCircleCiBackbone.js @@ -0,0 +1,108 @@ +var StubbedCircleCiBackbone = require("./StubbedCircleCiBackbone"); +var VectorScalarClient = require("../../../scalar/VectorScalarClient"); +var _ = require("lodash"); +var log = require("../../../util/LogService"); + +/** + * Backbone for Travis CI bots running on vector.im through scalar + */ +class VectorCircleCiBackbone extends StubbedCircleCiBackbone { + + /** + * Creates a new Vector CircleCI backbone + * @param {string} roomId the room ID to manage + * @param {string} upstreamScalarToken the vector scalar token + */ + constructor(roomId, upstreamScalarToken) { + super(); + this._roomId = roomId; + this._scalarToken = upstreamScalarToken; + this._info = null; + this._otherTemplates = []; + } + + /*override*/ + getUserId() { + return (this._info ? Promise.resolve() : this._getInfo()).then(() => { + return this._info.bot_user_id; + }); + } + + /*override*/ + getRepos() { + return (this._info ? Promise.resolve() : this._getInfo()).then(() => { + if (this._info.integrations.length == 0) return []; + + var rooms = _.keys(this._info.integrations[0].config.rooms); + if (rooms.indexOf(this._roomId) === -1) return []; + + var repos = _.keys(this._info.integrations[0].config.rooms[this._roomId].repos); + return _.map(repos, r => { + return {repoKey: r, template: this._info.integrations[0].config.rooms[this._roomId].repos[r].template}; + }); + }); + } + + /*override*/ + getImmutableRepos() { + return (this._info ? Promise.resolve() : this._getInfo()).then(() => { + return this._otherTemplates; + }); + } + + /*override*/ + setRepos(newRepos) { + var config = {}; + config[this._roomId] = {repos: {}}; + for (var repo of newRepos) config[this._roomId].repos[repo.repoKey] = {template: repo.template}; + + return VectorScalarClient.configureIntegration("circleci", this._scalarToken, { + rooms: config + }); + } + + /*override*/ + getWebhookUrl() { + // string + return (this._info ? Promise.resolve() : this._getInfo()).then(() => { + if (this._info.integrations.length == 0) return ""; + return this._info.integrations[0].config.webhook_url; + }); + } + + _getInfo() { + return VectorScalarClient.getIntegrationsForRoom(this._roomId, this._scalarToken).then(integrations => { + this._otherTemplates = []; + for (var integration of integrations) { + if (integration.self) continue; // skip - we're not looking for ones we know about + if (integration.type == "circleci") { + var roomIds = _.keys(integration.config.rooms); + if (roomIds.length === 0) continue; + if (roomIds.length !== 1) log.warn("VectorCircleCiBackbone", "Expected 1 room but found " + roomIds.length); + + var roomConfig = integration.config.rooms[roomIds[0]]; + var repositories = _.keys(roomConfig.repos); + + for (var repo of repositories) { + this._otherTemplates.push({ + repoKey: repo, + template: roomConfig.repos[repo].template, + ownerId: integration.user_id + }); + } + } + } + + return VectorScalarClient.getIntegration("circleci", this._roomId, this._scalarToken); + }).then(info => { + this._info = info; + }); + } + + /*override*/ + removeFromRoom(roomId) { + return VectorScalarClient.removeIntegration("circleci", roomId, this._scalarToken); + } +} + +module.exports = VectorCircleCiBackbone; \ No newline at end of file diff --git a/src/integration/impl/index.js b/src/integration/impl/index.js index d3be9ea..92d7e9b 100644 --- a/src/integration/impl/index.js +++ b/src/integration/impl/index.js @@ -4,12 +4,14 @@ var SimpleBotFactory = require("./simple_bot/SimpleBotFactory"); var RSSFactory = require("./rss/RSSFactory"); var IRCFactory = require("./irc/IRCFactory"); var TravisCiFactory = require("./travisci/TravisCiFactory"); +var CircleCiFactory = require("./circleci/CircleCiFactory"); var SimpleWidgetFactory = require("./simple_widget/SimpleWidgetFactory"); var mapping = { "complex-bot": { "rss": RSSFactory, "travisci": TravisCiFactory, + "circleci": CircleCiFactory, }, "bridge": { "irc": IRCFactory,