Skip to content

Commit

Permalink
DOP-4264: allow sibling associated products' ToC to be visible (#985)
Browse files Browse the repository at this point in the history
  • Loading branch information
seungpark authored Jan 23, 2024
1 parent 3bab418 commit 3c9fe3e
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 145 deletions.
4 changes: 4 additions & 0 deletions plugins/gatsby-source-snooty-preview/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ exports.createSchemaCustomization = async ({ actions }) => {
slug: String
images: [File] @link(by: "relativePath", from: "pageAssets")
}
type AssociatedProduct implements Node @dontInfer {
productName: String
}
`;
createTypes(typeDefs);
};
Expand Down
78 changes: 52 additions & 26 deletions plugins/gatsby-source-snooty-prod/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,43 +24,23 @@ const assets = new Map();

let db;

let isAssociatedProduct = false;
const associatedReposInfo = {};

// Creates node for RemoteMetadata, mostly used for Embedded Versions. If no associated products
// or data are found, the node will be null
const createRemoteMetadataNode = async ({ createNode, createNodeId, createContentDigest }) => {
// fetch associated child products
const productList = manifestMetadata?.associated_products || [];
await Promise.all(
productList.map(async (product) => {
associatedReposInfo[product.name] = await db.realmInterface.fetchDocset({ project: product.name });
})
);
// check if product is associated child product
try {
const umbrellaProduct = await db.realmInterface.getMetadata({
'associated_products.name': siteMetadata.project,
});
isAssociatedProduct = !!umbrellaProduct;
} catch (e) {
console.log('No umbrella product found. Not an associated product.');
isAssociatedProduct = false;
}

const createRemoteMetadataNode = async ({ createNode, createNodeId, createContentDigest }, umbrellaProduct) => {
// get remote metadata for updated ToC in Atlas
try {
const filter = {
project: manifestMetadata.project,
branch: manifestMetadata.branch,
};
const isAssociatedProduct = !!umbrellaProduct;
if (isAssociatedProduct || manifestMetadata?.associated_products?.length) {
filter['is_merged_toc'] = true;
}
const findOptions = {
sort: { build_id: -1 },
};
const remoteMetadata = await db.realmInterface.getMetadata(filter, findOptions);
const remoteMetadata = await db.realmInterface.getMetadata(filter, undefined, findOptions);

createNode({
children: [],
Expand All @@ -78,6 +58,37 @@ const createRemoteMetadataNode = async ({ createNode, createNodeId, createConten
}
};

/**
* Creates graphql nodes on metadata for associated products
* Association can be an umbrella product, associated product (in snooty.toml),
* or sibling products
*/
const createAssociatedProductNodes = async ({ createNode, createNodeId, createContentDigest }, umbrellaProduct) => {
try {
const associatedProducts = manifestMetadata?.associated_products || [];
if (umbrellaProduct) {
associatedProducts.push(...(umbrellaProduct.associated_products || []));
}

return await Promise.all(
associatedProducts.map(async (product) =>
createNode({
children: [],
id: createNodeId(`associated-metadata-${product.name}`),
internal: {
contentDigest: createContentDigest(product),
type: 'AssociatedProduct',
},
parent: null,
productName: product.name,
})
)
);
} catch (e) {
console.error(`Error while creating associated metadata nodes: ${JSON.stringify(e)}`);
}
};

exports.sourceNodes = async ({ actions, createContentDigest, createNodeId }) => {
let hasOpenAPIChangelog = false;
const { createNode } = actions;
Expand Down Expand Up @@ -167,11 +178,24 @@ exports.sourceNodes = async ({ actions, createContentDigest, createNodeId }) =>

await createProductNodes({ db, createNode, createNodeId, createContentDigest });

await createRemoteMetadataNode({ createNode, createNodeId, createContentDigest });
const umbrellaProduct = await db.realmInterface.getMetadata(
{
'associated_products.name': siteMetadata.project,
},
{ associated_products: 1 }
);

await createAssociatedProductNodes({ createNode, createNodeId, createContentDigest }, umbrellaProduct);

await createRemoteMetadataNode({ createNode, createNodeId, createContentDigest }, umbrellaProduct);

if (siteMetadata.project === 'cloud-docs' && hasOpenAPIChangelog)
await createOpenAPIChangelogNode({ createNode, createNodeId, createContentDigest, siteMetadata, db });

await saveAssetFiles(assets, db);
if (!siteMetadata.manifestPath) {
console.error('Getting metadata from realm without filters');
}
const { static_files: staticFiles, ...metadataMinusStatic } = await db.getMetadata();

const { parentPaths, slugToTitle } = metadataMinusStatic;
Expand Down Expand Up @@ -257,8 +281,6 @@ exports.createPages = async ({ actions }) => {
context: {
slug,
repoBranches,
associatedReposInfo,
isAssociatedProduct,
template: pageNodes?.options?.template,
page: pageNodes,
},
Expand Down Expand Up @@ -327,5 +349,9 @@ exports.createSchemaCustomization = ({ actions }) => {
slug: String
images: [File] @link(by: "relativePath", from: "pageAssets")
}
type AssociatedProduct implements Node @dontInfer {
productName: String
}
`);
};
1 change: 1 addition & 0 deletions plugins/utils/docsets.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const createDocsetNodes = async ({ db, createNode, createNodeId, createContentDi
prefix: docset.prefix,
project: docset.project,
url: docset.url,
branches: docset.branches,
});
});
};
Expand Down
5 changes: 4 additions & 1 deletion src/components/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const Image = ({ nodeData, className }) => {
border: 0.5px solid ${palette.gray.light1};
border-radius: 4px;
`;
const gatsbyContainerStyle = css`
height: max-content;
`;

const { imageByPath } = useContext(ImageContext);
const image = imageByPath[removeLeadingSlash(imgSrc)];
Expand Down Expand Up @@ -61,7 +64,7 @@ const Image = ({ nodeData, className }) => {
alt={altText}
style={userOptionStyle}
imgClassName={cx(defaultStyling, hasBorder ? borderStyling : '')}
className={cx(directiveClass, customAlign, className)}
className={cx(gatsbyContainerStyle, directiveClass, customAlign, className)}
/>
);
}
Expand Down
19 changes: 2 additions & 17 deletions src/components/RootProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,12 @@ import { SearchContextProvider } from './SearchResults/SearchContext';
// Check for feature flag here to make it easier to pass down for testing purposes
const SHOW_FACETS = process.env.GATSBY_FEATURE_FACETED_SEARCH === 'true';

const RootProvider = ({
children,
headingNodes,
selectors,
slug,
repoBranches,
associatedReposInfo,
isAssociatedProduct,
remoteMetadata,
project,
}) => {
const RootProvider = ({ children, headingNodes, selectors, slug, repoBranches, remoteMetadata, project }) => {
let providers = (
<TabProvider selectors={selectors}>
<ContentsProvider headingNodes={headingNodes}>
<HeaderContextProvider>
<VersionContextProvider
repoBranches={repoBranches}
slug={slug}
associatedReposInfo={associatedReposInfo}
isAssociatedProduct={isAssociatedProduct}
>
<VersionContextProvider repoBranches={repoBranches} slug={slug}>
<TocContextProvider remoteMetadata={remoteMetadata}>
<SidenavContextProvider>
<SearchContextProvider showFacets={SHOW_FACETS}>{children}</SearchContextProvider>
Expand Down
9 changes: 4 additions & 5 deletions src/context/toc-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
import { useCallback } from 'react';
import { METADATA_COLLECTION } from '../build-constants';
import { useSiteMetadata } from '../hooks/use-site-metadata';
import { fetchDocuments } from '../utils/realm';
import { fetchDocument } from '../utils/realm';
import useSnootyMetadata from '../utils/use-snooty-metadata';
import { isGatsbyPreview } from '../utils/is-gatsby-preview';
import { VersionContext } from './version-context';
Expand Down Expand Up @@ -39,16 +39,15 @@ const TocContextProvider = ({ children, remoteMetadata }) => {
if (associatedProducts?.length || showVersionDropdown) {
filter['is_merged_toc'] = true;
}
const db = database;
const metadata = await fetchDocuments(db, METADATA_COLLECTION, filter, undefined, findOptions);
return metadata[0]?.toctree ?? toctree;
const metadata = await fetchDocument(database, METADATA_COLLECTION, filter, { toctree: 1 }, findOptions);
return metadata?.toctree ?? toctree;
} catch (e) {
// fallback to toctree from build time
console.error(e);
return remoteMetadata?.toctree || toctree;
}
// below dependents are server constants
}, [project, branch, associatedProducts, showVersionDropdown, database, toctree, remoteMetadata]);
}, [toctree, project, branch, associatedProducts?.length, showVersionDropdown, database, remoteMetadata?.toctree]);

const setInitVersion = useCallback(
(tocNode) => {
Expand Down
37 changes: 28 additions & 9 deletions src/context/version-context.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { createContext, useReducer, useEffect, useState, useCallback, useRef, useMemo } from 'react';
import { navigate } from '@gatsbyjs/reach-router';
import { METADATA_COLLECTION } from '../build-constants';
import { useAllDocsets } from '../hooks/useAllDocsets';
import { useAllAssociatedProducts } from '../hooks/useAssociatedProducts';
import { useSiteMetadata } from '../hooks/use-site-metadata';
import { useCurrentUrlSlug } from '../hooks/use-current-url-slug';
import { getLocalValue, setLocalValue } from '../utils/browser-storage';
import { fetchDocset, fetchDocuments } from '../utils/realm';
import { fetchDocset, fetchDocument } from '../utils/realm';
import { getUrl } from '../utils/url-utils';
import useSnootyMetadata from '../utils/use-snooty-metadata';

Expand Down Expand Up @@ -60,7 +62,7 @@ const getBranches = async (metadata, repoBranches, associatedReposInfo, associat
for (let associatedProduct of associatedProducts) {
promises.push(
fetchDocset(metadata.reposDatabase, {
project: associatedProduct.name,
project: associatedProduct,
})
);
}
Expand Down Expand Up @@ -122,8 +124,8 @@ const getUmbrellaProject = async (project, dbName) => {
const query = {
'associated_products.name': project,
};
const umbrellaProjects = fetchDocuments(dbName, METADATA_COLLECTION, query);
return umbrellaProjects;
const umbrellaProject = await fetchDocument(dbName, METADATA_COLLECTION, query);
return umbrellaProject;
} catch (e) {
console.error(e);
}
Expand All @@ -139,12 +141,28 @@ const VersionContext = createContext({
setAvailableVersions: () => {},
showVersionDropdown: false,
showEol: false,
isAssociatedProduct: false,
onVersionSelect: () => {},
});

const VersionContextProvider = ({ repoBranches, associatedReposInfo, isAssociatedProduct, slug, children }) => {
const VersionContextProvider = ({ repoBranches, slug, children }) => {
const siteMetadata = useSiteMetadata();
const { associated_products: associatedProducts, project } = useSnootyMetadata();
const associatedProductNames = useAllAssociatedProducts();
const docsets = useAllDocsets();
const { project } = useSnootyMetadata();
const associatedReposInfo = useMemo(
() =>
associatedProductNames.reduce((res, productName) => {
res[productName] = docsets.find((docset) => docset.project === productName);
return res;
}, {}),
[associatedProductNames, docsets]
);

const isAssociatedProduct = useMemo(
() => associatedProductNames.includes(project),
[associatedProductNames, project]
);
const metadata = useMemo(() => {
return {
...siteMetadata,
Expand Down Expand Up @@ -176,7 +194,7 @@ const VersionContextProvider = ({ repoBranches, associatedReposInfo, isAssociate

// on init, fetch versions from realm app services
useEffect(() => {
getBranches(metadata, repoBranches, associatedReposInfo, associatedProducts || []).then(
getBranches(metadata, repoBranches, associatedReposInfo, associatedProductNames).then(
({ versions, groups, hasEolBranches }) => {
if (!mountRef.current) {
return;
Expand All @@ -194,11 +212,11 @@ const VersionContextProvider = ({ repoBranches, associatedReposInfo, isAssociate

const [showVersionDropdown, setShowVersionDropdown] = useState(isAssociatedProduct);
useEffect(() => {
getUmbrellaProject(metadata.project, metadata.database).then((metadataList) => {
getUmbrellaProject(metadata.project, metadata.database).then((umbrellaMetadata) => {
if (!mountRef.current) {
return;
}
setShowVersionDropdown(metadataList.length > 0);
setShowVersionDropdown(!!umbrellaMetadata);
});
}, [metadata.project, metadata.database]);

Expand Down Expand Up @@ -273,6 +291,7 @@ const VersionContextProvider = ({ repoBranches, associatedReposInfo, isAssociate
availableGroups,
showVersionDropdown,
onVersionSelect,
isAssociatedProduct,
showEol,
}}
>
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useAllDocsets.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export const useAllDocsets = () => {
stg
regression
}
branches {
gitBranchName
active
urlSlug
urlAliases
}
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/hooks/useAssociatedProducts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useStaticQuery, graphql } from 'gatsby';

// Return an array of MongoDB products
export const useAllAssociatedProducts = () => {
const { allAssociatedProduct } = useStaticQuery(
graphql`
query AllAssociatedProducts {
allAssociatedProduct {
nodes {
productName
}
}
}
`
);
return allAssociatedProduct.nodes.map((ap) => ap.productName);
};
13 changes: 10 additions & 3 deletions src/init/DocumentDatabase.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,19 @@ class RealmInterface {
return this.realmClient.callFunction('fetchDocuments', DB, collection, buildFilter);
}

async getMetadata(buildFilter, findOptions) {
return this.realmClient.callFunction('fetchDocument', DB, METADATA_COLLECTION, buildFilter, undefined, findOptions);
async getMetadata(buildFilter, projectionOptions, findOptions) {
return this.realmClient.callFunction(
'fetchDocumentSorted',
DB,
METADATA_COLLECTION,
buildFilter,
projectionOptions,
findOptions
);
}

async fetchDocument(database, collectionName, query) {
return this.realmClient.callFunction('fetchDocument', database, collectionName, query);
return this.realmClient.callFunction('fetchDocumentSorted', database, collectionName, query);
}

async fetchDocset(matchConditions = { project: siteMetadata.project }) {
Expand Down
7 changes: 1 addition & 6 deletions src/layouts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ const GlobalGrid = styled('div')`
overflow: clip;
`;

const DefaultLayout = ({
children,
pageContext: { page, slug, repoBranches, template, associatedReposInfo, isAssociatedProduct },
}) => {
const DefaultLayout = ({ children, pageContext: { page, slug, repoBranches, template } }) => {
const { sidenav } = getTemplate(template);
const { chapters, guides, publishedBranches, slugToTitle, title, toctree, eol, project } = useSnootyMetadata();
const remoteMetadata = useRemoteMetadata();
Expand All @@ -102,10 +99,8 @@ const DefaultLayout = ({
<RootProvider
slug={slug}
repoBranches={repoBranches}
associatedReposInfo={associatedReposInfo}
headingNodes={page?.options?.headings}
selectors={page?.options?.selectors}
isAssociatedProduct={isAssociatedProduct}
remoteMetadata={remoteMetadata}
project={project}
>
Expand Down
Loading

0 comments on commit 3c9fe3e

Please sign in to comment.