diff --git a/ReadMe.md b/ReadMe.md index 7102377..e2581a7 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -34,6 +34,8 @@ The following parameters can be set in config files or in env variables: if provided, it can be either path to private key file or private key content - KAFKA_GROUP_ID: the Kafka group id, default value is 'legacy-challenge-processor' - KAFKA_ERROR_TOPIC: The kafka error topic. +- BUSAPI_URL: Bus API URL +- RETRY_TIMEOUT: The timeout to retry processing the same message - CREATE_CHALLENGE_TOPIC: the create challenge Kafka message topic, default value is 'challenge.notification.create' - UPDATE_CHALLENGE_TOPIC: the update challenge Kafka message topic, default value is 'challenge.notification.update' - AUTH0_URL: Auth0 URL, used to get TC M2M token diff --git a/config/default.js b/config/default.js index d2ca3f9..02ff77b 100644 --- a/config/default.js +++ b/config/default.js @@ -14,6 +14,8 @@ module.exports = { // Kafka group id KAFKA_GROUP_ID: process.env.KAFKA_GROUP_ID || 'legacy-challenge-processor', KAFKA_ERROR_TOPIC: process.env.KAFKA_ERROR_TOPIC || 'common.error.reporting', + BUSAPI_URL: process.env.BUSAPI_URL || 'https://api.topcoder-dev.com/v5', + RETRY_TIMEOUT: process.env.RETRY_TIMEOUT || 10 * 1000, // Topics to listen CREATE_CHALLENGE_TOPIC: process.env.CREATE_CHALLENGE_TOPIC || 'challenge.notification.create', diff --git a/package-lock.json b/package-lock.json index 1b810ad..9d65fc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -325,49 +325,6 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, - "@topcoder-platform/topcoder-bus-api-wrapper": { - "version": "github:topcoder-platform/tc-bus-api-wrapper#f8cbd335a0e0b4d6edd7cae859473593271fd97f", - "from": "github:topcoder-platform/tc-bus-api-wrapper#master", - "requires": { - "joi": "^13.4.0", - "lodash": "^4.17.15", - "superagent": "^3.8.3", - "tc-core-library-js": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151" - }, - "dependencies": { - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - } - }, - "tc-core-library-js": { - "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", - "from": "github:appirio-tech/tc-core-library-js#v2.6.4", - "requires": { - "axios": "^0.19.0", - "bunyan": "^1.8.12", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^1.6.0", - "lodash": "^4.17.15", - "millisecond": "^0.1.2", - "r7insight_node": "^1.8.4", - "request": "^2.88.0" - } - } - } - }, "@types/bluebird": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.0.tgz", @@ -670,14 +627,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, "babel-runtime": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", @@ -2090,29 +2039,6 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2323,11 +2249,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" - }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -2678,14 +2599,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - } - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2841,16 +2754,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, "js-cookie": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", @@ -4175,26 +4078,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" }, - "r7insight_node": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/r7insight_node/-/r7insight_node-1.8.4.tgz", - "integrity": "sha512-6cQrzLkaOxdv/SRFXWRJjgFr8a3nXUOT/4IMFuBv+mWzBnu5DJl+HzONAsWYvclrlZnvfa54PaIPqPuPRSlbrQ==", - "requires": { - "babel-runtime": "6.6.1", - "codependency": "0.1.4", - "json-stringify-safe": "5.0.1", - "lodash": "4.17.15", - "reconnect-core": "1.3.0", - "semver": "5.1.0" - }, - "dependencies": { - "semver": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" - } - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5092,21 +4975,6 @@ "express": "^4.16.3" } }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - }, - "dependencies": { - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - } - } - }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", diff --git a/package.json b/package.json index d70ae18..8e6f900 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@hapi/joi": "^15.0.2", - "@topcoder-platform/topcoder-bus-api-wrapper": "github:topcoder-platform/tc-bus-api-wrapper#master", + "topcoder-bus-api-wrapper": "topcoder-platform/tc-bus-api-wrapper.git", "async-mutex": "^0.1.4", "bluebird": "^3.7.2", "config": "^3.3.1", diff --git a/src/common/helper.js b/src/common/helper.js index e9d4231..844d3b1 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -6,8 +6,13 @@ const _ = require('lodash') const config = require('config') const request = require('superagent') const m2mAuth = require('tc-core-library-js').auth.m2m +const busApi = require('topcoder-bus-api-wrapper') +const constants = require('../constants') const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL'])) +// Bus API Client +let busApiClient + /** * Get Kafka options * @return {Object} the Kafka options @@ -90,11 +95,44 @@ async function postRequest (url, body, m2mToken) { .set('Accept', 'application/json') } +/** + * Get Bus API Client + * @return {Object} Bus API Client Instance + */ +function getBusApiClient () { + // if there is no bus API client instance, then create a new instance + if (!busApiClient) { + busApiClient = busApi(_.pick(config, + ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', + 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', + 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) + } + + return busApiClient +} + +/** + * Post bus event. + * @param {String} topic the event topic + * @param {Object} payload the event payload + */ +async function postBusEvent (topic, payload) { + const client = getBusApiClient() + await client.postEvent({ + topic, + originator: constants.EVENT_ORIGINATOR, + timestamp: new Date().toISOString(), + 'mime-type': constants.EVENT_MIME_TYPE, + payload + }) +} + module.exports = { getKafkaOptions, getM2MToken, patchRequest, getRequest, putRequest, - postRequest + postRequest, + postBusEvent } diff --git a/src/services/ProcessorService.js b/src/services/ProcessorService.js index bb640db..d7163f2 100644 --- a/src/services/ProcessorService.js +++ b/src/services/ProcessorService.js @@ -286,9 +286,22 @@ async function processUpdate (message) { const saveDraftContestDTO = await parsePayload(message.payload, m2mToken, false) logger.debug('Parsed Payload', saveDraftContestDTO) + let challenge try { // ensure challenge existed - const challenge = await getChallengeById(m2mToken, message.payload.legacyId) + challenge = await getChallengeById(m2mToken, message.payload.legacyId) + } catch (e) { + // postponne kafka event + logger.info('Challenge does not exist yet. Will post the same message back to the bus API') + await new Promise((resolve) => { + setTimeout(async () => { + await helper.postBusEvent(config.UPDATE_CHALLENGE_TOPIC, message.payload) + resolve() + }, config.RETRY_TIMEOUT) + }) + return + } + try { if (!challenge) { throw new Error(`Could not find challenge ${message.payload.legacyId}`) }