Skip to content

Commit

Permalink
DOP-4549: Branches and repos in separate dropdowns in slack deploy di…
Browse files Browse the repository at this point in the history
…alog (#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
  • Loading branch information
anabellabuckvar authored May 7, 2024
1 parent 3c1059f commit 3a93a7b
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 87 deletions.
1 change: 0 additions & 1 deletion .github/workflows/deploy-stg-enhanced-webhooks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
branches:
- 'main'
- 'integration'

concurrency:
group: environment-stg-enhanced-webhooks-${{ github.ref }}
cancel-in-progress: true
Expand Down
4 changes: 2 additions & 2 deletions api/controllers/v1/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -48,7 +48,7 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat

const isAdmin = await repoEntitlementRepository.getIsAdmin(key_val['user_id']);

const entitledBranches = await buildEntitledBranchList(entitlement, repoBranchesRepository);
const entitledBranches = await buildEntitledGroupsList(entitlement, repoBranchesRepository);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], isAdmin);
if (resp?.status == 200 && resp?.data) {
return {
Expand Down
65 changes: 42 additions & 23 deletions api/controllers/v2/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { JobRepository } from '../../../src/repositories/jobRepository';
import { APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda';
import { EnhancedPayload, JobStatus } from '../../../src/entities/job';
import {
buildEntitledBranchList,
buildEntitledGroupsList,
getQSString,
isRestrictedToDeploy,
isUserEntitled,
Expand Down Expand Up @@ -46,10 +46,10 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat
return prepResponse(401, 'text/plain', response);
}

const admin = await repoEntitlementRepository.getIsAdmin(key_val['user_id']);
const isAdmin = await repoEntitlementRepository.getIsAdmin(key_val['user_id']);

const entitledBranches = await buildEntitledBranchList(entitlement, repoBranchesRepository);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], admin);
const entitledBranches = await buildEntitledGroupsList(entitlement, repoBranchesRepository);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], isAdmin);
if (resp?.status == 200 && resp?.data) {
return {
statusCode: 200,
Expand Down Expand Up @@ -92,20 +92,29 @@ export const getDeployableJobs = async (
) => {
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 ?? '';

Expand All @@ -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;
}
Expand All @@ -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 = '';
Expand Down Expand Up @@ -209,12 +216,24 @@ export const DeployRepo = async (event: APIGatewayEvent): Promise<APIGatewayProx
const parsed = JSON.parse(decoded);
const stateValues = parsed.view.state.values;

//TODO: create an interface for slack view_submission payloads
if (parsed.type !== 'view_submission') {
return prepResponse(200, 'text/plain', 'Form not submitted, will not process request');
}

const entitlement = await repoEntitlementRepository.getRepoEntitlementsBySlackUserId(parsed.user.id);
if (!isUserEntitled(entitlement)) {
return prepResponse(401, 'text/plain', 'User is not entitled!');
}

const values = slackConnector.parseSelection(stateValues, entitlement, repoBranchesRepository);
let values = [];
const isAdmin = await repoEntitlementRepository.getIsAdmin(parsed.user.id);
try {
values = await slackConnector.parseSelection(stateValues, isAdmin, repoBranchesRepository);
} catch (e) {
console.log(`Error parsing selection: ${e}`);
return prepResponse(401, 'text/plain', e);
}

const deployable = await getDeployableJobs(values, entitlement, repoBranchesRepository, docsetsRepository);
if (deployable.length > 0) {
Expand Down
41 changes: 33 additions & 8 deletions api/handlers/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion cdk-infra/lib/constructs/api/webhook-api-construct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion src/repositories/jobRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
57 changes: 6 additions & 51 deletions src/services/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ export class SlackConnector implements ISlackConnector {
}

async displayRepoOptions(repos: string[], triggerId: string, isAdmin: boolean): Promise<any> {
const reposToShow = this._buildDropdown(repos);
const repoOptView = this._getDropDownView(triggerId, reposToShow, isAdmin);
const repoOptView = this._getDropDownView(triggerId, repos, isAdmin);
const slackToken = this._config.get<string>('slackAuthToken');
const slackUrl = this._config.get<string>('slackViewOpenUrl');
return await axiosApi.post(slackUrl, repoOptView, {
Expand Down Expand Up @@ -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,
},
},
{
Expand All @@ -237,42 +230,4 @@ export class SlackConnector implements ISlackConnector {
},
};
}

private _buildDropdown(branches: Array<string>): Array<any> {
let reposToShow: Array<any> = [];
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;
}
}

0 comments on commit 3a93a7b

Please sign in to comment.