[BREAKING] Allow possibility of disabling Scalar upstream
This is a breaking change because of the structure change for upstreams. Instead of being an object, it is now a list. Existing configurations are not guaranteed to work. Adds #108 and starts work on #22 (upstream config).
This commit is contained in:
parent
1138dc3c0a
commit
02c981b070
16 changed files with 196 additions and 55 deletions
|
@ -22,4 +22,5 @@ demobot:
|
||||||
|
|
||||||
# Upstream configuration. This should almost never change.
|
# Upstream configuration. This should almost never change.
|
||||||
upstreams:
|
upstreams:
|
||||||
vector: "https://scalar.vector.im/api"
|
- name: vector
|
||||||
|
url: "https://scalar.vector.im/api"
|
|
@ -32,7 +32,11 @@ class DimensionApi {
|
||||||
var factory = IntegrationImpl.getFactory(integrationConfig);
|
var factory = IntegrationImpl.getFactory(integrationConfig);
|
||||||
if (!factory) throw new Error("Missing config factory for " + integrationConfig.name);
|
if (!factory) throw new Error("Missing config factory for " + integrationConfig.name);
|
||||||
|
|
||||||
|
try {
|
||||||
return factory(this._db, integrationConfig, roomId, scalarToken);
|
return factory(this._db, integrationConfig, roomId, scalarToken);
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error("Error using factory for " + integrationConfig.name + ". Please either fix the integration settings or disable the integration.", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getIntegrations(req, res) {
|
_getIntegrations(req, res) {
|
||||||
|
@ -49,7 +53,9 @@ class DimensionApi {
|
||||||
var integrations = _.map(Integrations.all, i => JSON.parse(JSON.stringify(i))); // clone
|
var integrations = _.map(Integrations.all, i => JSON.parse(JSON.stringify(i))); // clone
|
||||||
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
|
var remove = [];
|
||||||
_.forEach(integrations, integration => {
|
_.forEach(integrations, integration => {
|
||||||
|
try {
|
||||||
promises.push(this._getIntegration(integration, roomId, scalarToken).then(builtIntegration => {
|
promises.push(this._getIntegration(integration, roomId, scalarToken).then(builtIntegration => {
|
||||||
return builtIntegration.getState().then(state => {
|
return builtIntegration.getState().then(state => {
|
||||||
var keys = _.keys(state);
|
var keys = _.keys(state);
|
||||||
|
@ -62,8 +68,19 @@ class DimensionApi {
|
||||||
integration.userId = userId;
|
integration.userId = userId;
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
remove.push(integration);
|
||||||
|
log.error("DimensionApi", err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (var toRemove of remove) {
|
||||||
|
var idx = integrations.indexOf(toRemove);
|
||||||
|
if (idx === -1) continue;
|
||||||
|
log.warn("DimensionApi", "Disabling integration " + toRemove.name +" due to an error encountered in setup");
|
||||||
|
integrations.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
Promise.all(promises).then(() => res.send(_.map(integrations, integration => {
|
Promise.all(promises).then(() => res.send(_.map(integrations, integration => {
|
||||||
// Remove sensitive material
|
// Remove sensitive material
|
||||||
integration.upstream = undefined;
|
integration.upstream = undefined;
|
||||||
|
|
|
@ -4,6 +4,7 @@ var ScalarClient = require("./scalar/ScalarClient.js");
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
var log = require("./util/LogService");
|
var log = require("./util/LogService");
|
||||||
var Promise = require("bluebird");
|
var Promise = require("bluebird");
|
||||||
|
var UpstreamConfiguration = require("./UpstreamConfiguration");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API handler for the Scalar API, as required by Riot
|
* API handler for the Scalar API, as required by Riot
|
||||||
|
@ -33,7 +34,11 @@ class ScalarApi {
|
||||||
if (!token) res.sendStatus(400);
|
if (!token) res.sendStatus(400);
|
||||||
else this._db.checkToken(token).then(() => {
|
else this._db.checkToken(token).then(() => {
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
}).catch(() => res.sendStatus(401));
|
}).catch(e => {
|
||||||
|
res.sendStatus(401);
|
||||||
|
log.warn("ScalarApi", "Failed to authenticate token");
|
||||||
|
log.verbose("ScalarApi", e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_scalarRegister(req, res) {
|
_scalarRegister(req, res) {
|
||||||
|
@ -51,6 +56,9 @@ class ScalarApi {
|
||||||
client.getSelfMxid().then(mxid => {
|
client.getSelfMxid().then(mxid => {
|
||||||
userId = mxid;
|
userId = mxid;
|
||||||
if (!mxid) throw new Error("Token does not resolve to a matrix user");
|
if (!mxid) throw new Error("Token does not resolve to a matrix user");
|
||||||
|
|
||||||
|
// TODO: Make this part more generic for other upstreams (#22)
|
||||||
|
if (!UpstreamConfiguration.hasUpstream("vector")) return Promise.resolve(null);
|
||||||
return ScalarClient.register(tokenInfo);
|
return ScalarClient.register(tokenInfo);
|
||||||
}).then(upstreamToken => {
|
}).then(upstreamToken => {
|
||||||
return this._db.createToken(userId, tokenInfo, scalarToken, upstreamToken);
|
return this._db.createToken(userId, tokenInfo, scalarToken, upstreamToken);
|
||||||
|
|
50
src/UpstreamConfiguration.js
Normal file
50
src/UpstreamConfiguration.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
var LogService = require("./util/LogService");
|
||||||
|
var _ = require("lodash");
|
||||||
|
var config = require("config");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles all upstream configuration information, such as URLs, tokens, and whether or not they are enabled.
|
||||||
|
*/
|
||||||
|
class UpstreamConfiguration {
|
||||||
|
/**
|
||||||
|
* Creates a new upstream configuration handler
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this._upstreams = {};
|
||||||
|
this._loadUpstreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadUpstreams() {
|
||||||
|
for (var upstream of config.upstreams) {
|
||||||
|
var upstreamConfig = upstream;
|
||||||
|
|
||||||
|
if (this._upstreams[upstream.name]) {
|
||||||
|
LogService.warn("UpstreamConfiguration", "Duplicate upstream " + upstream.name +" - skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._upstreams[upstream.name] = upstreamConfig;
|
||||||
|
LogService.info("UpstreamConfiguration", "Loaded upstream '" + upstream.name + "' as: " + JSON.stringify(upstreamConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a particular upstream exists
|
||||||
|
* @param {string} name the name of the upstream
|
||||||
|
* @returns {boolean} true if it is enabled and exists
|
||||||
|
*/
|
||||||
|
hasUpstream(name) {
|
||||||
|
return !!this._upstreams[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an upstream's configuration
|
||||||
|
* @param {string} name the upstream name
|
||||||
|
* @returns {{url:string}} the upstream configuration
|
||||||
|
*/
|
||||||
|
getUpstream(name) {
|
||||||
|
return _.clone(this._upstreams[name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new UpstreamConfiguration();
|
|
@ -8,6 +8,13 @@ var IntegrationStub = require("../generic_types/IntegrationStub");
|
||||||
* @param {string} scalarToken the scalar token
|
* @param {string} scalarToken the scalar token
|
||||||
* @returns {Promise<*>} resolves to the configured integration
|
* @returns {Promise<*>} resolves to the configured integration
|
||||||
*/
|
*/
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
return Promise.resolve(new IntegrationStub(integrationConfig));
|
return Promise.resolve(new IntegrationStub(integrationConfig));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
factory.validateConfig = (integrationConfig) => {
|
||||||
|
// Nothing to do
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = factory;
|
|
@ -19,7 +19,7 @@ class IRCApi {
|
||||||
* @param {DimensionStore} db the store to use
|
* @param {DimensionStore} db the store to use
|
||||||
*/
|
*/
|
||||||
bootstrap(app, db) {
|
bootstrap(app, db) {
|
||||||
if (!Integrations.byType["bridge"]["irc"]) {
|
if (!Integrations.byType["bridge"] || !Integrations.byType["bridge"]["irc"]) {
|
||||||
log.info("IRCApi", "IRC Bridge not enabled - not setting up the API");
|
log.info("IRCApi", "IRC Bridge not enabled - not setting up the API");
|
||||||
return;
|
return;
|
||||||
} else log.info("IRCApi", "Setting up IRC API");
|
} else log.info("IRCApi", "Setting up IRC API");
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
var IRCBridge = require("./IRCBridge");
|
var IRCBridge = require("./IRCBridge");
|
||||||
var VectorIrcBackbone = require("./VectorIrcBackbone");
|
var VectorIrcBackbone = require("./VectorIrcBackbone");
|
||||||
|
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
|
||||||
|
|
||||||
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
|
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
|
||||||
if (integrationConfig.upstream) {
|
|
||||||
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
|
||||||
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
||||||
var backbone = new VectorIrcBackbone(roomId, upstreamToken);
|
var backbone = new VectorIrcBackbone(roomId, upstreamToken);
|
||||||
return new IRCBridge(integrationConfig, backbone);
|
return new IRCBridge(integrationConfig, backbone);
|
||||||
});
|
});
|
||||||
} else throw new Error("Unsupported config");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
|
@ -1,12 +1,20 @@
|
||||||
var RSSBot = require("./RSSBot");
|
var RSSBot = require("./RSSBot");
|
||||||
var VectorRssBackbone = require("./VectorRssBackbone");
|
var VectorRssBackbone = require("./VectorRssBackbone");
|
||||||
|
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
|
||||||
|
|
||||||
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
|
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
|
||||||
if (integrationConfig.upstream) {
|
|
||||||
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
|
||||||
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
||||||
var backbone = new VectorRssBackbone(roomId, upstreamToken);
|
var backbone = new VectorRssBackbone(roomId, upstreamToken);
|
||||||
return new RSSBot(integrationConfig, backbone);
|
return new RSSBot(integrationConfig, backbone);
|
||||||
});
|
});
|
||||||
} else throw new Error("Unsupported config");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
|
@ -1,10 +1,12 @@
|
||||||
var SimpleBot = require("./SimpleBot");
|
var SimpleBot = require("./SimpleBot");
|
||||||
var VectorSimpleBackbone = require("./VectorSimpleBackbone");
|
var VectorSimpleBackbone = require("./VectorSimpleBackbone");
|
||||||
var HostedSimpleBackbone = require("./HostedSimpleBackbone");
|
var HostedSimpleBackbone = require("./HostedSimpleBackbone");
|
||||||
|
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
|
||||||
|
|
||||||
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
|
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
|
||||||
if (integrationConfig.upstream) {
|
if (integrationConfig.upstream) {
|
||||||
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
|
||||||
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
||||||
var backbone = new VectorSimpleBackbone(integrationConfig, upstreamToken);
|
var backbone = new VectorSimpleBackbone(integrationConfig, upstreamToken);
|
||||||
return new SimpleBot(integrationConfig, backbone);
|
return new SimpleBot(integrationConfig, backbone);
|
||||||
|
@ -12,5 +14,14 @@ module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
} else if (integrationConfig.hosted) {
|
} else if (integrationConfig.hosted) {
|
||||||
var backbone = new HostedSimpleBackbone(integrationConfig);
|
var backbone = new HostedSimpleBackbone(integrationConfig);
|
||||||
return Promise.resolve(new SimpleBot(integrationConfig, backbone));
|
return Promise.resolve(new SimpleBot(integrationConfig, backbone));
|
||||||
} else throw new Error("Unsupported config");
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
factory.validateConfig = (integrationConfig) => {
|
||||||
|
if (integrationConfig.upstream) {
|
||||||
|
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
||||||
|
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
|
||||||
|
} else if (!integrationConfig.hosted) throw new Error("Unsupported configuration");
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = factory;
|
|
@ -1,6 +1,13 @@
|
||||||
var SimpleWidget = require("./SimpleWidget");
|
var SimpleWidget = require("./SimpleWidget");
|
||||||
var Promise = require("bluebird");
|
var Promise = require("bluebird");
|
||||||
|
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
return Promise.resolve(new SimpleWidget(integrationConfig, roomId));
|
return Promise.resolve(new SimpleWidget(integrationConfig, roomId));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
factory.validateConfig = (integrationConfig) => {
|
||||||
|
// Nothing to do
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = factory;
|
|
@ -1,12 +1,20 @@
|
||||||
var TravisCiBot = require("./TravisCiBot");
|
var TravisCiBot = require("./TravisCiBot");
|
||||||
var VectorTravisCiBackbone = require("./VectorTravisCiBackbone");
|
var VectorTravisCiBackbone = require("./VectorTravisCiBackbone");
|
||||||
|
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
|
||||||
|
|
||||||
|
var factory = (db, integrationConfig, roomId, scalarToken) => {
|
||||||
|
factory.validateConfig(integrationConfig);
|
||||||
|
|
||||||
module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
|
||||||
if (integrationConfig.upstream) {
|
|
||||||
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
|
||||||
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
||||||
var backbone = new VectorTravisCiBackbone(roomId, upstreamToken);
|
var backbone = new VectorTravisCiBackbone(roomId, upstreamToken);
|
||||||
return new TravisCiBot(integrationConfig, backbone);
|
return new TravisCiBot(integrationConfig, backbone);
|
||||||
});
|
});
|
||||||
} else throw new Error("Unsupported config");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
|
@ -3,6 +3,7 @@ var log = require("../util/LogService");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
var IntegrationImpl = require("./impl");
|
||||||
|
|
||||||
log.info("Integrations", "Discovering integrations");
|
log.info("Integrations", "Discovering integrations");
|
||||||
|
|
||||||
|
@ -43,6 +44,19 @@ for (var key of keys) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var factory = IntegrationImpl.getFactory(merged);
|
||||||
|
if (!factory) {
|
||||||
|
log.warn("Integrations", "Integration " + key + " does not have an associated factory - skipping");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
factory.validateConfig(merged);
|
||||||
|
} catch (err) {
|
||||||
|
log.error("Integrations", "Error while validating integration " + key + " - skipping");
|
||||||
|
log.error("Integrations", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
linear.push(merged);
|
linear.push(merged);
|
||||||
if (merged['userId'])
|
if (merged['userId'])
|
||||||
byUserId[merged['userId']] = merged;
|
byUserId[merged['userId']] = merged;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var log = require("../util/LogService");
|
var log = require("../util/LogService");
|
||||||
var config = require("config");
|
var config = require("config");
|
||||||
|
var UpstreamConfiguration = require("../UpstreamConfiguration");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a scalar client
|
* Represents a scalar client
|
||||||
|
@ -32,7 +33,7 @@ class ScalarClient {
|
||||||
// TODO: Merge this, VectorScalarClient, and MatrixLiteClient into a base class
|
// TODO: Merge this, VectorScalarClient, and MatrixLiteClient into a base class
|
||||||
_do(method, endpoint, qs = null, body = null) {
|
_do(method, endpoint, qs = null, body = null) {
|
||||||
// TODO: Generify URL
|
// TODO: Generify URL
|
||||||
var url = config.get("upstreams.vector") + endpoint;
|
var url = UpstreamConfiguration.getUpstream("vector").url + endpoint;
|
||||||
|
|
||||||
log.verbose("ScalarClient", "Performing request: " + url);
|
log.verbose("ScalarClient", "Performing request: " + url);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var log = require("../util/LogService");
|
var log = require("../util/LogService");
|
||||||
var config = require("config");
|
var config = require("config");
|
||||||
|
var UpstreamConfiguration = require("../UpstreamConfiguration");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a scalar client for vector.im
|
* Represents a scalar client for vector.im
|
||||||
|
@ -234,7 +235,7 @@ class VectorScalarClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
_do(method, endpoint, qs = null, body = null) {
|
_do(method, endpoint, qs = null, body = null) {
|
||||||
var url = config.get("upstreams.vector") + endpoint;
|
var url = UpstreamConfiguration.getUpstream("vector").url + endpoint;
|
||||||
|
|
||||||
log.verbose("VectorScalarClient", "Performing request: " + url);
|
log.verbose("VectorScalarClient", "Performing request: " + url);
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class DimensionStore {
|
||||||
* @param {string} mxid the matrix user id
|
* @param {string} mxid the matrix user id
|
||||||
* @param {OpenID} openId the open ID
|
* @param {OpenID} openId the open ID
|
||||||
* @param {string} scalarToken the token associated with the user
|
* @param {string} scalarToken the token associated with the user
|
||||||
* @param {string} upstreamToken the upstream scalar token
|
* @param {String?} upstreamToken the upstream scalar token (optional)
|
||||||
* @returns {Promise<>} resolves when complete
|
* @returns {Promise<>} resolves when complete
|
||||||
*/
|
*/
|
||||||
createToken(mxid, openId, scalarToken, upstreamToken) {
|
createToken(mxid, openId, scalarToken, upstreamToken) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
},
|
},
|
||||||
upstreamToken: {
|
upstreamToken: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: true,
|
||||||
field: 'upstreamToken'
|
field: 'upstreamToken'
|
||||||
},
|
},
|
||||||
expires: {
|
expires: {
|
||||||
|
|
Loading…
Reference in a new issue