// [start-readme] // // Run this script to check if OpenAPI operations match versions in content/rest operations // // [end-readme] import fs from 'fs' import path from 'path' import _ from 'lodash' import frontmatter from '@/frame/lib/read-frontmatter' import getApplicableVersions from '@/versions/lib/get-applicable-versions' import { allVersions, getDocsVersion } from '@/versions/lib/all-versions' import { REST_DATA_DIR, REST_SCHEMA_FILENAME } from '../lib/index' import { nonAutomatedRestPaths } from '../lib/config' import { deprecated } from '@/versions/lib/enterprise-server-releases' import walkFiles from '@/workflows/walk-files' type CheckObject = Record> type DifferenceResult = Record type ErrorMessages = Record> export async function getDiffOpenAPIContentRest(): Promise { const contentFiles = getAutomatedMarkdownFiles('content/rest') // Creating the categories/subcategories based on the current content directory const checkContentDir = await createCheckContentDirectory(contentFiles) // Create categories/subcategories from OpenAPI Schemas const openAPISchemaCheck = await createOpenAPISchemasCheck() // Get Differences between categories/subcategories from dereferenced schemas and the content/rest directory frontmatter versions const differences = getDifferences(openAPISchemaCheck, checkContentDir) const errorMessages: ErrorMessages = {} if (Object.keys(differences).length > 0) { for (const schemaName in differences) { errorMessages[schemaName] = {} differences[schemaName].forEach((category) => { errorMessages[schemaName][category] = { contentDir: checkContentDir[schemaName][category], openAPI: openAPISchemaCheck[schemaName][category], } }) } } return errorMessages } async function createOpenAPISchemasCheck(): Promise { const openAPICheck = createCheckObj() const restDirectory = fs .readdirSync(REST_DATA_DIR) .filter((dir) => !dir.endsWith('.json')) // Allow the most recent deprecation to exist on disk until fully deprecated .filter((dir) => !dir.includes(deprecated[0])) restDirectory.forEach((dir) => { const filename = path.join(REST_DATA_DIR, dir, REST_SCHEMA_FILENAME) const fileSchema = JSON.parse(fs.readFileSync(filename, 'utf8')) const categories = Object.keys(fileSchema).sort() const version = getDocsVersion(dir) categories.forEach((category) => { const subcategories = Object.keys(fileSchema[category]) as string[] if (isApiVersioned(version)) { getOnlyApiVersions(version).forEach( (apiVersion) => (openAPICheck[apiVersion][category] = subcategories.sort()), ) } else { openAPICheck[version][category] = subcategories.sort() } }) }) return openAPICheck } async function createCheckContentDirectory(contentFiles: string[]): Promise { const checkContent = createCheckObj() for (const filename of contentFiles) { const { data } = frontmatter(await fs.promises.readFile(filename, 'utf8')) const applicableVersions = getApplicableVersions(data?.versions, filename) const splitPath = filename.split('/') const subCategory = splitPath[splitPath.length - 1].replace('.md', '') const category = splitPath[splitPath.length - 2] === 'rest' ? subCategory : splitPath[splitPath.length - 2] // All versions with appended calendar date versions if it exists const allCompleteVersions = applicableVersions.flatMap((version) => { return isApiVersioned(version) ? allVersions[version].apiVersions.map( (apiVersion) => `${allVersions[version].version}.${apiVersion}`, ) : version }) allCompleteVersions.forEach((version) => { if (!checkContent[version][category]) { checkContent[version][category] = [subCategory] } else { checkContent[version][category].push(subCategory) } checkContent[version][category].sort() }) } return checkContent } function isApiVersioned(version: string): boolean { return allVersions[version] && allVersions[version].apiVersions.length > 0 } function getOnlyApiVersions(version: string): string[] { return allVersions[version].apiVersions.map( (apiVersion) => `${allVersions[version].version}.${apiVersion}`, ) } function createCheckObj(): CheckObject { const versions: CheckObject = {} Object.keys(allVersions).forEach((version) => { if (isApiVersioned(version)) { getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {})) } else { versions[`${allVersions[version].version}`] = {} } }) return versions } function getDifferences( openAPISchemaCheck: CheckObject, contentCheck: CheckObject, ): DifferenceResult { const differences: DifferenceResult = {} for (const version in openAPISchemaCheck) { const diffOpenApiContent = difference(openAPISchemaCheck[version], contentCheck[version]) if (Object.keys(diffOpenApiContent).length > 0) differences[version] = diffOpenApiContent } return differences } function difference(obj1: Record, obj2: Record): string[] { const diff = Object.keys(obj1).reduce((result, key) => { if (!Object.prototype.hasOwnProperty.call(obj2, key)) { result.push(key) } else if (_.isEqual(obj1[key], obj2[key])) { const resultKeyIndex = result.indexOf(key) result.splice(resultKeyIndex, 1) } return result }, Object.keys(obj2)) return diff } export function getAutomatedMarkdownFiles(rootDir: string): string[] { return walkFiles(rootDir, '.md') .filter((file) => !file.includes('index.md')) .filter((file) => !nonAutomatedRestPaths.some((path) => file.includes(path))) }