Skip to content
This repository was archived by the owner on Aug 6, 2025. It is now read-only.

Commit c7ef1a0

Browse files
authored
* remove associated products if they are not in repo branches. separate repo branches and cache response from DB * update failing test * update metadata when getting from zip * destructure for one prop
1 parent c81785e commit c7ef1a0

File tree

6 files changed

+164
-53
lines changed

6 files changed

+164
-53
lines changed

modules/persistence/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import minimist from 'minimist';
77
import * as mongodb from 'mongodb';
88
import { teardown as closeDBConnection } from './src/services/connector';
99
import { insertPages } from './src/services/pages';
10-
import { insertMetadata, insertMergedMetadataEntries, deleteStaleMetadata } from './src/services/metadata';
10+
import {
11+
insertMetadata,
12+
insertMergedMetadataEntries,
13+
deleteStaleMetadata,
14+
metadataFromZip,
15+
} from './src/services/metadata';
1116
import { upsertAssets } from './src/services/assets';
1217

1318
interface ModuleArgs {
@@ -31,11 +36,11 @@ const app = async (path: string) => {
3136
// atomic buildId for all artifacts read by this module - fundamental assumption
3237
// that only one build will be used per run of this module.
3338
const buildId = new mongodb.ObjectId();
34-
35-
await Promise.all([insertPages(buildId, zip), insertMetadata(buildId, zip), upsertAssets(zip)]);
36-
await insertMergedMetadataEntries(buildId, zip);
39+
const metadata = await metadataFromZip(zip);
40+
await Promise.all([insertPages(buildId, zip), insertMetadata(buildId, metadata), upsertAssets(zip)]);
41+
await insertMergedMetadataEntries(buildId, metadata);
3742
// DOP-3447 clean up stale metadata
38-
await deleteStaleMetadata(zip);
43+
await deleteStaleMetadata(metadata);
3944
closeDBConnection();
4045
process.exit(0);
4146
} catch (error) {

modules/persistence/src/services/metadata/ToC/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ export interface ToC {
1313
[key: string]: any;
1414
}
1515

16-
type project = string;
17-
type branchName = string;
16+
// TODO: move this typing to outer scope ie. metadata/index.ts
17+
export type project = string;
18+
export type branchName = string;
1819
type branch = {
1920
[key: branchName]: ToCCopies;
2021
};

modules/persistence/src/services/metadata/associated_products/index.ts

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { AggregationCursor } from 'mongodb';
22
import { Metadata } from '..';
3-
import { pool, db } from '../../connector';
4-
import { ToCInsertions, TocOrderInsertions, traverseAndMerge, copyToCTree } from '../ToC';
3+
import { db } from '../../connector';
4+
import { getAllAssociatedRepoBranchesEntries, getRepoBranchesEntry } from '../repos_branches';
5+
import { ToCInsertions, TocOrderInsertions, traverseAndMerge, copyToCTree, project } from '../ToC';
56
import { prefixFromEnvironment } from '../ToC/utils/prefixFromEnvironment';
67

78
export interface AssociatedProduct {
8-
name: string;
9-
versions: string[];
9+
name: project;
10+
versions?: string[];
1011
}
1112

1213
export type SharedMetadata = Metadata;
@@ -41,33 +42,6 @@ export interface ReposBranchesDocument {
4142
[key: string]: any;
4243
}
4344

44-
// Queries pool*.repos_branches for any entries for the given project and branch from a metadata entry.
45-
const getRepoBranchesEntry = async (project, branch = ''): Promise<ReposBranchesDocument> => {
46-
const db = await pool();
47-
const query = {
48-
project,
49-
};
50-
if (branch) {
51-
query['branches'] = {
52-
$elemMatch: { gitBranchName: branch },
53-
};
54-
}
55-
return db.collection('repos_branches').findOne(query) as unknown as ReposBranchesDocument;
56-
};
57-
58-
// Queries pool*.repos_branches for all entries for associated_products in a shared metadata entry
59-
const getAllAssociatedRepoBranchesEntries = async (metadata: Metadata) => {
60-
const { associated_products } = metadata;
61-
if (!associated_products || !associated_products.length) return [];
62-
const associatedProductNames = associated_products.map((a) => a.name);
63-
const db = await pool();
64-
const entries = await db
65-
.collection('repos_branches')
66-
.find({ project: { $in: associatedProductNames } })
67-
.toArray();
68-
return entries as unknown as ReposBranchesDocument[];
69-
};
70-
7145
const mapRepoBranches = (repoBranches: ReposBranchesDocument[]) =>
7246
Object.fromEntries(
7347
repoBranches.map((entry) => {
@@ -194,8 +168,6 @@ const getAssociatedProducts = async (umbrellaMetadata) => {
194168
export const mergeAssociatedToCs = async (metadata: Metadata) => {
195169
try {
196170
const { project, branch } = metadata;
197-
// TODO: DOP-3518
198-
// should get an umbrella metadata entry that is in repos branches
199171
const umbrellaMetadata = hasAssociations(metadata) ? metadata : await umbrellaMetadataEntry(project);
200172

201173
// Short circuit execution here if there's no umbrella product metadata found

modules/persistence/src/services/metadata/index.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { deserialize } from 'bson';
33
import { ObjectId } from 'mongodb';
44
import { db, deleteDocuments, insert } from '../connector';
55
import { mergeAssociatedToCs, AssociatedProduct } from './associated_products';
6-
import { ToC } from './ToC';
6+
import { getRepoBranchesEntry } from './repos_branches';
7+
import { project, ToC } from './ToC';
78

89
const COLLECTION_NAME = 'metadata';
910

@@ -18,26 +19,55 @@ export interface Metadata {
1819
// Service responsible for memoization of metadata entries.
1920
// Any extraneous logic performed on metadata entries as part of upload should be added here
2021
// or within subfolders of this module
21-
const metadataFromZip = (zip: AdmZip) => {
22+
export const metadataFromZip = async (zip: AdmZip) => {
2223
const zipEntries = zip.getEntries();
23-
return zipEntries
24+
const metadata = zipEntries
2425
.filter((entry) => entry.entryName === 'site.bson')
2526
.map((entry) => deserialize(entry.getData()))[0] as Metadata;
27+
await verifyMetadata(metadata);
28+
return metadata;
2629
};
2730

28-
export const insertMetadata = async (buildId: ObjectId, zip: AdmZip) => {
31+
// Verifies the entries for associated_products in metadata
32+
const verifyMetadata = async (metadata: Metadata) => {
33+
try {
34+
if (!metadata['associated_products']?.length) {
35+
return metadata;
36+
}
37+
const invalidNames: project[] = [];
38+
const promises = metadata['associated_products'].map(async (ap) => {
39+
const branchEntry = await getRepoBranchesEntry(ap.name);
40+
if (!branchEntry) {
41+
invalidNames.push(ap.name);
42+
}
43+
});
44+
await Promise.all(promises);
45+
if (invalidNames.length) {
46+
console.warn(`No branches found for associated project(s) [${invalidNames}]. Removing such associated_products`);
47+
metadata.associated_products = metadata.associated_products.filter((ap) => !invalidNames.includes(ap.name));
48+
}
49+
if (!metadata['associated_products'].length) {
50+
delete metadata['associated_products'];
51+
}
52+
return metadata;
53+
} catch (e) {
54+
console.error(`Error while verifying metadata ${e}`);
55+
throw e;
56+
}
57+
};
58+
59+
export const insertMetadata = async (buildId: ObjectId, metadata: Metadata) => {
2960
try {
30-
const metadata = metadataFromZip(zip);
3161
return insert([metadata], COLLECTION_NAME, buildId);
3262
} catch (error) {
3363
console.error(`Error at insertion time for ${COLLECTION_NAME}: ${error}`);
3464
throw error;
3565
}
3666
};
3767

38-
export const insertMergedMetadataEntries = async (buildId: ObjectId, zip: AdmZip) => {
68+
export const insertMergedMetadataEntries = async (buildId: ObjectId, metadata: Metadata) => {
3969
try {
40-
const mergedMetadataEntries = await mergeAssociatedToCs(metadataFromZip(zip));
70+
const mergedMetadataEntries = await mergeAssociatedToCs(metadata);
4171
return mergedMetadataEntries
4272
? await Promise.all(mergedMetadataEntries.map((m) => insert([m], COLLECTION_NAME, buildId)))
4373
: [];
@@ -47,9 +77,9 @@ export const insertMergedMetadataEntries = async (buildId: ObjectId, zip: AdmZip
4777
}
4878
};
4979

50-
export const deleteStaleMetadata = async (zip: AdmZip) => {
80+
export const deleteStaleMetadata = async (metadata: Metadata) => {
5181
try {
52-
const { project, branch } = metadataFromZip(zip);
82+
const { project, branch } = metadata;
5383
const LIMIT = 4;
5484
// get most recent metadata for this project-branch
5585
const snooty = await db();
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { pool, db } from '../../connector';
2+
import { Metadata } from '..';
3+
import { project } from '../ToC';
4+
import { WithId } from 'mongodb';
5+
6+
type EnvKeyedObject = {
7+
prd: any;
8+
preprd: any;
9+
dotcomstg: any;
10+
dotcomprd: any;
11+
};
12+
13+
export interface BranchEntry {
14+
name: string;
15+
gitBranchName: string;
16+
[key: string]: any;
17+
}
18+
19+
export interface ReposBranchesDocument extends WithId<Document> {
20+
repoName: string;
21+
project: string;
22+
branches: BranchEntry[];
23+
url: EnvKeyedObject;
24+
prefix: EnvKeyedObject;
25+
[key: string]: any;
26+
}
27+
28+
const internals: { [key: project]: ReposBranchesDocument } = {};
29+
30+
// Queries pool*.repos_branches for all entries for associated_products in a shared metadata entry
31+
export const getAllAssociatedRepoBranchesEntries = async (metadata: Metadata) => {
32+
const { associated_products = [] } = metadata;
33+
if (!associated_products.length) return [];
34+
35+
const res: ReposBranchesDocument[] = [],
36+
fetch: project[] = [];
37+
associated_products.forEach((ap) => {
38+
if (internals[ap.name]) {
39+
res.push(internals[ap.name]);
40+
} else {
41+
fetch.push(ap.name);
42+
}
43+
});
44+
45+
if (!fetch.length) {
46+
return res;
47+
}
48+
49+
try {
50+
const db = await pool();
51+
await db
52+
.collection('repos_branches')
53+
.find({ project: { $in: fetch } })
54+
.forEach((doc: ReposBranchesDocument) => {
55+
// TODO: store in cache
56+
internals[doc['project']] = doc;
57+
res.push(doc);
58+
});
59+
return res;
60+
} catch (e) {
61+
console.error(`Error while getting associated repo branches: ${e}`);
62+
throw e;
63+
}
64+
};
65+
66+
// Queries pool*.repos_branches for any entries for the given project and branch from a metadata entry.
67+
export const getRepoBranchesEntry = async (project: project, branch = ''): Promise<ReposBranchesDocument> => {
68+
const cachedDoc = internals[project];
69+
// return cached repo doc if exists
70+
if (cachedDoc !== undefined) {
71+
if (!branch) {
72+
return cachedDoc;
73+
}
74+
75+
return cachedDoc.branches.map((b) => b.gitBranchName).includes(branch)
76+
? cachedDoc
77+
: (null as unknown as ReposBranchesDocument);
78+
}
79+
80+
// get from DB if not cached
81+
try {
82+
const db = await pool();
83+
const query = {
84+
project,
85+
};
86+
if (branch) {
87+
query['branches'] = {
88+
$elemMatch: { gitBranchName: branch },
89+
};
90+
}
91+
const res = (await db.collection('repos_branches').findOne(query)) as unknown as ReposBranchesDocument;
92+
// if not already set, set cache value for repo_branches
93+
if (!internals[project]) {
94+
internals[project] = res;
95+
}
96+
return res;
97+
} catch (e) {
98+
console.error(`Error while getting repo branches entry: ${e}`);
99+
throw e;
100+
}
101+
};

modules/persistence/tests/metadata/metadata.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ describe('metadata module', () => {
6464
});
6565

6666
describe('metadataFromZip', () => {
67-
const metaFromZip = _metadataFromZip(zip);
68-
it('should get metadata from site.bson', () => {
67+
it('should get metadata from site.bson', async () => {
68+
const metaFromZip = await _metadataFromZip(zip);
6969
expect(metaFromZip).toEqual(meta);
7070
});
7171
});
@@ -74,7 +74,8 @@ describe('metadata module', () => {
7474
const buildId = new ObjectId();
7575
it('should insert metadata docs into metadata collection', async () => {
7676
try {
77-
await insertMetadata(buildId, zip);
77+
const metaFromZip = await _metadataFromZip(zip);
78+
await insertMetadata(buildId, metaFromZip);
7879
} catch (e) {
7980
console.log(e);
8081
}
@@ -100,7 +101,8 @@ describe('metadata module', () => {
100101

101102
it('removes copies of metadata for same project-branch, keeping the most recent ones', async () => {
102103
await mockDb.collection('metadata').insertMany(testData);
103-
await deleteStaleMetadata(zip);
104+
const metaFromZip = await _metadataFromZip(zip);
105+
await deleteStaleMetadata(metaFromZip);
104106
const res = await mockDb
105107
.collection('metadata')
106108
.find({ project, branch })

0 commit comments

Comments
 (0)