Skip to content

Commit

Permalink
Merge branch 'main' into DOP-4451
Browse files Browse the repository at this point in the history
  • Loading branch information
anabellabuckvar authored May 6, 2024
2 parents e532d41 + ebebd37 commit ecbfdec
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 66 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ RUN cd ./modules/oas-page-builder \
# where repo work will happen
FROM ubuntu:20.04
ARG WORK_DIRECTORY=/home/docsworker-xlarge
ARG SNOOTY_PARSER_VERSION=0.16.5
ARG SNOOTY_FRONTEND_VERSION=0.16.9
ARG SNOOTY_PARSER_VERSION=0.16.6
ARG SNOOTY_FRONTEND_VERSION=0.16.13
ARG MUT_VERSION=0.11.2
ARG REDOC_CLI_VERSION=1.2.3
ARG NPM_BASE_64_AUTH
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.local
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM arm64v8/ubuntu:20.04 as initial
ARG NPM_BASE_64_AUTH
ARG NPM_EMAIL
ARG SNOOTY_PARSER_VERSION=0.16.5
ARG SNOOTY_FRONTEND_VERSION=0.16.9
ARG SNOOTY_PARSER_VERSION=0.16.6
ARG SNOOTY_FRONTEND_VERSION=0.16.13
ARG MUT_VERSION=0.11.2
ARG REDOC_CLI_VERSION=1.2.3
ARG NPM_BASE_64_AUTH
Expand Down
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
18 changes: 18 additions & 0 deletions infrastructure/ecs-main/buckets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ Resources:
Protocol: "https"
HostName: ${self:custom.site.host.${self:provider.stage}}
ReplaceKeyPrefixWith: ${self:custom.site.prefix.${self:provider.stage}}/php-library/upcoming
- RoutingRuleCondition:
KeyPrefixEquals: ${self:custom.site.prefix.${self:provider.stage}}/realm/sdk
RedirectRule:
Protocol: "https"
HostName: ${self:custom.site.host.${self:provider.stage}}
ReplaceKeyPrefixWith: ${self:custom.site.prefix.${self:provider.stage}}/atlas/device-sdks/sdk
- RoutingRuleCondition:
KeyPrefixEquals: ${self:custom.site.prefix.${self:provider.stage}}/realm/web
RedirectRule:
Protocol: "https"
HostName: ${self:custom.site.host.${self:provider.stage}}
ReplaceKeyPrefixWith: ${self:custom.site.prefix.${self:provider.stage}}/atlas/device-sdks/web
- RoutingRuleCondition:
KeyPrefixEquals: ${self:custom.site.prefix.${self:provider.stage}}/realm/studio
RedirectRule:
Protocol: "https"
HostName: ${self:custom.site.host.${self:provider.stage}}
ReplaceKeyPrefixWith: ${self:custom.site.prefix.${self:provider.stage}}/atlas/device-sdks/studio
DocAtlasBucket:
Type: "AWS::S3::Bucket"
Properties:
Expand Down
1 change: 1 addition & 0 deletions src/job/jobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export abstract class JobHandler {
// TODO: Can empty string check be removed?
if (pathPrefix || pathPrefix === '') {
this.currJob.payload.pathPrefix = pathPrefix;
//sets mutPrefix to the full pathPrefix unless server user is in the path (I believe this only happens in a subset of cases when docs-worker-xlarge is the server-user)
const mutPrefix = pathPrefix.split(`/${server_user}`)[0];
this.currJob.payload.mutPrefix = mutPrefix;
}
Expand Down
23 changes: 8 additions & 15 deletions src/job/productionJobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,15 @@ export class ProductionJobHandler extends JobHandler {
}

getPathPrefix(): string {
try {
if (this.currJob.payload.prefix && this.currJob.payload.prefix === '') {
return this.currJob.payload.urlSlug ?? '';
}
if (this.currJob.payload.urlSlug) {
if (this.currJob.payload.urlSlug === '') {
return this.currJob.payload.prefix;
} else {
return `${this.currJob.payload.prefix}/${this.currJob.payload.urlSlug}`;
}
}
return this.currJob.payload.prefix;
} catch (error) {
this.logger.save(this.currJob._id, error).then();
throw new InvalidJobError(error.message);
const prefix = this.currJob.payload.urlSlug
? `${this.currJob.payload.prefix}/${this.currJob.payload.urlSlug}`
: this.currJob.payload.prefix;
if (this.currJob.payload.action == 'automatedTest') {
const titleArray = this.currJob.title.split(' ');
const commitHash = titleArray[titleArray.length - 5];
return `${prefix}/smokeTests/${commitHash}`;
}
return prefix;
}

private async purgePublishedContent(makefileOutput: Array<string>): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/job/stagingJobHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class StagingJobHandler extends JobHandler {
prepDeployCommands(): void {
this.currJob.deployCommands = [
`cd repos/${getDirectory(this.currJob)}`,
`make next-gen-stage${this.currJob.payload.pathPrefix ? `MUT_PREFIX=${this.currJob.payload.mutPrefix}` : ''}`,
`make next-gen-stage${this.currJob.payload.pathPrefix ? ` MUT_PREFIX=${this.currJob.payload.mutPrefix}` : ''}`,
];
}

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 ecbfdec

Please sign in to comment.