Skip to content

Commit

Permalink
Merge branch 'main' into DOP-4401-c
Browse files Browse the repository at this point in the history
  • Loading branch information
anabellabuckvar authored Apr 15, 2024
2 parents 8e15403 + 1a2ff58 commit 32a1a2f
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 46 deletions.
85 changes: 58 additions & 27 deletions api/controllers/v1/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RepoEntitlementsRepository } from '../../../src/repositories/repoEntitl
import { RepoBranchesRepository } from '../../../src/repositories/repoBranchesRepository';
import { ConsoleLogger, ILogger } from '../../../src/services/logger';
import { SlackConnector } from '../../../src/services/slack';
import { APIGatewayEvent, APIGatewayProxyResult } from 'aws-lambda';
import { JobRepository } from '../../../src/repositories/jobRepository';
import {
buildEntitledBranchList,
Expand All @@ -13,13 +14,23 @@ import {
prepResponse,
} from '../../handlers/slack';
import { DocsetsRepository } from '../../../src/repositories/docsetsRepository';
import { Payload } from '../../../src/entities/job';

export const DisplayRepoOptions = async (event: any = {}, context: any = {}): Promise<any> => {
export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGatewayProxyResult> => {
const consoleLogger = new ConsoleLogger();
const slackConnector = new SlackConnector(consoleLogger, c);

if (!slackConnector.validateSlackRequest(event)) {
return prepResponse(401, 'text/plain', 'Signature Mismatch, Authentication Failed!');
}

if (!event.body) {
return {
statusCode: 400,
body: 'Event body is undefined',
};
}

const client = new mongodb.MongoClient(c.get('dbUrl'));
await client.connect();
const db = client.db(process.env.DB_NAME);
Expand All @@ -34,8 +45,11 @@ export const DisplayRepoOptions = async (event: any = {}, context: any = {}): Pr
: 'User is not entitled!';
return prepResponse(401, 'text/plain', response);
}

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']);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], isAdmin);
if (resp?.status == 200 && resp?.data) {
return {
statusCode: 200,
Expand Down Expand Up @@ -71,22 +85,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 (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;
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 {
throw Error('Selected entitlement value is configured incorrectly. Check user entitlements!');
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 @@ -96,12 +117,11 @@ export const getDeployableJobs = async (
const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName, repoInfo.project);
if (!branchObject?.aliasObject) continue;

// TODO: Create strong typing for these rather than comments
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 @@ -118,12 +138,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 @@ -164,7 +182,7 @@ export const getDeployableJobs = async (
return deployable;
};

export const DeployRepo = async (event: any = {}, context: any = {}): Promise<any> => {
export const DeployRepo = async (event: any = {}): Promise<any> => {
const consoleLogger = new ConsoleLogger();
const slackConnector = new SlackConnector(consoleLogger, c);
if (!slackConnector.validateSlackRequest(event)) {
Expand All @@ -173,6 +191,7 @@ export const DeployRepo = async (event: any = {}, context: any = {}): Promise<an
const client = new mongodb.MongoClient(c.get('dbUrl'));
await client.connect();
const db = client.db(c.get('dbName'));

const repoEntitlementRepository = new RepoEntitlementsRepository(db, c, consoleLogger);
const repoBranchesRepository = new RepoBranchesRepository(db, c, consoleLogger);
const docsetsRepository = new DocsetsRepository(db, c, consoleLogger);
Expand All @@ -183,14 +202,26 @@ export const DeployRepo = async (event: any = {}, context: any = {}): Promise<an
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);

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) {
await deployRepo(deployable, consoleLogger, jobRepository, c.get('jobsQueueUrl'));
}
Expand Down Expand Up @@ -235,7 +266,7 @@ function createPayload(
};
}

function createJob(payload: any, jobTitle: string, jobUserName: string, jobUserEmail: string) {
function createJob(payload: Payload, jobTitle: string, jobUserName: string, jobUserEmail: string) {
return {
title: jobTitle,
user: jobUserName,
Expand Down
15 changes: 10 additions & 5 deletions api/controllers/v2/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat
const consoleLogger = new ConsoleLogger();
const slackConnector = new SlackConnector(consoleLogger, c);

if (!slackConnector.validateSlackRequest(event)) {
return prepResponse(401, 'text/plain', 'Signature Mismatch, Authentication Failed!');
}

if (!event.body) {
return {
statusCode: 400,
body: 'Event body is undefined',
};
}

if (!slackConnector.validateSlackRequest(event)) {
return prepResponse(401, 'text/plain', 'Signature Mismatch, Authentication Failed!');
}
const client = new mongodb.MongoClient(c.get('dbUrl'));
await client.connect();
const db = client.db(process.env.DB_NAME);
Expand All @@ -44,8 +45,11 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat
: 'User is not entitled!';
return prepResponse(401, 'text/plain', response);
}

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

const entitledBranches = await buildEntitledBranchList(entitlement, repoBranchesRepository);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id']);
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], admin);
if (resp?.status == 200 && resp?.data) {
return {
statusCode: 200,
Expand Down Expand Up @@ -194,6 +198,7 @@ export const DeployRepo = async (event: APIGatewayEvent): Promise<APIGatewayProx
const client = new mongodb.MongoClient(c.get('dbUrl'));
await client.connect();
const db = client.db(c.get('dbName'));

const repoEntitlementRepository = new RepoEntitlementsRepository(db, c, consoleLogger);
const repoBranchesRepository = new RepoBranchesRepository(db, c, consoleLogger);
const docsetsRepository = new DocsetsRepository(db, c, consoleLogger);
Expand All @@ -209,7 +214,7 @@ export const DeployRepo = async (event: APIGatewayEvent): Promise<APIGatewayProx
return prepResponse(401, 'text/plain', 'User is not entitled!');
}

const values = slackConnector.parseSelection(stateValues);
const values = slackConnector.parseSelection(stateValues, entitlement, repoBranchesRepository);

const deployable = await getDeployableJobs(values, entitlement, repoBranchesRepository, docsetsRepository);
if (deployable.length > 0) {
Expand Down
9 changes: 8 additions & 1 deletion src/repositories/repoBranchesRepository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Db } from 'mongodb';
import { Document, Db } from 'mongodb';
import { BaseRepository } from './baseRepository';
import { ILogger } from '../services/logger';
import { IConfig } from 'config';
Expand Down Expand Up @@ -32,6 +32,13 @@ export class RepoBranchesRepository extends BaseRepository {
return repo?.['branches'] ?? [];
}

async getProdDeployableRepoBranches(): Promise<Document[]> {
const reposArray = await this._collection
.aggregate([{ $match: { prodDeployable: true, internalOnly: false } }, { $project: { _id: 0, repoName: 1 } }])
.toArray();
return reposArray ?? [];
}

async getRepoBranchAliases(repoName: string, branchName: string, project: string): Promise<any> {
const returnObject = { status: 'failure' };
const aliasArray = await this._collection
Expand Down
9 changes: 9 additions & 0 deletions src/repositories/repoEntitlementsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ export class RepoEntitlementsRepository extends BaseRepository {
}
}

async getIsAdmin(slackUserId: string): Promise<boolean> {
const query = { slack_user_id: slackUserId };
const entitlementsObject = await this.findOne(
query,
`Mongo Timeout Error: Timedout while retrieving entitlements for ${slackUserId}`
);
return entitlementsObject?.admin;
}

async getGatsbySiteIdByGithubUsername(githubUsername: string): Promise<string | undefined> {
return this.getBuildHookByGithubUsername(githubUsername, 'gatsby_site_id');
}
Expand Down
Loading

0 comments on commit 32a1a2f

Please sign in to comment.