diff --git a/Dockerfile b/Dockerfile index 8db3546ebf73..f9a1d3877238 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ # --------------------------------------------------------------- # To update the sha: # https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble -FROM ghcr.io/github/gh-base-image/gh-base-noble:20250711-165924-g6f92253c7 AS base +FROM ghcr.io/github/gh-base-image/gh-base-noble:20250715-152201-gef17a3886 AS base # Install curl for Node install and determining the early access branch # Install git for cloning docs-early-access & translations repos diff --git a/content/index.md b/content/index.md index 2b2d02ba2191..0fe82c9cbd73 100644 --- a/content/index.md +++ b/content/index.md @@ -1,5 +1,5 @@ --- -title: '{% data variables.product.github %}{% ifversion fpt or ghec%}.com{% endif %} Help Documentation' +title: '{% data variables.product.github %}{% ifversion fpt or ghec %}.com{% endif %} Help Documentation' featuredLinks: gettingStarted: - /get-started/git-basics/set-up-git @@ -170,6 +170,7 @@ childGroups: - codeql - electron - npm + - gh-wa externalProducts: electron: id: electron @@ -192,4 +193,3 @@ externalProducts: href: 'https://wellarchitected.github.com/' external: true --- - diff --git a/src/rest/lib/index.js b/src/rest/lib/index.ts similarity index 51% rename from src/rest/lib/index.js rename to src/rest/lib/index.ts index 748d7d1920b5..47d96dfb1c2d 100644 --- a/src/rest/lib/index.js +++ b/src/rest/lib/index.ts @@ -5,11 +5,26 @@ import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file' import { getAutomatedPageMiniTocItems } from '@/frame/lib/get-mini-toc-items' import { allVersions, getOpenApiVersion } from '@/versions/lib/all-versions' import languages from '@/languages/lib/languages' +import type { Context } from '@/types' +import type { Operation } from '@/rest/components/types' export const REST_DATA_DIR = 'src/rest/data' export const REST_SCHEMA_FILENAME = 'schema.json' const REST_CONTENT_DIR = 'content/rest' +// Type definitions for REST operations +interface RestOperationCategory { + [subcategory: string]: Operation[] +} + +interface RestOperationData { + [category: string]: RestOperationCategory +} + +interface RestMiniTocData { + restOperationsMiniTocItems: any[] +} + /* Loads the schemas from the static/decorated folder into a single object organized by version. Not all products are calendar date @@ -38,87 +53,107 @@ const REST_CONTENT_DIR = 'content/rest' } */ const NOT_API_VERSIONED = 'not_api_versioned' -const restOperationData = new Map() -const restOperations = new Map() +const restOperationData = new Map< + string, + Map>>> +>() +const restOperations = new Map>() -Object.keys(languages).forEach((language) => { +Object.keys(languages).forEach((language: string) => { restOperationData.set(language, new Map()) - Object.keys(allVersions).forEach((version) => { + Object.keys(allVersions).forEach((version: string) => { // setting to undefined will allow us to perform checks // more easily later on - restOperationData.get(language).set(version, new Map()) + restOperationData.get(language)!.set(version, new Map()) if (allVersions[version].apiVersions && allVersions[version].apiVersions.length > 0) { - allVersions[version].apiVersions.forEach((date) => { - restOperationData.get(language).get(version).set(date, new Map()) + allVersions[version].apiVersions.forEach((date: string) => { + restOperationData.get(language)!.get(version)!.set(date, new Map()) }) } else { // Products that are not been calendar date versioned - restOperationData.get(language).get(version).set(NOT_API_VERSIONED, new Map()) + restOperationData.get(language)!.get(version)!.set(NOT_API_VERSIONED, new Map()) } }) }) -export const categoriesWithoutSubcategories = fs +export const categoriesWithoutSubcategories: string[] = fs .readdirSync(REST_CONTENT_DIR) - .filter((file) => { + .filter((file: string) => { return file.endsWith('.md') && !file.includes('index.md') && !file.includes('README.md') }) - .map((filteredFile) => filteredFile.replace('.md', '')) + .map((filteredFile: string) => filteredFile.replace('.md', '')) // version: plan + release e.g. For ghes-3.5, ghes is the plan and 3.5 is the release // apiVersion (not all versions have apiVersions): REST API Calendar Dates // openApiVersion (below, every version has an openApiVersion mapping): There's a mapping between our Docs versions // and the OpenApi Version bc it's not the same -export default async function getRest(version, apiVersion, category, subCategory) { + +export default async function getRest( + version: string, + apiVersion?: string, +): Promise { const openApiVersion = getOpenApiVersion(version) const openapiSchemaName = apiVersion ? `${openApiVersion}-${apiVersion}` : `${openApiVersion}` const apiDate = apiVersion || NOT_API_VERSIONED const fileName = path.join(REST_DATA_DIR, openapiSchemaName, REST_SCHEMA_FILENAME) + if (!restOperations.has(openApiVersion)) { restOperations.set(openApiVersion, new Map()) - restOperations.get(openApiVersion).set(apiDate, new Map()) // The `readCompressedJsonFileFallback()` function // will check for both a .br and .json extension. - restOperations.get(openApiVersion).set(apiDate, readCompressedJsonFileFallback(fileName)) - } else if (!restOperations.get(openApiVersion).has(apiDate)) { - restOperations.get(openApiVersion).set(apiDate, new Map()) + restOperations + .get(openApiVersion)! + .set(apiDate, readCompressedJsonFileFallback(fileName) as RestOperationData) + } else if (!restOperations.get(openApiVersion)!.has(apiDate)) { // The `readCompressedJsonFileFallback()` function // will check for both a .br and .json extension. - restOperations.get(openApiVersion).set(apiDate, readCompressedJsonFileFallback(fileName)) - } - if (subCategory) { - return restOperations.get(openApiVersion).get(apiDate)[category][subCategory] - } else if (category) { - return restOperations.get(openApiVersion).get(apiDate)[category] - } else { - return restOperations.get(openApiVersion).get(apiDate) + restOperations + .get(openApiVersion)! + .set(apiDate, readCompressedJsonFileFallback(fileName) as RestOperationData) } + + return restOperations.get(openApiVersion)!.get(apiDate)! } // Generates the miniToc for a rest reference page. export async function getRestMiniTocItems( - category, - subCategory, - apiVersion, - restOperations, - language, - version, - context, -) { + category: string, + subCategory: string, + apiVersion: string | undefined, + restOperations: Operation[], + language: string, + version: string, + context: Context, +): Promise { const apiDate = apiVersion || NOT_API_VERSIONED - if (!restOperationData.get(language).get(version).get(apiDate).has(category)) { - restOperationData.get(language).get(version).get(apiDate).set(category, new Map()) + const languageData = restOperationData.get(language) + if (!languageData) { + throw new Error(`Language ${language} not found in rest operation data`) + } + + const versionData = languageData.get(version) + if (!versionData) { + throw new Error(`Version ${version} not found for language ${language}`) + } + + const apiData = versionData.get(apiDate) + if (!apiData) { + throw new Error(`API date ${apiDate} not found for version ${version} and language ${language}`) + } + + if (!apiData.has(category)) { + apiData.set(category, new Map()) } - if (!restOperationData.get(language).get(version).get(apiDate).get(category).get(subCategory)) { - const languageTree = restOperationData.get(language) - const titles = restOperations.map((operation) => operation.title) + const categoryData = apiData.get(category)! + if (!categoryData.get(subCategory)) { + const titles = restOperations.map((operation: Operation) => operation.title) const restOperationsMiniTocItems = await getAutomatedPageMiniTocItems(titles, context, 3) - languageTree.get(version).get(apiDate).get(category).set(subCategory, { + categoryData.set(subCategory, { restOperationsMiniTocItems, }) - restOperationData.set(restOperationData, languageTree) } - return restOperationData.get(language).get(version).get(apiDate).get(category).get(subCategory) + + return categoryData.get(subCategory)! } diff --git a/src/rest/pages/category.tsx b/src/rest/pages/category.tsx index 06d22bc86e41..e14eaf70fcd2 100644 --- a/src/rest/pages/category.tsx +++ b/src/rest/pages/category.tsx @@ -60,7 +60,7 @@ export default function Category({ } export const getServerSideProps: GetServerSideProps = async (context) => { - const { default: getRest, getRestMiniTocItems } = await import('@/rest/lib/index.js') + const { default: getRest, getRestMiniTocItems } = await import('@/rest/lib/index') const nonEnterpriseDefaultVersionModule = await import( 'src/versions/lib/non-enterprise-default-version.js' ) @@ -86,7 +86,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => subcategory = category } - const restOperations = (await getRest(currentVersion, apiVersion, category, subcategory)) || [] + const restData = await getRest(currentVersion, apiVersion) + const restOperations = (restData && restData[category] && restData[category][subcategory]) || [] // Build table of contents for all category operations for TocLanding: // @@ -94,7 +95,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => // * loop over subcategories and get the operations per subcategory // * get the minitoc items per set of subcategory operations // * with this data, build a collection of toc items that can be used by TocLanding - const restCategoryOperations = (await getRest(currentVersion, apiVersion, category)) || [] + const restCategoryOperations = (restData && restData[category]) || {} const restCategoryTocItems = [] for (const [subCat, subCatOperations] of Object.entries(restCategoryOperations)) { diff --git a/src/rest/pages/subcategory.tsx b/src/rest/pages/subcategory.tsx index 6bb9e8439080..b6d7774f2efb 100644 --- a/src/rest/pages/subcategory.tsx +++ b/src/rest/pages/subcategory.tsx @@ -35,7 +35,7 @@ export default function SubCategory({ mainContext, automatedPageContext, restOpe } export const getServerSideProps: GetServerSideProps = async (context) => { - const { default: getRest, getRestMiniTocItems } = await import('@/rest/lib/index.js') + const { default: getRest, getRestMiniTocItems } = await import('@/rest/lib/index') const req = context.req as any const res = context.res as any @@ -55,7 +55,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => subCategory = category } - const restOperations = (await getRest(currentVersion, apiVersion, category, subCategory)) || [] + const restData = await getRest(currentVersion, apiVersion) + const restOperations = (restData && restData[category] && restData[category][subCategory]) || [] // Gets the miniTocItems in the article context. At this point it will only // include miniTocItems generated from the Markdown pages in diff --git a/src/rest/tests/rendering.js b/src/rest/tests/rendering.js index d22774fb9c91..c9f6021d064a 100644 --- a/src/rest/tests/rendering.js +++ b/src/rest/tests/rendering.js @@ -15,7 +15,9 @@ describe('REST references docs', () => { test('loads schema data for all versions', async () => { for (const version of Object.keys(allVersions)) { const calendarDate = allVersions[version].latestApiVersion - const checksRestOperations = await getRest(version, calendarDate, 'checks', 'runs') + const restData = await getRest(version, calendarDate) + const checksRestOperations = + (restData && restData['checks'] && restData['checks']['runs']) || [] const $ = await getDOM(`/en/${version}/rest/checks/runs?restVersion=${calendarDate}`) const domH2Ids = $('h2') .map((i, h2) => $(h2).attr('id'))