From 3a93a7b7d2eec9e739ae15172ce1975764f16447 Mon Sep 17 00:00:00 2001 From: anabellabuckvar <41971124+anabellabuckvar@users.noreply.github.com> Date: Tue, 7 May 2024 11:35:02 -0400 Subject: [PATCH] DOP-4549: Branches and repos in separate dropdowns in slack deploy dialog (#1029) * DOP-4504 v2 slack consistent with v1 * DOP-4504 push to preprd * DOP-4504 fix displayrepooptions name * DOP-4549 push to preprd * DOP-4549 adding logs * DOP-4549 pushing to webhooks * DOP-4549 logger * DOP-4945 commment out dispatch action * DOP-4549 adding boolean back in * DOP-4549 more logging * DOP-4549 log view * DOP-4549 fixing log of view * DOP-4549 fixing log of view * DOP-4549 trying selection groups * DOP-4549 logging repo length * DOP-4549 logging repo length * DOP-4549 logging repo length * DOP-4549 logging repo length again * DOP-4549 remove 100 item truncation * DOP-4945 reinstate slice * DOP-4945 reconfigure app to display in groups * DOP-4945 fixing imports * DOP-4945 fixing display format * DOP-4549 fix sorting * DOP-4549 undoing changes * DOP-4549 re fix sorting * DOP-4549 unfix sorting * DOP-4549 reinstate sorting * DOP-4549 implement multi static select * DOP-4549 update view for parsing ability * DOP-4549 adding logging, repushing to preprd * DOP-4549 more logging * DOP-4549 adding more logging * DOP-4549 return early * DOP-4549 return early * DOP-4549 return earlier * DOP-4549 return immediately from deployrepo * DOP-4549 removing from preprd * DOP-4549 push to preprd * DOP-4549 push preprd * DOP-4549 fix errors * DOP-4549 added logs, return later * DOP-4549 added logs, return later * DOP-4549 added logs, return even later * DOP-4549 added logs, return even later * DOP-4549 don't return early * DOP-4549 cleaning up * DOP-4549 re add inactive label * DOP-4549 remove from preprd * DOP-4497 change comment placement * DOP-4549 nits --- .../deploy-stg-enhanced-webhooks.yml | 1 - api/controllers/v1/slack.ts | 4 +- api/controllers/v2/slack.ts | 65 ++++++++++++------- api/handlers/slack.ts | 41 +++++++++--- .../constructs/api/webhook-api-construct.ts | 2 +- src/repositories/jobRepository.ts | 5 +- src/services/slack.ts | 57 ++-------------- 7 files changed, 88 insertions(+), 87 deletions(-) diff --git a/.github/workflows/deploy-stg-enhanced-webhooks.yml b/.github/workflows/deploy-stg-enhanced-webhooks.yml index 97decd119..c69e736b6 100644 --- a/.github/workflows/deploy-stg-enhanced-webhooks.yml +++ b/.github/workflows/deploy-stg-enhanced-webhooks.yml @@ -4,7 +4,6 @@ on: branches: - 'main' - 'integration' - concurrency: group: environment-stg-enhanced-webhooks-${{ github.ref }} cancel-in-progress: true diff --git a/api/controllers/v1/slack.ts b/api/controllers/v1/slack.ts index 45484e78a..3b2f71864 100644 --- a/api/controllers/v1/slack.ts +++ b/api/controllers/v1/slack.ts @@ -7,7 +7,7 @@ import { SlackConnector } from '../../../src/services/slack'; import { APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda'; import { JobRepository } from '../../../src/repositories/jobRepository'; import { - buildEntitledBranchList, + buildEntitledGroupsList, getQSString, isRestrictedToDeploy, isUserEntitled, @@ -48,7 +48,7 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise { const deployable = []; - for (let i = 0; i < values.repo_option.length; i++) { - let repoOwner: string, repoName: string, branchName: string, directory: string | undefined; - const splitValues = values.repo_option[i].value.split('/'); - - if (process.env.FEATURE_FLAG_MONOREPO_PATH === 'true' && splitValues.length === 4) { - // e.g. 10gen/docs-monorepo/cloud-docs/master => (owner/monorepo/repoDirectory/branch) - [repoOwner, repoName, directory, branchName] = splitValues; + for (let i = 0; i < values?.repo_option?.length; i++) { + let jobTitle: string, repoOwner: string, repoName: string, branchName: string, directory: string | undefined; + if (values.deploy_option == 'deploy_all') { + repoOwner = 'mongodb'; + branchName = 'master'; + repoName = values.repo_option[i].repoName; + jobTitle = `Slack deploy: ${repoOwner}/${repoName}/${branchName}, by ${entitlement.github_username}`; } else { - // e.g. mongodb/docs-realm/master => (owner/repo/branch) - [repoOwner, repoName, branchName] = splitValues; + const splitValues = values.repo_option[i].value.split('/'); + jobTitle = `Slack deploy: ${values.repo_option[i].value}, by ${entitlement.github_username}`; + + if (splitValues.length === 3) { + // e.g. mongodb/docs-realm/master => (owner/repo/branch) + [repoOwner, repoName, branchName] = splitValues; + } else if (splitValues.length === 4 && process.env.FEATURE_FLAG_MONOREPO_PATH === 'true') { + // e.g. 10gen/docs-monorepo/cloud-docs/master => (owner/monorepo/repoDirectory/branch) + [repoOwner, repoName, directory, branchName] = splitValues; + } else { + throw Error('Selected entitlement value is configured incorrectly. Check user entitlements!'); + } } const hashOption = values?.hash_option ?? null; - const jobTitle = `Slack deploy: ${values.repo_option[i].value}, by ${entitlement.github_username}`; const jobUserName = entitlement.github_username; const jobUserEmail = entitlement?.email ?? ''; @@ -115,11 +124,11 @@ export const getDeployableJobs = async ( const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName, repoInfo.project); if (!branchObject?.aliasObject) continue; - const publishOriginalBranchName = branchObject.aliasObject.publishOriginalBranchName; //bool - let aliases = branchObject.aliasObject.urlAliases; // array or null - let urlSlug = branchObject.aliasObject.urlSlug; // string or null, string must match value in urlAliases or gitBranchName - const isStableBranch = branchObject.aliasObject.isStableBranch; // bool or Falsey - aliases = aliases?.filter((a) => a); + const publishOriginalBranchName: boolean = branchObject.aliasObject.publishOriginalBranchName; + const aliases: string[] | null = branchObject.aliasObject.urlAliases; + let urlSlug: string = branchObject.aliasObject.urlSlug; // string or null, string must match value in urlAliases or gitBranchName + const isStableBranch = !!branchObject.aliasObject.isStableBranch; // bool or Falsey, add strong typing + if (!urlSlug || !urlSlug.trim()) { urlSlug = branchName; } @@ -136,12 +145,10 @@ export const getDeployableJobs = async ( urlSlug, false, false, - false, + isStableBranch, directory ); - newPayload.stable = !!isStableBranch; - if (!aliases || aliases.length === 0) { if (non_versioned) { newPayload.urlSlug = ''; @@ -209,12 +216,24 @@ export const DeployRepo = async (event: APIGatewayEvent): Promise 0) { diff --git a/api/handlers/slack.ts b/api/handlers/slack.ts index 4b8d534cd..83d42c086 100644 --- a/api/handlers/slack.ts +++ b/api/handlers/slack.ts @@ -18,27 +18,52 @@ export function prepResponse(statusCode, contentType, body) { }; } -export async function buildEntitledBranchList(entitlement: any, repoBranchesRepository: RepoBranchesRepository) { - const entitledBranches: string[] = []; +//if person is admin, get all prod deployable repos +export async function buildEntitledGroupsList(entitlement: any, repoBranchesRepository: RepoBranchesRepository) { + const repoOptions: any[] = []; for (const repo of entitlement.repos) { const [repoOwner, repoName, directoryPath] = repo.split('/'); + const branches = await repoBranchesRepository.getRepoBranches(repoName, directoryPath); + const options: any[] = []; for (const branch of branches) { const buildWithSnooty = branch['buildsWithSnooty']; if (buildWithSnooty) { const active = branch['active']; - const repoPath = `${repoOwner}/${repoName}${directoryPath ? '/' + directoryPath : ''}/${ - branch['gitBranchName'] - }`; + const branchName = `${directoryPath ? `${directoryPath}/` : ''}${branch['gitBranchName']}`; + const repoPath = `${repoOwner}/${repoName}/${branchName}`; + let txt: string; if (!active) { - entitledBranches.push(`(!inactive) ${repoPath}`); + txt = `(!inactive) ${repoPath}`; } else { - entitledBranches.push(repoPath); + txt = repoPath; } + options.push({ + text: { + type: 'plain_text', + text: txt, + }, + value: repoPath, + }); } } + + const repoOption = { + label: { + type: 'plain_text', + text: repoName, + }, + //sort the options by version number + options: options.sort((branchOne, branchTwo) => + branchTwo.text.text + .toString() + .replace(/\d+/g, (n) => +n + 100000) + .localeCompare(branchOne.text.text.toString().replace(/\d+/g, (n) => +n + 100000)) + ), + }; + repoOptions.push(repoOption); } - return entitledBranches.sort(); + return repoOptions.sort((repoOne, repoTwo) => repoOne.label.text.localeCompare(repoTwo.label.text)); } export function getQSString(qs: string) { diff --git a/cdk-infra/lib/constructs/api/webhook-api-construct.ts b/cdk-infra/lib/constructs/api/webhook-api-construct.ts index 10862f6b8..f892f20ea 100644 --- a/cdk-infra/lib/constructs/api/webhook-api-construct.ts +++ b/cdk-infra/lib/constructs/api/webhook-api-construct.ts @@ -51,7 +51,7 @@ export class WebhookApiConstruct extends Construct { const slackDisplayRepoLambda = new NodejsFunction(this, 'slackDisplayRepoLambda', { entry: `${HANDLERS_PATH}/slack.ts`, runtime, - handler: 'DeployRepoDisplayRepoOptions', + handler: 'DisplayRepoOptions', environment, bundling, timeout, diff --git a/src/repositories/jobRepository.ts b/src/repositories/jobRepository.ts index d2e27da19..54c1464e4 100644 --- a/src/repositories/jobRepository.ts +++ b/src/repositories/jobRepository.ts @@ -69,7 +69,10 @@ export class JobRepository extends BaseRepository { throw new DBError('insertBulkJobs: Unable to insert multiple jobs'); } // Insertion/re-enqueueing should be sent to jobs queue and updates for an existing job should be sent to jobUpdates Queue - this._logger.info('insertBulkJobs', `Total Jobs Expected : ${jobs.length}, Total Jobs Sent: ${jobIds.length}`); + this._logger.info( + 'insertBulkJobs', + `Total Jobs Expected : ${jobs.length}, Jobs: ${JSON.stringify(jobIds)}, Total Jobs Sent: ${jobIds.length}` + ); await Promise.all( Object.values(jobIds).map(async (jobId: string) => { await this._queueConnector.sendMessage(new JobQueueMessage(jobId, JobStatus.inQueue), url, 0); diff --git a/src/services/slack.ts b/src/services/slack.ts index e5483ec98..229646f1e 100644 --- a/src/services/slack.ts +++ b/src/services/slack.ts @@ -119,8 +119,7 @@ export class SlackConnector implements ISlackConnector { } async displayRepoOptions(repos: string[], triggerId: string, isAdmin: boolean): Promise { - const reposToShow = this._buildDropdown(repos); - const repoOptView = this._getDropDownView(triggerId, reposToShow, isAdmin); + const repoOptView = this._getDropDownView(triggerId, repos, isAdmin); const slackToken = this._config.get('slackAuthToken'); const slackUrl = this._config.get('slackViewOpenUrl'); return await axiosApi.post(slackUrl, repoOptView, { @@ -182,37 +181,31 @@ export class SlackConnector implements ISlackConnector { title: { type: 'plain_text', text: 'Deploy Docs', - emoji: true, }, submit: { type: 'plain_text', text: 'Submit', - emoji: true, }, close: { type: 'plain_text', text: 'Cancel', - emoji: true, }, blocks: [ { type: 'input', block_id: 'block_repo_option', + label: { + type: 'plain_text', + text: 'Select Repo', + }, element: { type: 'multi_static_select', action_id: 'repo_option', placeholder: { type: 'plain_text', text: 'Select a repo to deploy', - emoji: true, }, - options: repos, - }, - optional: true, - label: { - type: 'plain_text', - text: 'Select Repo', - emoji: true, + option_groups: repos, }, }, { @@ -237,42 +230,4 @@ export class SlackConnector implements ISlackConnector { }, }; } - - private _buildDropdown(branches: Array): Array { - let reposToShow: Array = []; - branches.forEach((fullPath) => { - const displayBranchPath = fullPath; - let valueBranchPath = fullPath; - const isInactive = fullPath.startsWith('(!inactive)'); - if (isInactive == true) { - valueBranchPath = fullPath.slice(12); - } - const opt = { - text: { - type: 'plain_text', - text: displayBranchPath, - }, - value: valueBranchPath, - }; - reposToShow.push(opt); - }); - - // This is the limitation enforced by slack as no more 100 items are allowd in the dropdown - //Sort the list so that any inactive versions are at the end and will be truncated if any items must be truncated - //'[ERROR] no more than 100 items allowed [json-pointer:/view/blocks/0/element/options]' - - if (reposToShow.length > 100) { - reposToShow = reposToShow.sort().reverse().splice(0, 100); - } - - //sort versions like so: 4.1, 4.2, 4.11 - reposToShow.sort((a, b) => { - return b.text.text - .toString() - .replace(/\d+/g, (n) => +n + 100000) - .localeCompare(a.text.text.toString().replace(/\d+/g, (n) => +n + 100000)); - }); - - return reposToShow; - } }