diff --git a/README.md b/README.md index 0bd5b64..dff6b63 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ npm run lint - GET /users/settings - gets the current user's setup - GET /users/accessToken - gets the user's access token +- GET /health - gets the app health + ## Configuration please see [configuration.md](configuration.md). diff --git a/configuration.md b/configuration.md index b6e5262..9a0f7d1 100644 --- a/configuration.md +++ b/configuration.md @@ -31,6 +31,7 @@ The following config parameters are supported, they are defined in `src/config.j |TOPCODER_AUTH_SECRET| The auth secret used to sign the JWT| No default - needs to be set up| |TOPCODER_VALID_ISSUERS| Stringified array of valid JWT issuers| `'["topcoder-dev.com"]'`| |TOPCODER_JWT_KEY_CACHE_TIME| They JWT cache time | 90 | +|MONGODB_TIMEOUT | The timeout used to check if the app is healthy. |10000 | ## GitHub OAuth App Setup diff --git a/src/common/errors.js b/src/common/errors.js index 1e35d44..e1aae48 100644 --- a/src/common/errors.js +++ b/src/common/errors.js @@ -53,6 +53,13 @@ class ForbiddenError extends ApiError { this.details = details; } } +// The forbidden error +class ServiceUnavailable extends ApiError { + constructor(message, details) { + super(503, 'SERVICE_UNAVAILABLE', message); + this.details = details; + } +} module.exports = { ApiError, @@ -60,4 +67,5 @@ module.exports = { NotFoundError, UnauthorizedError, ForbiddenError, + ServiceUnavailable, }; diff --git a/src/config.js b/src/config.js index 4852dad..71c94bc 100644 --- a/src/config.js +++ b/src/config.js @@ -13,6 +13,7 @@ module.exports = { API_VERSION: process.env.API_VERSION || 'v1', LOG_LEVEL: process.env.LOG_LEVEL || 'info', MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/topcoderx', + MONGODB_TIMEOUT: process.env.MONGODB_TIMEOUT || 10000, // eslint-disable-line no-magic-numbers SESSION_SECRET: process.env.SESSION_SECRET || 'kjsdfkj34857', // Github and gitlab client id and secret GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID || 'ae39bea2a2a23f1dd032', diff --git a/src/controllers/AppHealthController.js b/src/controllers/AppHealthController.js new file mode 100644 index 0000000..0469a1f --- /dev/null +++ b/src/controllers/AppHealthController.js @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 TopCoder, Inc. All rights reserved. + */ + +/** + * This controller exposes application health related endpoints. + * + * @author Thomas Kranitsas + * @version 1.0 + */ +const helper = require('../common/helper'); +const AppHealthService = require('../services/AppHealthService'); + +/** + * gets the application health + * @returns {Object} the health details + */ +async function getAppHealth() { + return await AppHealthService.getAppHealth(); +} + +module.exports = { + getAppHealth, +}; + +helper.buildController(module.exports); diff --git a/src/routes.js b/src/routes.js index 2325d39..d3bc669 100644 --- a/src/routes.js +++ b/src/routes.js @@ -208,4 +208,10 @@ module.exports = { method: 'getAppConfig', }, }, + '/health': { + get: { + controller: 'AppHealthController', + method: 'getAppHealth', + }, + }, }; diff --git a/src/services/AppHealthService.js b/src/services/AppHealthService.js new file mode 100644 index 0000000..c98b947 --- /dev/null +++ b/src/services/AppHealthService.js @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 TopCoder, Inc. All rights reserved. + */ + +/** + * This service will provide app health related operations. + * + * @author Thomas Kranitsas + * @version 1.0 + */ +const config = require('../config'); +const helper = require('../common/helper'); +const errors = require('../common/errors'); +const User = require('../models').User; + +/** + * gets the application health + * @returns {Object} the health details + */ +async function getAppHealth() { + const checkMongoDB = new Promise((resolve, reject) => { + User.findOne({}, (err, data) => { + if (err) { + return reject(new errors.ServiceUnavailable('MongoDB instance cannot be reached')); + } + return resolve(); + }); + }); + + const timeOutBreak = new Promise((resolve, reject) => { + setTimeout(reject, config.MONGODB_TIMEOUT, new errors.ServiceUnavailable('MongoDB instance cannot be reached')); + }); + + await Promise.race([checkMongoDB, timeOutBreak]); + + return { + checksRun: 1, + }; +} + +module.exports = { + getAppHealth, +}; + +helper.buildService(module.exports);