diff --git a/Dockerfile b/Dockerfile index e40bb7f91..17502a020 100644 --- a/Dockerfile +++ b/Dockerfile @@ -74,7 +74,7 @@ USER docsworker-xlarge WORKDIR ${WORK_DIRECTORY} # get shared.mk -RUN curl https://raw.githubusercontent.com/mongodb/docs-worker-pool/meta/makefiles/shared.mk -o shared.mk +RUN curl https://raw.githubusercontent.com/mongodb/docs-worker-pool/monorepo-pub-branches/makefiles/shared.mk -o shared.mk # install snooty frontend and docs-tools RUN git clone -b v${SNOOTY_FRONTEND_VERSION} --depth 1 https://github.com/mongodb/snooty.git \ diff --git a/Dockerfile.enhanced b/Dockerfile.enhanced index 2eeccb459..3ca920e1e 100644 --- a/Dockerfile.enhanced +++ b/Dockerfile.enhanced @@ -74,7 +74,7 @@ USER docsworker-xlarge WORKDIR ${WORK_DIRECTORY} # get shared.mk -RUN curl https://raw.githubusercontent.com/mongodb/docs-worker-pool/meta/makefiles/shared.mk -o shared.mk +RUN curl https://raw.githubusercontent.com/mongodb/docs-worker-pool/monorepo-pub-branches/makefiles/shared.mk -o shared.mk # install snooty frontend and docs-tools RUN git clone -b v${SNOOTY_FRONTEND_VERSION} --depth 1 https://github.com/mongodb/snooty.git \ diff --git a/api/controllers/v1/github.ts b/api/controllers/v1/github.ts index 36471662a..db741457f 100644 --- a/api/controllers/v1/github.ts +++ b/api/controllers/v1/github.ts @@ -14,7 +14,11 @@ async function prepGithubPushPayload( repoInfo: ReposBranchesDocument ) { const branch_name = githubEvent.ref.split('/')[2]; - const branch_info = await repoBranchesRepository.getRepoBranchAliases(githubEvent.repository.name, branch_name); + const branch_info = await repoBranchesRepository.getRepoBranchAliases( + githubEvent.repository.name, + branch_name, + repoInfo.project + ); const urlSlug = branch_info.aliasObject?.urlSlug ?? branch_name; const project = repoInfo?.project ?? githubEvent.repository.name; diff --git a/api/controllers/v1/jobs.ts b/api/controllers/v1/jobs.ts index 38ea304da..4b116e19c 100644 --- a/api/controllers/v1/jobs.ts +++ b/api/controllers/v1/jobs.ts @@ -261,7 +261,7 @@ async function SubmitArchiveJob(jobId: string) { repoBranches: new DocsetsRepository(db, c, consoleLogger), }; const job = await models.jobs.getJobById(jobId); - const repo = await models.repoBranches.getRepo(job.payload.repoName); + const repo = await models.repoBranches.getRepo(job.payload.repoName, job?.payload.directory); /* NOTE * we don't archive landing for two reasons: diff --git a/api/controllers/v1/slack.ts b/api/controllers/v1/slack.ts index f34665af8..6f208b7e2 100644 --- a/api/controllers/v1/slack.ts +++ b/api/controllers/v1/slack.ts @@ -72,20 +72,32 @@ export const getDeployableJobs = async ( const deployable = []; for (let i = 0; i < values.repo_option.length; i++) { - // e.g. mongodb/docs-realm/master => (site/repo/branch) - const [repoOwner, repoName, branchName] = values.repo_option[i].value.split('/'); + 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; + } 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 ?? ''; - const repoInfo = await docsetsRepository.getRepo(repoName); + const repoInfo = await docsetsRepository.getRepo(repoName, directory); const non_versioned = repoInfo.branches.length === 1; - const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName); + const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName, repoInfo.project); if (!branchObject?.aliasObject) continue; - const publishOriginalBranchName = branchObject.aliasObject.publishOriginalBranchName; //bool + // 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 @@ -106,7 +118,8 @@ export const getDeployableJobs = async ( urlSlug, false, false, - false + false, + directory ); newPayload.stable = !!isStableBranch; @@ -198,7 +211,8 @@ function createPayload( urlSlug, aliased = false, primaryAlias = false, - stable = false + stable = false, + directory?: string ) { return { jobType, @@ -217,6 +231,7 @@ function createPayload( newHead, primaryAlias, stable, + directory, }; } diff --git a/api/controllers/v2/github.ts b/api/controllers/v2/github.ts index bfa145bec..a6e37fea6 100644 --- a/api/controllers/v2/github.ts +++ b/api/controllers/v2/github.ts @@ -12,6 +12,7 @@ import { DocsetsRepository } from '../../../src/repositories/docsetsRepository'; import { getMonorepoPaths } from '../../../src/monorepo'; import { getUpdatedFilePaths } from '../../../src/monorepo/utils/path-utils'; import { ReposBranchesDocument } from '../../../modules/persistence/src/services/metadata/associated_products'; +import { MONOREPO_NAME } from '../../../src/monorepo/utils/monorepo-constants'; async function prepGithubPushPayload( githubEvent: PushEvent, @@ -20,7 +21,11 @@ async function prepGithubPushPayload( repoInfo: ReposBranchesDocument ): Promise> { const branch_name = githubEvent.ref.split('/')[2]; - const branch_info = await repoBranchesRepository.getRepoBranchAliases(githubEvent.repository.name, branch_name); + const branch_info = await repoBranchesRepository.getRepoBranchAliases( + githubEvent.repository.name, + branch_name, + repoInfo.project + ); const urlSlug = branch_info.aliasObject?.urlSlug ?? branch_name; const project = repoInfo?.project ?? githubEvent.repository.name; @@ -101,32 +106,60 @@ export const TriggerBuild = async (event: APIGatewayEvent): Promise('env'); - const repoInfo = await docsetsRepository.getRepo(body.repository.name); - const jobPrefix = repoInfo?.prefix ? repoInfo['prefix'][env] : ''; - const job = await prepGithubPushPayload(body, repoBranchesRepository, jobPrefix, repoInfo); + async function createAndInsertJob(path?: string) { + const repoInfo = await docsetsRepository.getRepo(body.repository.name, path); + const jobPrefix = repoInfo?.prefix ? repoInfo['prefix'][env] : ''; + const job = await prepGithubPushPayload(body, repoBranchesRepository, jobPrefix, repoInfo); - if (process.env.MONOREPO_PATH_FEATURE === 'true') { + consoleLogger.info(job.title, 'Creating Job'); + const jobId = await jobRepository.insertJob(job, c.get('jobsQueueUrl')); + jobRepository.notify(jobId, c.get('jobUpdatesQueueUrl'), JobStatus.inQueue, 0); + consoleLogger.info(job.title, `Created Job ${jobId}`); + } + + if (process.env.FEATURE_FLAG_MONOREPO_PATH === 'true' && body.repository.name === MONOREPO_NAME) { + let monorepoPaths: string[] = []; try { if (body.head_commit && body.repository.owner.name) { - const monorepoPaths = await getMonorepoPaths({ + monorepoPaths = await getMonorepoPaths({ commitSha: body.head_commit.id, repoName: body.repository.name, ownerName: body.repository.owner.name, updatedFilePaths: getUpdatedFilePaths(body.head_commit), }); - console.log('monorepoPaths: ', monorepoPaths); + consoleLogger.info('monoRepoPaths', `Monorepo Paths with new changes: ${monorepoPaths}`); } } catch (error) { console.warn('Warning, attempting to get repo paths caused an error', error); } + + /* Create and insert Job for each monorepo project that has changes */ + for (const path of monorepoPaths) { + // TODO: Deal with nested monorepo projects + /* For now, we will ignore nested monorepo projects until necessary */ + if (path.split('/').length > 1) continue; + + try { + await createAndInsertJob(`/${path}`); + } catch (err) { + return { + statusCode: 500, + headers: { 'Content-Type': 'text/plain' }, + body: err, + }; + } + } + + return { + statusCode: 202, + headers: { 'Content-Type': 'text/plain' }, + body: 'Jobs Queued', + }; } try { - consoleLogger.info(job.title, 'Creating Job'); - const jobId = await jobRepository.insertJob(job, c.get('jobsQueueUrl')); - jobRepository.notify(jobId, c.get('jobUpdatesQueueUrl'), JobStatus.inQueue, 0); - consoleLogger.info(job.title, `Created Job ${jobId}`); + await createAndInsertJob(); } catch (err) { return { statusCode: 500, diff --git a/api/controllers/v2/slack.ts b/api/controllers/v2/slack.ts index 7e8c2d2ec..68afa8eec 100644 --- a/api/controllers/v2/slack.ts +++ b/api/controllers/v2/slack.ts @@ -89,17 +89,28 @@ export const getDeployableJobs = async ( const deployable = []; for (let i = 0; i < values.repo_option.length; i++) { - // e.g. mongodb/docs-realm/master => (site/repo/branch) - const [repoOwner, repoName, branchName] = values.repo_option[i].value.split('/'); + 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; + } 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 ?? ''; - const repoInfo = await docsetsRepository.getRepo(repoName); + const repoInfo = await docsetsRepository.getRepo(repoName, directory); const non_versioned = repoInfo.branches.length === 1; - const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName); + const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName, repoInfo.project); if (!branchObject?.aliasObject) continue; const publishOriginalBranchName = branchObject.aliasObject.publishOriginalBranchName; //bool @@ -123,7 +134,8 @@ export const getDeployableJobs = async ( urlSlug, false, false, - false + false, + directory ); newPayload.stable = !!isStableBranch; @@ -223,7 +235,8 @@ function createPayload( urlSlug, aliased = false, primaryAlias = false, - stable = false + stable = false, + directory?: string ) { return { jobType, @@ -241,6 +254,7 @@ function createPayload( newHead, primaryAlias, stable, + directory, }; } diff --git a/api/handlers/jobs.ts b/api/handlers/jobs.ts index 2c71813a1..8aedd6ffd 100644 --- a/api/handlers/jobs.ts +++ b/api/handlers/jobs.ts @@ -82,7 +82,13 @@ async function prepSummaryMessage( if (repoName == 'mms-docs') { msg = `Your Job <${jobUrl}${jobId}|Completed>! \n- Repo: *${repoName}*\n- Branch: *${fullDocument.payload.branchName}*\n- Commit: *${fullDocument.payload.newHead}*\n- urlSlug: *${fullDocument.payload.urlSlug}*\n- Env: *${env}*\n*Urls*\n *CM*: <${mms_urls[0]}|Cloud Manager> \n *OPM*: <${mms_urls[1]}|OPS Manager>\n- InvalidationStatus: <${fullDocument.invalidationStatusURL}|Status> \nEnjoy :smile:!`; } else { - msg = `Your Job <${jobUrl}${jobId}|Completed>! \n- Repo: *${repoName}*\n- Branch: *${fullDocument.payload.branchName}*\n- Commit: *${fullDocument.payload.newHead}*\n- urlSlug: *${fullDocument.payload.urlSlug}*\n- Env: *${env}*\n- Url: <${url}|${repoName}>\n- InvalidationStatus: <${fullDocument.invalidationStatusURL}|Status> \nEnjoy :smile:!`; + msg = `Your Job <${jobUrl}${jobId}|Completed>! \n- Repo: *${repoName}${ + fullDocument.payload.directory ? `/${fullDocument.payload.directory}` : `` + }*\n- Branch: *${fullDocument.payload.branchName}*\n- Commit: *${fullDocument.payload.newHead}*\n- urlSlug: *${ + fullDocument.payload.urlSlug + }*\n- Env: *${env}*\n- Url: <${url}|${fullDocument.payload.directory ?? repoName}>\n- InvalidationStatus: <${ + fullDocument.invalidationStatusURL + }|Status> \nEnjoy :smile:!`; } } // Remove instances of two or more periods diff --git a/api/handlers/slack.ts b/api/handlers/slack.ts index 2ceabc94d..a13df5e4c 100644 --- a/api/handlers/slack.ts +++ b/api/handlers/slack.ts @@ -21,15 +21,17 @@ export function prepResponse(statusCode, contentType, body) { export async function buildEntitledBranchList(entitlement: any, repoBranchesRepository: RepoBranchesRepository) { const entitledBranches: string[] = []; for (const repo of entitlement.repos) { - const [repoOwner, repoName] = repo.split('/'); - const branches = await repoBranchesRepository.getRepoBranches(repoName); + const [repoOwner, repoName, directoryPath] = repo.split('/'); + const branches = await repoBranchesRepository.getRepoBranches(repoName, directoryPath); for (const branch of branches) { let buildWithSnooty = true; if ('buildsWithSnooty' in branch) { buildWithSnooty = branch['buildsWithSnooty']; } if (buildWithSnooty) { - entitledBranches.push(`${repoOwner}/${repoName}/${branch['gitBranchName']}`); + entitledBranches.push( + `${repoOwner}/${repoName}${directoryPath ? '/' + directoryPath : ''}/${branch['gitBranchName']}` + ); } } } diff --git a/cdk-infra/lib/constructs/api/webhook-env-construct.ts b/cdk-infra/lib/constructs/api/webhook-env-construct.ts index 8daf0af50..114db52dc 100644 --- a/cdk-infra/lib/constructs/api/webhook-env-construct.ts +++ b/cdk-infra/lib/constructs/api/webhook-env-construct.ts @@ -3,7 +3,7 @@ import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; import { getSsmPathPrefix } from '../../../utils/ssm'; import { getDashboardUrl } from '../../../utils/slack'; -import { getEnv, getFeatureName } from '../../../utils/env'; +import { getEnv } from '../../../utils/env'; interface WebhookEnvConstructProps { jobsQueue: IQueue; @@ -18,14 +18,8 @@ export class WebhookEnvConstruct extends Construct { const ssmPrefix = getSsmPathPrefix(); const env = getEnv(); - const featureName = getFeatureName(); - - // Create configurable feature flag that lives in parameter store. - const monorepoPathFeature = new StringParameter(this, 'monorepoPathFeature', { - parameterName: `${ssmPrefix}/${featureName}/monorepo/path_feature`, - stringValue: env === 'dotcomstg' || env === 'stg' ? 'true' : 'false', - }); + const featureFlagMonorepoPath = StringParameter.valueFromLookup(this, `${ssmPrefix}/flag/monorepo_path`); const dbName = StringParameter.valueFromLookup(this, `${ssmPrefix}/atlas/dbname`); const snootyDbName = StringParameter.valueFromLookup(this, `${ssmPrefix}/atlas/collections/snooty`); const repoBranchesCollection = StringParameter.valueFromLookup(this, `${ssmPrefix}/atlas/collections/repo`); @@ -56,7 +50,7 @@ export class WebhookEnvConstruct extends Construct { USER_ENTITLEMENT_COL_NAME: entitlementCollection, DASHBOARD_URL: getDashboardUrl(env, jobCollection), STAGE: env, - MONOREPO_PATH_FEATURE: monorepoPathFeature.stringValue, + FEATURE_FLAG_MONOREPO_PATH: featureFlagMonorepoPath.stringValue, }; } } diff --git a/cdk-infra/lib/constructs/worker/worker-env-construct.ts b/cdk-infra/lib/constructs/worker/worker-env-construct.ts index ec439118f..ed39f0de8 100644 --- a/cdk-infra/lib/constructs/worker/worker-env-construct.ts +++ b/cdk-infra/lib/constructs/worker/worker-env-construct.ts @@ -50,6 +50,8 @@ export class WorkerEnvConstruct extends Construct { // doing this for the time being, but I think we don't need to necessarily retrieve this from ssm for feature branches, nor would we want to in that case const previewBuildEnabled = StringParameter.valueFromLookup(this, `${ssmPrefix}/flag/preview_build/enabled`); const featureFlagUpdatePages = StringParameter.valueFromLookup(this, `${ssmPrefix}/flag/update_pages`); + const featureFlagMonorepoPath = StringParameter.valueFromLookup(this, `${ssmPrefix}/flag/monorepo_path`); + const entitlementCollection = StringParameter.valueFromLookup( this, `${ssmPrefix}/atlas/collections/user/entitlements` @@ -76,6 +78,7 @@ export class WorkerEnvConstruct extends Construct { GATSBY_BASE_URL: gatsbyBaseUrl, PREVIEW_BUILD_ENABLED: previewBuildEnabled, FEATURE_FLAG_UPDATE_PAGES: featureFlagUpdatePages, + FEATURE_FLAG_MONOREPO_PATH: featureFlagMonorepoPath, USER_ENTITLEMENT_COL_NAME: entitlementCollection, NPM_EMAIL: npmEmail, REPO_BRANCHES_COL_NAME: repoBranchesCollection, diff --git a/infrastructure/ecs-main/ecs_service.yml b/infrastructure/ecs-main/ecs_service.yml index de1422758..e5799746f 100644 --- a/infrastructure/ecs-main/ecs_service.yml +++ b/infrastructure/ecs-main/ecs_service.yml @@ -62,6 +62,8 @@ Resources: Value: ${self:custom.previewBuildEnabled} - Name: FEATURE_FLAG_UPDATE_PAGES Value: ${self:custom.featureFlagUpdatePages} + - Name: FEATURE_FLAG_MONOREPO_PATH + Value: ${self:custom.featureFlagMonorepoPath} - Name: GATSBY_TEST_SEARCH_UI Value: ${self:custom.featureFlagSearchUI} - Name: GATSBY_SHOW_CHATBOT diff --git a/infrastructure/ecs-main/serverless.yml b/infrastructure/ecs-main/serverless.yml index 9a55255f7..c08bc710a 100644 --- a/infrastructure/ecs-main/serverless.yml +++ b/infrastructure/ecs-main/serverless.yml @@ -115,6 +115,7 @@ custom: gatsbyCloudPreviewWebhookEnabled: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/preview_webhook_enable} previewBuildEnabled: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/preview_build/enabled} featureFlagUpdatePages: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/update_pages} + featureFlagMonorepoPath: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/monorepo_path} featureFlagSearchUI: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/search_ui} gatsbyTestEmbedVersions: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/embedded_versions} gatsbyUseChatbot: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/use_chatbot} diff --git a/serverless.yml b/serverless.yml index 0ac22c91d..72c4365f0 100644 --- a/serverless.yml +++ b/serverless.yml @@ -69,6 +69,7 @@ custom: slackSecret: ${ssm:/env/${self:provider.stage}/docs/worker_pool/slack/webhook/secret} slackAuthToken: ${ssm:/env/${self:provider.stage}/docs/worker_pool/slack/auth/token} snootySecret: ${ssm:/env/${self:provider.stage}/docs/worker_pool/snooty/webhook/secret} + featureFlagMonorepoPath: ${ssm:/env/${self:provider.stage}/docs/worker_pool/flag/monorepo_path} JobsQueueName: autobuilder-jobs-queue-${self:provider.stage} JobsDLQueueName: autobuilder-jobs-dlqueue-${self:provider.stage} JobUpdatesQueueName: autobuilder-job-updates-queue-${self:provider.stage} @@ -120,6 +121,7 @@ webhook-env-core: &webhook-env-core SLACK_SECRET: ${self:custom.slackSecret} SLACK_TOKEN: ${self:custom.slackAuthToken} SNOOTY_SECRET: ${self:custom.snootySecret} + FEATURE_FLAG_MONOREPO_PATH: ${self:custom.featureFlagMonorepoPath} DASHBOARD_URL: ${self:custom.dashboardUrl.${self:provider.stage}} NODE_CONFIG_DIR: './api/config' TASK_DEFINITION_FAMILY: docs-worker-pool-${self:provider.stage} diff --git a/src/enhanced/job/enhancedJobHandlers.ts b/src/enhanced/job/enhancedJobHandlers.ts index cdce4930f..78484a2ab 100644 --- a/src/enhanced/job/enhancedJobHandlers.ts +++ b/src/enhanced/job/enhancedJobHandlers.ts @@ -10,7 +10,10 @@ import { StagingJobHandler } from '../../job/stagingJobHandler'; * @param this reference to current object */ async function setEnvironmentVariablesEnhanced(this: JobHandler) { - const repo_info = await this._docsetsRepo.getRepoBranchesByRepoName(this.currJob.payload.repoName); + const repo_info = await this._docsetsRepo.getRepoBranchesByRepoName( + this.currJob.payload.repoName, + this.currJob.payload.project + ); let env = this._config.get('env'); this.logger.info( diff --git a/src/entities/job.ts b/src/entities/job.ts index eb2d9121a..d09ca1c96 100644 --- a/src/entities/job.ts +++ b/src/entities/job.ts @@ -43,6 +43,7 @@ export type Payload = { prefix: string; project: string; includeInGlobalSearch: boolean; + directory?: string; }; export type EnhancedPayload = { diff --git a/src/job/jobHandler.ts b/src/job/jobHandler.ts index 7f93a9269..390521d3f 100644 --- a/src/job/jobHandler.ts +++ b/src/job/jobHandler.ts @@ -12,6 +12,7 @@ import { IConfig } from 'config'; import { IJobValidator } from './jobValidator'; import { RepoEntitlementsRepository } from '../repositories/repoEntitlementsRepository'; import { DocsetsRepository } from '../repositories/docsetsRepository'; +import { MONOREPO_NAME } from '../monorepo/utils/monorepo-constants'; require('fs'); export abstract class JobHandler { @@ -98,7 +99,7 @@ export abstract class JobHandler { if (publishResult) { if (publishResult?.status === 'success') { const files = this._fileSystemServices.getFilesInDirectory( - `./${this.currJob.payload.repoName}/build/public`, + `./${getDirectory(this.currJob)}/build/public`, '', null, null @@ -135,7 +136,7 @@ export abstract class JobHandler { } private cleanup(): void { - this._fileSystemServices.removeDirectory(`repos/${this.currJob.payload.repoName}`); + this._fileSystemServices.removeDirectory(`repos/${getDirectory(this.currJob)}`); } @throwIfJobInterupted() @@ -204,13 +205,13 @@ export abstract class JobHandler { @throwIfJobInterupted() private async downloadMakeFile(): Promise { try { - this._logger.info( - this.currJob._id, - `https://raw.githubusercontent.com/mongodb/docs-worker-pool/meta/makefiles/Makefile.${this.currJob.payload.repoName}` - ); + const makefileFileName = + this.currJob.payload.repoName === MONOREPO_NAME + ? this.currJob.payload.directory + : this.currJob.payload.repoName; await this._fileSystemServices.saveUrlAsFile( - `https://raw.githubusercontent.com/mongodb/docs-worker-pool/meta/makefiles/Makefile.${this.currJob.payload.repoName}`, - `repos/${this.currJob.payload.repoName}/Makefile`, + `https://raw.githubusercontent.com/mongodb/docs-worker-pool/monorepo-pub-branches/makefiles/Makefile.${makefileFileName}`, + `repos/${getDirectory(this.currJob)}/Makefile`, { encoding: 'utf8', flag: 'w', @@ -224,7 +225,7 @@ export abstract class JobHandler { @throwIfJobInterupted() public isbuildNextGen(): boolean { - const workerPath = `repos/${this.currJob.payload.repoName}/worker.sh`; + const workerPath = `repos/${getDirectory(this.currJob)}/worker.sh`; if (this._fileSystemServices.rootFileExists(workerPath)) { const workerContents = this._fileSystemServices.readFileAsUtf8(workerPath); const workerLines = workerContents.split(/\r?\n/); @@ -272,11 +273,10 @@ export abstract class JobHandler { // call this method when we want benchmarks and uses cwd option to call command outside of a one liner. private async callWithBenchmark(command: string, stage: string): Promise { const start = performance.now(); - const resp = await this._commandExecutor.execute([command], `repos/${this.currJob.payload.repoName}`); - await this._logger.save( - this.currJob._id, - `${'(COMMAND)'.padEnd(15)} ${command} run details in ${this.currJob.payload.repoName}` - ); + const pathToRepo = `repos/${getDirectory(this.currJob)}`; + const resp = await this._commandExecutor.execute([command], pathToRepo); + + await this._logger.save(this.currJob._id, `${'(COMMAND)'.padEnd(15)} ${command} run details in ${pathToRepo}`); const end = performance.now(); const update = { [`${stage}StartTime`]: start, @@ -380,7 +380,8 @@ export abstract class JobHandler { for (const [envName, envValue] of Object.entries(snootyFrontEndVars)) { if (envValue) envVars += `${envName}=${envValue}\n`; } - this._fileSystemServices.writeToFile(`repos/${this.currJob.payload.repoName}/.env.production`, envVars, { + const fileToWriteTo = `repos/${getDirectory(this.currJob)}/.env.production`; + this._fileSystemServices.writeToFile(fileToWriteTo, envVars, { encoding: 'utf8', flag: 'w', }); @@ -430,18 +431,21 @@ export abstract class JobHandler { protected prepBuildCommands(): void { this.currJob.buildCommands = [ `. /venv/bin/activate`, - `cd repos/${this.currJob.payload.repoName}`, + `cd repos/${getDirectory(this.currJob)}`, `rm -f makefile`, `make html`, ]; } protected async setEnvironmentVariables(): Promise { - const repo_info = await this._docsetsRepo.getRepoBranchesByRepoName(this._currJob.payload.repoName); + const repo_info = await this._docsetsRepo.getRepoBranchesByRepoName( + this._currJob.payload.repoName, + this._currJob.payload.project + ); let env = this._config.get('env'); this._logger.info( this._currJob._id, - `setEnvironmentVariables for ${this._currJob.payload.repoName} env ${env} jobType ${this._currJob.payload.jobType}` + `setEnvironmentVariables for ${getDirectory(this._currJob)} env ${env} jobType ${this._currJob.payload.jobType}` ); if (repo_info?.['bucket'] && repo_info?.['url']) { if (this._currJob.payload.regression) { @@ -470,21 +474,21 @@ export abstract class JobHandler { protected async build(): Promise { this.cleanup(); await this.cloneRepo(this._config.get('repo_dir')); - this._logger.info(this._currJob._id, 'Cloned Repo'); + this._logger.save(this._currJob._id, 'Cloned Repo'); await this.commitCheck(); - this._logger.info(this._currJob._id, 'Checked Commit'); + this._logger.save(this._currJob._id, 'Checked Commit'); await this.pullRepo(); - this._logger.info(this._currJob._id, 'Pulled Repo'); + this._logger.save(this._currJob._id, 'Pulled Repo'); this.prepBuildCommands(); - this._logger.info(this._currJob._id, 'Prepared Build commands'); + this._logger.save(this._currJob._id, 'Prepared Build commands'); await this.prepNextGenBuild(); - this._logger.info(this._currJob._id, 'Prepared Next Gen build'); + this._logger.save(this._currJob._id, 'Prepared Next Gen build'); await this._repoConnector.applyPatch(this.currJob); - this._logger.info(this._currJob._id, 'Patch Applied'); + this._logger.save(this._currJob._id, 'Patch Applied'); await this.downloadMakeFile(); - this._logger.info(this._currJob._id, 'Downloaded Makefile'); + this._logger.save(this._currJob._id, 'Downloaded Makefile'); await this.setEnvironmentVariables(); - this._logger.info(this._currJob._id, 'Prepared Environment variables'); + this._logger.save(this._currJob._id, 'Prepared Environment variables'); return await this.executeBuild(); } @@ -558,7 +562,8 @@ export abstract class JobHandler { // TODO: Give 'shouldGenerateSearchManifest' boolean to users' control shouldGenerateSearchManifest(): boolean { const doNotSearchProperties = ['docs-landing', 'docs-404', 'docs-meta']; - if (doNotSearchProperties.includes(this.currJob.payload.repoName)) { + const localDirectory = getDirectory(this.currJob); + if (doNotSearchProperties.some((property) => localDirectory.includes(property))) { return false; } // Ensures that we only build a manifest for aliased properties if the job @@ -731,3 +736,10 @@ function throwIfJobInterupted() { return descriptor; }; } + +export function getDirectory(job: Job) { + const { payload } = job; + let directory = payload.repoName; + if (payload.repoName === MONOREPO_NAME && !!payload.directory) directory += `/${payload.directory}`; + return directory; +} diff --git a/src/job/jobValidator.ts b/src/job/jobValidator.ts index e1f266c99..9434bdb48 100644 --- a/src/job/jobValidator.ts +++ b/src/job/jobValidator.ts @@ -5,6 +5,7 @@ import { IFileSystemServices } from '../services/fileServices'; import { RepoEntitlementsRepository } from '../repositories/repoEntitlementsRepository'; import { RepoBranchesRepository } from '../repositories/repoBranchesRepository'; import { DocsetsRepository } from '../repositories/docsetsRepository'; +import { MONOREPO_NAME } from '../monorepo/utils/monorepo-constants'; export interface IJobValidator { throwIfJobInvalid(job: Job): Promise; @@ -32,13 +33,19 @@ export class JobValidator implements IJobValidator { async throwIfUserNotEntitled(job: Job): Promise { const entitlementsObject = await this._repoEntitlementRepository.getRepoEntitlementsByGithubUsername(job.user); - if (!entitlementsObject?.repos?.includes(`${job.payload.repoOwner}/${job.payload.repoName}`)) { - throw new AuthorizationError(`${job.user} is not entitled for repo ${job.payload.repoName}`); + const entitlementToFind = `${job.payload.repoOwner}/${job.payload.repoName}${ + job.payload.repoName === MONOREPO_NAME ? `/${job.payload.directory}` : `` + }`; + if (!entitlementsObject?.repos?.includes(entitlementToFind)) { + throw new AuthorizationError(`${job.user} is not entitled for repo ${entitlementToFind}`); } } async throwIfBranchNotConfigured(job: Job): Promise { - job.payload.repoBranches = await this._docsetsRepository.getRepoBranchesByRepoName(job.payload.repoName); + job.payload.repoBranches = await this._docsetsRepository.getRepoBranchesByRepoName( + job.payload.repoName, + job.payload.project + ); if (!job.payload?.repoBranches) { throw new AuthorizationError(`repoBranches not found for ${job.payload.repoName}`); } diff --git a/src/job/manifestJobHandler.ts b/src/job/manifestJobHandler.ts index 2f1b99602..bf1908902 100644 --- a/src/job/manifestJobHandler.ts +++ b/src/job/manifestJobHandler.ts @@ -1,7 +1,7 @@ // TODO: remove manifest job handler // not run as a separate job, handled in productionJobHandler prepSearchDeploy -import { JobHandler } from './jobHandler'; +import { getDirectory, JobHandler } from './jobHandler'; import { IConfig } from 'config'; import type { Job } from '../entities/job'; import { JobRepository } from '../repositories/jobRepository'; @@ -100,7 +100,7 @@ export class ManifestJobHandler extends JobHandler { // For mut-index usage info, see: https://github.com/mongodb/mut/blob/master/mut/index/main.py#L2 this.currJob.deployCommands = [ '. /venv/bin/activate', - `cd repos/${this.currJob.payload.repoName}`, + `cd repos/${getDirectory(this.currJob)}`, 'echo IGNORE: testing manifest generation deploy commands', 'ls -al', `mut-index upload bundle.zip -b ${b} -o ${f}/${maP}.json -u ${jUaP(url, muP || '')} ${globalSearch}`, diff --git a/src/job/productionJobHandler.ts b/src/job/productionJobHandler.ts index 1c933f1a3..8a394cd1c 100644 --- a/src/job/productionJobHandler.ts +++ b/src/job/productionJobHandler.ts @@ -10,7 +10,7 @@ import { CommandExecutorResponse, IJobCommandExecutor } from '../services/comman import { IFileSystemServices } from '../services/fileServices'; import { IJobRepoLogger } from '../services/logger'; import { IRepoConnector } from '../services/repo'; -import { JobHandler } from './jobHandler'; +import { getDirectory, JobHandler } from './jobHandler'; import { IJobValidator } from './jobValidator'; import { joinUrlAndPrefix } from './manifestJobHandler'; @@ -49,7 +49,7 @@ export class ProductionJobHandler extends JobHandler { // TODO: Can we simplify the chain of logic here? this.currJob.deployCommands = [ '. /venv/bin/activate', - `cd repos/${this.currJob.payload.repoName}`, + `cd repos/${getDirectory(this.currJob)}`, 'make publish && make deploy', ]; @@ -116,7 +116,7 @@ export class ProductionJobHandler extends JobHandler { } const searchCommands = [ '. /venv/bin/activate', - `cd repos/${this.currJob.payload.repoName}`, + `cd repos/${getDirectory(this.currJob)}`, 'echo IGNORE: testing manifest generation deploy commands', 'ls -al', // For mut-index usage info, see: https://github.com/mongodb/mut/blob/master/mut/index/main.py#L2 diff --git a/src/job/stagingJobHandler.ts b/src/job/stagingJobHandler.ts index 919443b04..d4822d926 100644 --- a/src/job/stagingJobHandler.ts +++ b/src/job/stagingJobHandler.ts @@ -1,4 +1,4 @@ -import { JobHandler } from './jobHandler'; +import { getDirectory, JobHandler } from './jobHandler'; import { IConfig } from 'config'; import type { Job } from '../entities/job'; import { JobRepository } from '../repositories/jobRepository'; @@ -46,7 +46,7 @@ export class StagingJobHandler extends JobHandler { prepDeployCommands(): void { // TODO: Can we make this more readable? - this.currJob.deployCommands = ['. /venv/bin/activate', `cd repos/${this.currJob.payload.repoName}`, 'make stage']; + this.currJob.deployCommands = ['. /venv/bin/activate', `cd repos/${getDirectory(this.currJob)}`, 'make stage']; if (this.currJob.payload.isNextGen) { if (this.currJob.payload.pathPrefix) { this.currJob.deployCommands[ diff --git a/src/monorepo/types/aggregation-types.ts b/src/monorepo/types/aggregation-types.ts new file mode 100644 index 000000000..467c9bbf6 --- /dev/null +++ b/src/monorepo/types/aggregation-types.ts @@ -0,0 +1,6 @@ +// Types to narrow matchCondition arguments for Docset aggregations +type DirectoriesKey = 'snooty_toml'; +type RepoBranchesKey = 'project' | 'branches' | 'repoName' | `directories.${DirectoriesKey}`; + +export type MatchCondition = { [key in RepoBranchesKey]+?: string }; +export type Projection = { [key in RepoBranchesKey]+?: number }; diff --git a/src/repositories/docsetsRepository.ts b/src/repositories/docsetsRepository.ts index bd673fba8..166483de1 100644 --- a/src/repositories/docsetsRepository.ts +++ b/src/repositories/docsetsRepository.ts @@ -2,6 +2,7 @@ import { Db } from 'mongodb'; import { BaseRepository } from './baseRepository'; import { ILogger } from '../services/logger'; import { IConfig } from 'config'; +import { MatchCondition, Projection } from '../monorepo/types/aggregation-types'; const docsetsCollectionName = process.env.DOCSETS_COL_NAME || 'docsets'; export class DocsetsRepository extends BaseRepository { @@ -9,17 +10,19 @@ export class DocsetsRepository extends BaseRepository { super(config, logger, 'DocsetsRepository', db.collection(docsetsCollectionName)); } - private getAggregationPipeline( - matchConditionField: string, - matchConditionValue: string, - projection?: { [k: string]: number } - ) { + static getAggregationPipeline(matchConditions: MatchCondition, projection?: Projection) { const DEFAULT_PROJECTIONS = { _id: 0, repos: 0, repo: 0, }; + // Add prefix 'repo' to each field in matchConditions + const formattedMatchConditions = Object.entries(matchConditions).reduce((acc, [key, val]) => { + acc[`repo.${key}`] = val; + return acc; + }, {}); + return [ // Stage 1: Unwind the repos array to create multiple documents for each referenced repo { @@ -36,9 +39,7 @@ export class DocsetsRepository extends BaseRepository { }, // Stage 3: Match documents based on given field { - $match: { - [`repo.${matchConditionField}`]: matchConditionValue, - }, + $match: formattedMatchConditions, }, // Stage 4: Merge/flatten repo into docset { @@ -53,7 +54,7 @@ export class DocsetsRepository extends BaseRepository { async getProjectByRepoName(repoName: string): Promise { const projection = { project: 1 }; - const aggregationPipeline = this.getAggregationPipeline('repoName', repoName, projection); + const aggregationPipeline = DocsetsRepository.getAggregationPipeline({ repoName }, projection); const cursor = await this.aggregate(aggregationPipeline, `Error while getting project by repo name ${repoName}`); const res = await cursor.toArray(); if (!res.length) { @@ -63,20 +64,28 @@ export class DocsetsRepository extends BaseRepository { return res[0]?.project; } - async getRepo(repoName: string): Promise { - const aggregationPipeline = this.getAggregationPipeline('repoName', repoName); + async getRepo(repoName: string, directory?: string): Promise { + const matchConditions = { repoName }; + if (directory) matchConditions['directories.snooty_toml'] = `/${directory}`; + + const aggregationPipeline = DocsetsRepository.getAggregationPipeline(matchConditions); const cursor = await this.aggregate(aggregationPipeline, `Error while fetching repo by repo name ${repoName}`); const res = await cursor.toArray(); if (!res.length) { const msg = `DocsetsRepository.getRepo - Could not find repo by repoName: ${repoName}`; this._logger.info(this._repoName, msg); } - return res[0]; + return res?.[0]; } - async getRepoBranchesByRepoName(repoName: string): Promise { - const aggregationPipeline = this.getAggregationPipeline('repoName', repoName); - const cursor = await this.aggregate(aggregationPipeline, `Error while fetching repo by repo name ${repoName}`); + async getRepoBranchesByRepoName(repoName: string, project: string): Promise { + const matchConditions = { repoName, project }; + + const aggregationPipeline = DocsetsRepository.getAggregationPipeline(matchConditions); + const cursor = await this.aggregate( + aggregationPipeline, + `Error while fetching repo by repo name ${repoName} and project ${project}` + ); const res = await cursor.toArray(); if (res.length && res[0]?.bucket && res[0]?.url) { return res[0]; diff --git a/src/repositories/repoBranchesRepository.ts b/src/repositories/repoBranchesRepository.ts index d08bca2e7..dbd811fa6 100644 --- a/src/repositories/repoBranchesRepository.ts +++ b/src/repositories/repoBranchesRepository.ts @@ -8,18 +8,24 @@ export class RepoBranchesRepository extends BaseRepository { super(config, logger, 'RepoBranchesRepository', db.collection(config.get('repoBranchesCollection'))); } - async getRepoBranches(repoName: string): Promise { + async getRepoBranches(repoName: string, directoryPath?: string): Promise { const query = { repoName: repoName }; - const repo = await this.findOne(query, `Mongo Timeout Error: Timedout while retrieving branches for ${repoName}`); + if (directoryPath) query['directories.snooty_toml'] = `/${directoryPath}`; + const repo = await this.findOne( + query, + `Mongo Timeout Error: Timedout while retrieving branches for ${repoName}${ + directoryPath ? `/${directoryPath}` : '' + }` + ); // if user has specific entitlements return repo?.['branches'] ?? []; } - async getRepoBranchAliases(repoName: string, branchName: string): Promise { + async getRepoBranchAliases(repoName: string, branchName: string, project: string): Promise { const returnObject = { status: 'failure' }; const aliasArray = await this._collection .aggregate([ - { $match: { repoName: repoName } }, + { $match: { repoName, project } }, { $unwind: '$branches' }, { $match: { 'branches.gitBranchName': branchName } }, { $project: { branches: 1 } }, diff --git a/src/services/repo.ts b/src/services/repo.ts index 77f177a03..b5e780b1b 100644 --- a/src/services/repo.ts +++ b/src/services/repo.ts @@ -5,6 +5,7 @@ import { IConfig } from 'config'; import { InvalidJobError } from '../errors/errors'; import { IFileSystemServices } from './fileServices'; import simpleGit, { SimpleGit } from 'simple-git'; +import { getDirectory } from '../job/jobHandler'; const git: SimpleGit = simpleGit(); export interface IRepoConnector { diff --git a/tests/data/data.ts b/tests/data/data.ts index 595d1d97a..ec822e2bf 100644 --- a/tests/data/data.ts +++ b/tests/data/data.ts @@ -1,6 +1,7 @@ import type { Job } from '../../src/entities/job'; import { CommandExecutorResponse } from '../../src/services/commandExecutor'; import { getBuildJobDef } from '../data/jobDef'; +import { DocsetsRepository } from '../../src/repositories/docsetsRepository'; export class TestDataProvider { static getJobPropertiesValidateTestCases(): Array { @@ -447,44 +448,4 @@ export class TestDataProvider { } return retVal; } - - static getAggregationPipeline( - matchConditionField: string, - matchConditionValue: string, - projection?: { [k: string]: number } - ) { - return [ - // Stage 1: Unwind the repos array to create multiple documents for each referenced repo - { - $unwind: '$repos', - }, - // Stage 2: Lookup to join with the repos_branches collection - { - $lookup: { - from: 'repos_branches', - localField: 'repos', - foreignField: '_id', - as: 'repo', - }, - }, - // Stage 3: Match documents based on given field - { - $match: { - [`repo.${matchConditionField}`]: matchConditionValue, - }, - }, - // Stage 4: Merge/flatten repo into docset - { - $replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ['$repo', 0] }, '$$ROOT'] } }, - }, - // Stage 5: Exclude fields - { - $project: projection || { - _id: 0, - repos: 0, - repo: 0, - }, - }, - ]; - } } diff --git a/tests/unit/api/slack.test.ts b/tests/unit/api/slack.test.ts index 7962e938c..2b0cda063 100644 --- a/tests/unit/api/slack.test.ts +++ b/tests/unit/api/slack.test.ts @@ -39,7 +39,7 @@ jest.mock('../../../src/repositories/repoBranchesRepository', () => ({ RepoBranchesRepository: jest.fn().mockImplementation(() => ({ getRepoBranchAliases: jest .fn() - .mockImplementation((repoName, branchName) => mockBranchObject[repoName][branchName]), + .mockImplementation((repoName, branchName, project) => mockBranchObject[repoName][branchName]), })), })); diff --git a/tests/unit/job/jobValidator.test.ts b/tests/unit/job/jobValidator.test.ts index 7db94a212..2b6614545 100644 --- a/tests/unit/job/jobValidator.test.ts +++ b/tests/unit/job/jobValidator.test.ts @@ -64,7 +64,7 @@ describe('JobValidator Tests', () => { .calledWith(job.user) .mockReturnValue({ status: 'failure' }); await expect(jobValidator.throwIfUserNotEntitled(job)).rejects.toThrow( - `${job.user} is not entitled for repo ${job.payload.repoName}` + `${job.user} is not entitled for repo ${job.payload.repoOwner}/${job.payload.repoName}` ); expect(repoEntitlementRepository.getRepoEntitlementsByGithubUsername).toHaveBeenCalledTimes(1); }); @@ -72,7 +72,7 @@ describe('JobValidator Tests', () => { test('Throw If User Not Entitled Fails because undefined return value', async () => { repoEntitlementRepository.getRepoEntitlementsByGithubUsername.calledWith(job.user).mockReturnValue(undefined); await expect(jobValidator.throwIfUserNotEntitled(job)).rejects.toThrow( - `${job.user} is not entitled for repo ${job.payload.repoName}` + `${job.user} is not entitled for repo ${job.payload.repoOwner}/${job.payload.repoName}` ); expect(repoEntitlementRepository.getRepoEntitlementsByGithubUsername).toHaveBeenCalledTimes(1); }); @@ -82,7 +82,7 @@ describe('JobValidator Tests', () => { .calledWith(job.user) .mockReturnValue({ status: 'success', repos: [`someotherepo`], github_username: job.user }); await expect(jobValidator.throwIfUserNotEntitled(job)).rejects.toThrow( - `${job.user} is not entitled for repo ${job.payload.repoName}` + `${job.user} is not entitled for repo ${job.payload.repoOwner}/${job.payload.repoName}` ); expect(repoEntitlementRepository.getRepoEntitlementsByGithubUsername).toHaveBeenCalledTimes(1); }); diff --git a/tests/unit/job/productionJobHandler.test.ts b/tests/unit/job/productionJobHandler.test.ts index b6934df7f..bae49d4e5 100644 --- a/tests/unit/job/productionJobHandler.test.ts +++ b/tests/unit/job/productionJobHandler.test.ts @@ -120,7 +120,7 @@ describe('ProductionJobHandler Tests', () => { test('Execute throws error when Downloading makefile repo should update status', async () => { jobHandlerTestHelper.fileSystemServices.saveUrlAsFile .calledWith( - `https://raw.githubusercontent.com/mongodb/docs-worker-pool/meta/makefiles/Makefile.${jobHandlerTestHelper.job.payload.repoName}` + `https://raw.githubusercontent.com/mongodb/docs-worker-pool/monorepo-pub-branches/makefiles/Makefile.${jobHandlerTestHelper.job.payload.repoName}` ) .mockImplementation(() => { throw new Error('Error while Downloading makefile'); diff --git a/tests/unit/repositories/docsetsRepository.test.ts b/tests/unit/repositories/docsetsRepository.test.ts index 9fa497604..300ed7196 100644 --- a/tests/unit/repositories/docsetsRepository.test.ts +++ b/tests/unit/repositories/docsetsRepository.test.ts @@ -1,5 +1,4 @@ import { DBRepositoryHelper } from '../../utils/repositoryHelper'; -import { TestDataProvider } from '../../data/data'; import { DocsetsRepository } from '../../../src/repositories/docsetsRepository'; describe('Docsets Repository Tests', () => { @@ -16,24 +15,26 @@ describe('Docsets Repository Tests', () => { describe('Docsets Repository getRepoBranchesByRepoName Tests', () => { test('getRepoBranchesByRepoName returns failure as result is undefined', async () => { - const testPipeline = TestDataProvider.getAggregationPipeline('repoName', 'test_repo'); + const testPipeline = DocsetsRepository.getAggregationPipeline({ repoName: 'test_repo', project: 'test_project' }); dbRepoHelper.collection.aggregate.mockReturnValueOnce({ toArray: () => [], }); - await expect(docsetsRepo.getRepoBranchesByRepoName('test_repo')).resolves.toEqual({ status: 'failure' }); + await expect(docsetsRepo.getRepoBranchesByRepoName('test_repo', 'test_project')).resolves.toEqual({ + status: 'failure', + }); expect(dbRepoHelper.collection.aggregate).toBeCalledTimes(1); expect(dbRepoHelper.collection.aggregate).toBeCalledWith(testPipeline, {}); }); test('getRepoBranchesByRepoName is successfull', async () => { - const testPipeline = TestDataProvider.getAggregationPipeline('repoName', 'test_repo'); + const testPipeline = DocsetsRepository.getAggregationPipeline({ repoName: 'test_repo', project: 'test_project' }); dbRepoHelper.collection.aggregate.mockReturnValueOnce({ toArray: () => ({ bucket: {}, url: {}, }), }); - await docsetsRepo.getRepoBranchesByRepoName('test_repo'); + await docsetsRepo.getRepoBranchesByRepoName('test_repo', 'test_project'); expect(dbRepoHelper.collection.aggregate).toBeCalledTimes(1); expect(dbRepoHelper.collection.aggregate).toBeCalledWith(testPipeline, {}); }); @@ -45,7 +46,7 @@ describe('Docsets Repository Tests', () => { setTimeout(resolve, 5000, [[]]); }); }); - docsetsRepo.getRepoBranchesByRepoName('test_repo').catch((error) => { + docsetsRepo.getRepoBranchesByRepoName('test_repo', 'project').catch((error) => { expect(dbRepoHelper.logger.error).toBeCalledTimes(1); expect(error.message).toContain(`Error while fetching repo by repo name test_repo`); });