From e3904829c1a962dfbe504fc67db98e69efe0c6dc Mon Sep 17 00:00:00 2001 From: Mike Senn Date: Sun, 26 Feb 2023 09:54:45 -0500 Subject: [PATCH] Changes to get staging environment working While setting up the staging environment, I made these changes. Some are strictly necessary, like the namespace prefixing on fields. Some helped me find those issues, like returning errors from event handlers and logging more details in createWorkItem. ### Medium changes (connection.js and services/Gus/*) Keep GUS api sessions alive for 10 minutes. And add debug logging for SOQL requests (connection.js) Implement namespace prefixes on objects and fields. This should have been implemented when the app started supporting Agile Accelerator, which always has a namespace; not sure why this wasn't done already. (GithubEvents/index.js and ghEvents.js) Wait for event handlers to return before returning from the github webhook. Previously webhooks would return synchronously, before any GUS callouts would be processed. Now, GUS callouts will delay the webhook response and can affect the 2xx/5xx status code. This will help track errors and latency through logs. ### Small changes Add missing peer dependency 'winston' Add logs in createWorkItem that helped diagnose an issue with bug priorities. (GithubEvents/index.js) Use captureRejectionSymbol to mitigate an unhandled promise rejection and avoid app crash. Related to #161. --- README.md | 6 +- api/actions/__test__/createWorkItem.spec.js | 4 +- .../__test__/createWorkItemForPR.spec.js | 3 +- api/actions/createWorkItem.js | 59 +++++++++++-------- api/actions/createWorkItemForPR.js | 7 ++- api/controllers/GithubController.js | 2 +- api/modules/GithubEvents/index.js | 22 +++++-- api/services/Gus/closeWorkItem.js | 20 ++----- api/services/Gus/connection.js | 43 ++++++++++++++ api/services/Gus/createChangelistInGus.js | 42 ++++++------- api/services/Gus/createComment.js | 34 +++++------ api/services/Gus/createWorkItemInGus.js | 32 ++++------ api/services/Gus/getById.js | 16 +---- api/services/Gus/getByRelatedUrl.js | 18 ++---- api/services/Gus/getWorkItemIdByName.js | 18 ++---- api/services/Gus/index.js | 9 ++- api/services/Gus/resolveBuild.js | 38 +++++------- config/env/production.js | 2 +- config/ghEvents.js | 9 +-- package.json | 1 + yarn.lock | 2 +- 21 files changed, 198 insertions(+), 189 deletions(-) create mode 100644 api/services/Gus/connection.js diff --git a/README.md b/README.md index ed7a330..dac7077 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ smee -u --path /webhook --port 1337 - Get your Record Type IDs from the Object Manager and copy the base URL from your Agile Accelerator, it should resemble the one included in the example. - - When you later set up Heroku Connect, your database table and fields related to Agile Accelerator may have a prefix, which you will set as the `SALESFORCE_PREFIX` + - When you later set up Heroku Connect, your database table and fields related to Agile Accelerator may be in a specific Postgres schema, which you will set as the `SALESFORCE_PREFIX`. If you use the defaults in Heroku Connect, this will be `salesforce.` 7. Add a link to your GitHub app (ex: the GitHub app for Salesforce's instance is https://github.com/apps/git2gus) @@ -105,8 +105,8 @@ BUG_RECORD_TYPE_ID=NOPQRSTUVWXYZ INVESTIGATION_RECORD_TYPE_ID=123456789012 WORK_ITEM_BASE_URL=https://myproject.lightning.force.com/lightning/r/ADM_Work__c/ GITHUB_APP_URL= https://github.com/apps/yourapplication -SALESFORCE_PREFIX=agf__ - +SALESFORCE_PREFIX=salesforce. +NAMESPACE_PREFIX=agf ``` For use with SSO-enabled organizations, you would also have additional lines: diff --git a/api/actions/__test__/createWorkItem.spec.js b/api/actions/__test__/createWorkItem.spec.js index 1a16278..2828f74 100644 --- a/api/actions/__test__/createWorkItem.spec.js +++ b/api/actions/__test__/createWorkItem.spec.js @@ -30,7 +30,8 @@ jest.mock('../../services/Gus', () => ({ createWorkItemInGus: jest.fn(), resolveBuild: jest.fn(), getBugRecordTypeId: jest.fn(), - getById: jest.fn() + getById: jest.fn(), + field: name => name + '__c' })); jest.mock('../../actions/formatToGus', () => ({ formatToGus: jest.fn() @@ -442,7 +443,6 @@ describe('createGusItem action', () => { }); it('should create a comment without the url when the git2gus.config.hideWorkItemUrl = true', async () => { - expect.assertions(1); Github.getRecordTypeId.mockReturnValue('bug'); Github.isSalesforceLabel.mockReturnValue(true); Builds.resolveBuild.mockReturnValue(Promise.resolve('qwerty1234')); diff --git a/api/actions/__test__/createWorkItemForPR.spec.js b/api/actions/__test__/createWorkItemForPR.spec.js index 4da4540..cc90522 100644 --- a/api/actions/__test__/createWorkItemForPR.spec.js +++ b/api/actions/__test__/createWorkItemForPR.spec.js @@ -30,7 +30,8 @@ jest.mock('../../services/Gus', () => ({ createWorkItemInGus: jest.fn(), resolveBuild: jest.fn(), getBugRecordTypeId: jest.fn(), - getById: jest.fn() + getById: jest.fn(), + field: name => name + '__c' })); jest.mock('../../actions/formatToGus', () => ({ formatToGus: jest.fn() diff --git a/api/actions/createWorkItem.js b/api/actions/createWorkItem.js index 8661db5..96dc397 100644 --- a/api/actions/createWorkItem.js +++ b/api/actions/createWorkItem.js @@ -14,11 +14,12 @@ const { formatToGus } = require("./formatToGus"); const GithubEvents = require('../modules/GithubEvents'); const Github = require('../services/Github'); const Gus = require('../services/Gus'); +const logger = require('../services/Logs/logger'); module.exports = { eventName: GithubEvents.events.ISSUE_LABELED, fn: async function (req) { - console.log('createWorkItem Action called with req: ', req); + logger.info('createWorkItem Action called'); const { issue: { labels, html_url, body, milestone } } = req.body; @@ -26,67 +27,75 @@ module.exports = { issue: { title } } = req.body; // Only grab the label being added for comparison against Salesforce labels - const { label : labelAdded } = req.body; + const { label: labelAdded } = req.body; const { config } = req.git2gus; const { hideWorkItemUrl } = config; let productTag = config.productTag; if (config.productTagLabels) { - console.log('createWorkItem will work with custom productTagLabels for issue titled: ', title); + logger.info('createWorkItem will work with custom productTagLabels for issue titled: ', title); Object.keys(config.productTagLabels).forEach(productTagLabel => { if (labels.some(label => label.name === productTagLabel)) { productTag = config.productTagLabels[productTagLabel]; } }); } - if(config.issueTypeLabels) { - console.log('createWorkItem will work with custom issueTypeLabels for issue titled: ', title); + if (config.issueTypeLabels) { + logger.info('createWorkItem will work with custom issueTypeLabels for issue titled: ', title); Object.keys(config.issueTypeLabels).forEach(issueTypeLabel => { // If the label added is a Salesforce custom label, give it the correct base label if (labelAdded.name === issueTypeLabel) { labelAdded.name = config.issueTypeLabels[issueTypeLabel]; } if (labels.some(label => label.name === issueTypeLabel)) { - labels.push({name: config.issueTypeLabels[issueTypeLabel]}); + labels.push({ name: config.issueTypeLabels[issueTypeLabel] }); } }); } let normalizedTitle = getTitleWithOptionalPrefix(config, title); - console.log('createWorkItem will create GUS work item with title: ', normalizedTitle); + logger.info('createWorkItem will create GUS work item with title: ', normalizedTitle); // Only check the label being added if (Github.isSalesforceLabel(labelAdded.name) && productTag) { - console.log('Verified valid label and product tag for issue titled: ', title); + logger.info('Verified valid label and product tag for issue titled: ', title); const priority = Github.getPriority(labels); - console.log(`Found priority: ${priority} for issue titled: ${title}`); + logger.info(`Found priority: ${priority} for issue titled: ${title}`); const recordTypeId = Github.getRecordTypeId(labels); - console.log(`Found recordTypeId: ${recordTypeId} for issue titled: ${title}`); + logger.info(`Found recordTypeId: ${recordTypeId} for issue titled: ${title}`); const bodyInGusFormat = await formatToGus(html_url, body); - console.log(`Found bodyInGusFormat: ${bodyInGusFormat} for issue titled: ${title}`); + logger.info(`Found bodyInGusFormat: ${bodyInGusFormat} for issue titled: ${title}`); - console.log(`Using GUS Api to create workitem for issue titled: ${title}`); + logger.info(`Using GUS Api to create workitem for issue titled: ${title}`); + // default build to "undefined", to invoke our updateIssue error below const buildName = milestone ? milestone.title : config.defaultBuild; const foundInBuild = await Gus.resolveBuild(buildName); - console.log(`Found foundInBuild: ${foundInBuild} for issue titled: ${title}`); + logger.info(`Found foundInBuild: ${foundInBuild} for issue titled: ${title}`); const issue = await Gus.getByRelatedUrl(html_url); + if (issue) { + logger.info(`Found existing Work "${issue.Name}" for issue "${html_url}"`); + } else { + logger.info(`No existing Work for issue "${html_url}"`); + } const bugRecordTypeId = Gus.getBugRecordTypeId(); const alreadyLowestPriority = - issue && issue.Priority__c !== '' && issue.Priority__c <= priority; - const recordIdTypeIsSame = issue && issue.RecordTypeId === recordTypeId; + issue && issue[Gus.field('Priority')] !== '' && issue[Gus.field('Priority')] <= priority; + const recordTypeIdIsSame = issue && issue.RecordTypeId === recordTypeId; const isRecordTypeBug = recordTypeId === bugRecordTypeId; // If issue is a bug we check if it already exists and already has lowest priority // If issue type is investigation or story, we simply check it exists - if (isRecordTypeBug && alreadyLowestPriority && recordIdTypeIsSame) { + if (isRecordTypeBug && alreadyLowestPriority && recordTypeIdIsSame) { + logger.info(`Not opening new bug because existing bug has lower priority`); return; - } else if ( !isRecordTypeBug && recordIdTypeIsSame) { + } else if (!isRecordTypeBug && recordTypeIdIsSame) { + logger.info(`Not opening new bug because existing Work is another record type`); return; } if (foundInBuild) { - try{ - console.log('Calling GUS API to create a new work item'); + try { + logger.info('Calling GUS API to create a new work item'); const syncedItem = await Gus.createWorkItemInGus(normalizedTitle, bodyInGusFormat, productTag, @@ -96,21 +105,21 @@ module.exports = { html_url, recordTypeId); const syncedItemFromGus = await Gus.getById(syncedItem.id); - console.log('###hideWorkItemUrl:' + hideWorkItemUrl); + logger.info('###hideWorkItemUrl:' + hideWorkItemUrl); const displayUrl = (hideWorkItemUrl === 'true') ? syncedItemFromGus.Name : `[${syncedItemFromGus.Name}](${process.env.WORK_ITEM_BASE_URL + syncedItem.id}/view)`; const msg = `This issue has been linked to a new work item: ${displayUrl}`; - console.log(msg, ' for issue titled: ', title); + logger.info(msg, ' for issue titled: ', title); return await updateIssue(req, msg); - } catch(e) { - console.log(`Error while creating work item ${e.message}`); + } catch (e) { + logger.error(`Error while creating work item ${e.message}`, e); return await updateIssue(req, 'Error while creating work item!'); } } else { - console.log(`No correct build for issue titled: ${title}`); + logger.error(`No correct build for issue titled: ${title}`); return await updateIssue(req, 'Error while creating work item. No valid build found in GUS!'); } } - console.log('Failed to create work item for issue titled: ', title); + logger.error('Failed to create work item for issue titled: ', title); return null; } }; diff --git a/api/actions/createWorkItemForPR.js b/api/actions/createWorkItemForPR.js index fe864ed..626339f 100644 --- a/api/actions/createWorkItemForPR.js +++ b/api/actions/createWorkItemForPR.js @@ -18,7 +18,7 @@ const Gus = require('../services/Gus'); module.exports = { eventName: GithubEvents.events.PULL_REQUEST_LABELED, fn: async function(req) { - console.log('createWorkItem Action called with req: ', req); + console.log('createWorkItem Action called'); const { pull_request: { labels, html_url, body, milestone } } = req.body; @@ -86,6 +86,7 @@ module.exports = { console.log( `Using GUS Api to create workitem for issue titled: ${title}` ); + // default build to "undefined", to invoke our updateIssue error below const buildName = milestone ? milestone.title : config.defaultBuild; const foundInBuild = await Gus.resolveBuild(buildName); console.log( @@ -97,8 +98,8 @@ module.exports = { const alreadyLowestPriority = issue && - issue.Priority__c !== '' && - issue.Priority__c <= priority; + issue[Gus.field('Priority')] !== '' && + issue[Gus.field('Priority')] <= priority; const recordIdTypeIsSame = issue && issue.RecordTypeId === recordTypeId; const isRecordTypeBug = recordTypeId === bugRecordTypeId; diff --git a/api/controllers/GithubController.js b/api/controllers/GithubController.js index c04123a..15f83d3 100644 --- a/api/controllers/GithubController.js +++ b/api/controllers/GithubController.js @@ -7,7 +7,7 @@ module.exports = { async processEvent(req, res) { - sails.config.ghEvents.emitFromReq(req); + await sails.config.ghEvents.emitFromReq(req); return res.ok({ status: 'OK' }); diff --git a/api/modules/GithubEvents/index.js b/api/modules/GithubEvents/index.js index 5dd2985..afe9032 100644 --- a/api/modules/GithubEvents/index.js +++ b/api/modules/GithubEvents/index.js @@ -5,7 +5,7 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -const EventEmitter = require('events'); +const { EventEmitter, captureRejectionSymbol } = require('events'); const logger = require('../../services/Logs/logger'); const events = { @@ -71,6 +71,10 @@ const eventsConfig = { }; class GithubEvents extends EventEmitter { + constructor() { + super({ captureRejections: true }); + } + static match(req, eventName) { const event = req.headers['x-github-event']; const { action } = req.body; @@ -80,13 +84,23 @@ class GithubEvents extends EventEmitter { ); } - emitFromReq(req) { + async emitFromReq(req) { + const handlerPromises = []; + Object.keys(eventsConfig).forEach(eventName => { if (GithubEvents.match(req, eventName)) { - logger.info('Request matches eventName', { req, eventName }); - this.emit(eventName, req); + logger.info('Request matches eventName', { eventName }); + this.emit(eventName, req, handlerPromises); } }); + + // wait for all handlers to finish, and throw if any rejections + await Promise.all(handlerPromises); + } + + // Avoid unhandled promise rejection by logging rejections from event handlers + [captureRejectionSymbol](err, event, ...unusedArgs) { + logger.error(`unhandled async error in event ${event}`, err); } } diff --git a/api/services/Gus/closeWorkItem.js b/api/services/Gus/closeWorkItem.js index 5d80e89..8523143 100644 --- a/api/services/Gus/closeWorkItem.js +++ b/api/services/Gus/closeWorkItem.js @@ -4,27 +4,17 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -const jsforce = require('jsforce'); +const { getConnection, Work, field } = require('./connection'); module.exports = async function closeWorkItem(relatedUrl, status) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - async err => { - if (err) { - return console.error(err); - } - } - ); + const conn = await getConnection(); return Promise.resolve( conn - .sobject('ADM_Work__c') - .find({ related_url__c: relatedUrl }) + .sobject(Work) + .find({ [field('related_url')]: relatedUrl }) .update( { - status__c: status + [field('status')]: status }, (err, ret) => { if (err || !ret.success) { diff --git a/api/services/Gus/connection.js b/api/services/Gus/connection.js new file mode 100644 index 0000000..c6b4ed0 --- /dev/null +++ b/api/services/Gus/connection.js @@ -0,0 +1,43 @@ +const jsforce = require('jsforce'); +const logger = require('../../services/Logs/logger'); + +let connection; +async function getConnection() { + if (connection) { + return connection; + } + + const conn = new jsforce.Connection({ logLevel: 'DEBUG' }); + try { + await conn.login(process.env.GUS_USERNAME, process.env.GUS_PASSWORD); + connection = conn; + + // Keep connection open, but do reconnect every so often + setTimeout(() => { + connection = null; + logger.info(`Forgetting Gus session`); + }, 10 * 60 * 1000); // 10 minutes + + return conn; + } catch (err) { + logger.error('Error logging into GUS', err); + throw new Error(`Error logging into GUS ${err.message}`); + } +} + +const NAMESPACE_PREFIX = process.env.NAMESPACE_PREFIX + ? `${process.env.NAMESPACE_PREFIX}__` + : ''; + +function field(name) { + return `${NAMESPACE_PREFIX}${name}__c`; +} + +module.exports = { + getConnection, + Work: NAMESPACE_PREFIX + 'ADM_Work__c', + Build: NAMESPACE_PREFIX + 'ADM_Build__c', + Changelist: NAMESPACE_PREFIX + 'ADM_Changelist__c', + prefix: NAMESPACE_PREFIX, + field +}; diff --git a/api/services/Gus/createChangelistInGus.js b/api/services/Gus/createChangelistInGus.js index 4a533dd..0a81aa4 100644 --- a/api/services/Gus/createChangelistInGus.js +++ b/api/services/Gus/createChangelistInGus.js @@ -4,34 +4,28 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +const { getConnection, Changelist, field } = require('./connection'); -const jsforce = require('jsforce'); - -module.exports = function createChangelistInGus( +module.exports = async function createChangelistInGus( relativeUrl, issueId, mergedAt ) { - const conn = new jsforce.Connection(); - conn.login(process.env.GUS_USERNAME, process.env.GUS_PASSWORD, err => { - if (err) { - return console.error(err); - } - conn.sobject('ADM_Change_List__c').create( - { - Perforce_Changelist__c: relativeUrl, - Work__c: issueId, - External_ID__c: relativeUrl, - Source__c: 'GitHub', - Check_In_Date__c: mergedAt, - Check_In_By__c: process.env.GUS_USER - }, - (err, ret) => { - if (err || !ret.success) { - return console.error(err, ret); - } - return ret.id; + const conn = await getConnection(); + return conn.sobject(Changelist).create( + { + [field('Perforce_Changelist')]: relativeUrl, + [field('Work')]: issueId, + [field('External_ID')]: relativeUrl, + [field('Source')]: 'GitHub', + [field('Check_In_Date')]: mergedAt, + [field('Check_In_By')]: process.env.GUS_USER + }, + (err, ret) => { + if (err || !ret.success) { + return console.error(err, ret); } - ); - }); + return ret.id; + } + ); }; diff --git a/api/services/Gus/createComment.js b/api/services/Gus/createComment.js index 2b9db68..f8edea5 100644 --- a/api/services/Gus/createComment.js +++ b/api/services/Gus/createComment.js @@ -4,26 +4,20 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +const { getConnection } = require('./connection'); -const jsforce = require('jsforce'); - -module.exports = function createComment(comment, issueId) { - const conn = new jsforce.Connection(); - conn.login(process.env.GUS_USERNAME, process.env.GUS_PASSWORD, err => { - if (err) { - return console.error(err); - } - conn.sobject('FeedItem').create( - { - Body: comment, - ParentId: issueId - }, - (err, ret) => { - if (err || !ret.success) { - return console.error(err, ret); - } - return ret.id; +module.exports = async function createComment(comment, issueId) { + const conn = await getConnection(); + return conn.sobject('FeedItem').create( + { + Body: comment, + ParentId: issueId + }, + (err, ret) => { + if (err || !ret.success) { + return console.error(err, ret); } - ); - }); + return ret.id; + } + ); }; diff --git a/api/services/Gus/createWorkItemInGus.js b/api/services/Gus/createWorkItemInGus.js index 7be30ac..ac15427 100644 --- a/api/services/Gus/createWorkItemInGus.js +++ b/api/services/Gus/createWorkItemInGus.js @@ -4,8 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -const jsforce = require('jsforce'); +const { getConnection, Work, field } = require('./connection'); module.exports = async function createWorkItemInGus( subject, @@ -17,27 +16,18 @@ module.exports = async function createWorkItemInGus( relatedUrl, recordTypeId ) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - err => { - if (err) { - return console.error(err); - } - } - ); + const conn = await getConnection(); return Promise.resolve( - conn.sobject('ADM_Work__c').create( + conn.sobject(Work).create( { - Subject__c: subject, - details__c: description, - details_and_steps_to_reproduce__c: description, - product_tag__c: productTag, - status__c: status, - found_in_build__c: foundInBuild, - priority__c: priority, - related_url__c: relatedUrl, + [field('Subject')]: subject, + [field('details')]: description, + [field('details_and_steps_to_reproduce')]: description, + [field('product_tag')]: productTag, + [field('status')]: status, + [field('found_in_build')]: foundInBuild, + [field('priority')]: priority, + [field('related_url')]: relatedUrl, recordtypeid: recordTypeId }, (err, ret) => { diff --git a/api/services/Gus/getById.js b/api/services/Gus/getById.js index b6ab61f..c59161a 100644 --- a/api/services/Gus/getById.js +++ b/api/services/Gus/getById.js @@ -4,23 +4,13 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -const jsforce = require('jsforce'); +const { getConnection, Work } = require('./connection'); module.exports = async function getById(id) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - async err => { - if (err) { - return console.error(err); - } - } - ); + const conn = await getConnection(); return Promise.resolve( conn - .sobject('ADM_Work__c') + .sobject(Work) .find({ id }) .execute((err, ret) => { if (err) { diff --git a/api/services/Gus/getByRelatedUrl.js b/api/services/Gus/getByRelatedUrl.js index a08160e..8f463ae 100644 --- a/api/services/Gus/getByRelatedUrl.js +++ b/api/services/Gus/getByRelatedUrl.js @@ -4,24 +4,14 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -const jsforce = require('jsforce'); +const { getConnection, Work, field } = require('./connection'); module.exports = async function getByRelatedUrl(relatedUrl) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - async err => { - if (err) { - return console.error(err); - } - } - ); + const conn = await getConnection(); return Promise.resolve( conn - .sobject('ADM_Work__c') - .find({ related_url__c: relatedUrl }) + .sobject(Work) + .find({ [field('related_url')]: relatedUrl }) .execute((err, ret) => { if (err) { return console.error(err, ret); diff --git a/api/services/Gus/getWorkItemIdByName.js b/api/services/Gus/getWorkItemIdByName.js index d0f5f3b..e1944dd 100644 --- a/api/services/Gus/getWorkItemIdByName.js +++ b/api/services/Gus/getWorkItemIdByName.js @@ -4,23 +4,13 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +const { getConnection, Work } = require('./connection'); -const jsforce = require('jsforce'); - -module.exports = async function getWorkItemItemByName(workItemName) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - async err => { - if (err) { - return console.error(err); - } - } - ); +module.exports = async function getWorkItemIdByName(workItemName) { + const conn = await getConnection(); return Promise.resolve( conn - .sobject('ADM_Work__c') + .sobject(Work) .find({ Name: workItemName }) .execute((err, ret) => { if (err) { diff --git a/api/services/Gus/index.js b/api/services/Gus/index.js index 60982cd..a42a871 100644 --- a/api/services/Gus/index.js +++ b/api/services/Gus/index.js @@ -14,6 +14,7 @@ const closeWorkItem = require('./closeWorkItem'); const getByRelatedUrl = require('./getByRelatedUrl'); const getById = require('./getById'); const getBugRecordTypeId = require('./getBugRecordTypeId'); +const { field } = require('./connection'); module.exports = { createChangelistInGus, @@ -24,5 +25,11 @@ module.exports = { closeWorkItem, getByRelatedUrl, getById, - getBugRecordTypeId + getBugRecordTypeId, + /** + * Prepend namespace and append __c. Use only for custom fields from the ADM package. + * + * Usage: field("ADM_Work") returns "agf__ADM_Work__c" + */ + field }; diff --git a/api/services/Gus/resolveBuild.js b/api/services/Gus/resolveBuild.js index 892567e..f4a5297 100644 --- a/api/services/Gus/resolveBuild.js +++ b/api/services/Gus/resolveBuild.js @@ -4,29 +4,23 @@ * SPDX-License-Identifier: BSD-3-Clause * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ - -const jsforce = require('jsforce'); +const { getConnection, Build } = require('./connection'); +const logger = require('../../services/Logs/logger'); module.exports = async function resolveBuild(name) { - const conn = new jsforce.Connection(); - await conn.login( - process.env.GUS_USERNAME, - process.env.GUS_PASSWORD, - async err => { - if (err) { - return console.error(err); - } - } - ); - return Promise.resolve( - conn - .sobject('ADM_Build__c') + const conn = await getConnection(); + + try { + const ret = await conn + .sobject(Build) .find({ name }) - .execute((err, ret) => { - if (err) { - return console.error(err, ret); - } - return ret[0].Id; - }) - ); + .execute(); + if (!ret || ret.length === 0) { + logger.error(`No build with name ${name}`); + } + return ret[0].Id; + } catch (err) { + logger.error(`Error fetching ADM_Build__c with name ${name}`, err); + return; + } }; diff --git a/config/env/production.js b/config/env/production.js index a48983f..22442f8 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -204,7 +204,7 @@ module.exports = { * * ***************************************************************************/ cookie: { - // secure: true, + secure: true, maxAge: 24 * 60 * 60 * 1000 // 24 hours } }, diff --git a/config/ghEvents.js b/config/ghEvents.js index 00635b7..e14cd76 100644 --- a/config/ghEvents.js +++ b/config/ghEvents.js @@ -15,16 +15,17 @@ const actions = require('require-all')({ }); Object.keys(actions).forEach(actionName => { - const { eventName, fn } = actions[actionName]; + let { eventName, fn } = actions[actionName]; if (typeof eventName === 'string') { - console.log(`attach ${actionName} to ${eventName}`); - ghEvents.on(eventName, fn); + eventName = [eventName]; } if (Array.isArray(eventName)) { eventName.forEach(event => { console.log(`attach ${actionName} to ${event}`); - ghEvents.on(event, fn); + ghEvents.on(event, (req, handlerPromises) => { + handlerPromises.push(fn(req)); + }); }); } }); diff --git a/package.json b/package.json index 4486850..2305959 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "sails-hook-sockets": "^1.4.0", "sails-postgresql": "^1.0.1", "smee-client": "^1.1.0", + "winston": "^3.8.2", "winston-elasticsearch": "^0.11.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index b6b96a9..5850d41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10035,7 +10035,7 @@ winston@2.x: isstream "0.1.x" stack-trace "0.0.x" -winston@^3.2.1, winston@^3.3.3: +winston@^3.2.1, winston@^3.3.3, winston@^3.8.2: version "3.8.2" resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==