Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,10 @@ export default [
// Legacy files with @typescript-eslint/no-explicit-any violations (see github/docs-engineering#5797)
{
files: [
'src/article-api/scripts/generate-api-docs.ts',
'src/article-api/transformers/audit-logs-transformer.ts',
'src/article-api/transformers/rest-transformer.ts',
'src/codeql-cli/scripts/convert-markdown-for-docs.ts',
'src/content-linter/scripts/lint-content.ts',

'src/content-render/liquid/index.ts',
'src/content-render/scripts/liquid-tags.ts',
'src/content-render/scripts/move-content.ts',
Expand All @@ -248,11 +246,8 @@ export default [
'src/graphql/scripts/utils/schema-helpers.ts',
'src/graphql/tests/validate-schema.ts',
'src/landings/components/CookBookFilter.tsx',
'src/landings/components/SidebarProduct.tsx',
'src/landings/pages/product.tsx',
'src/languages/lib/correct-translation-content.ts',
'src/languages/lib/render-with-fallback.ts',
'src/languages/lib/translation-utils.ts',
'src/links/lib/update-internal-links.ts',
'src/links/scripts/check-github-github-links.ts',
'src/rest/components/get-rest-code-samples.ts',
Expand All @@ -266,20 +261,16 @@ export default [
'src/rest/scripts/utils/update-markdown.ts',
'src/rest/tests/get-rest-code-samples-2.ts',
'src/rest/tests/get-rest-code-samples.ts',
'src/rest/tests/openapi-schema.ts',
'src/rest/tests/rendering.ts',
'src/search/components/hooks/useAISearchAutocomplete.ts',
'src/search/components/hooks/useAISearchLocalStorageCache.ts',
'src/search/components/input/AskAIResults.tsx',
'src/search/components/input/SearchOverlay.tsx',
'src/search/lib/get-elasticsearch-results/ai-search-autocomplete.ts',
'src/search/lib/get-elasticsearch-results/general-search.ts',
'src/search/lib/routes/combined-search-route.ts',
'src/search/lib/search-request-params/get-search-from-request-params.ts',
'src/search/middleware/search-routes.ts',
'src/search/scripts/index/index-cli.ts',
'src/search/scripts/index/utils/indexing-elasticsearch-utils.ts',
'src/search/scripts/scrape/lib/parse-page-sections-into-records.ts',
'src/tests/helpers/check-url.ts',
'src/tests/scripts/copy-fixture-data.ts',
'src/tests/vitest.setup.ts',
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
"imurmurhash": "^0.1.4",
"is-svg": "6.0.0",
"javascript-stringify": "^2.1.0",
"js-cookie": "^3.0.5",
"js-cookie": "^3.0.7",
"js-yaml": "^4.1.1",
"liquidjs": "^10.25.7",
"lodash": "^4.18.0",
Expand Down
16 changes: 13 additions & 3 deletions src/article-api/scripts/generate-api-docs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { writeFileSync, readFileSync, existsSync } from 'fs'

type ApiDoc = {
method: string
path: string
description: string
params: string[]
returns: string
examples: string
throws: string[]
}

function main({ sources, outputPath }: { sources: string[]; outputPath: string }): void {
// Extract API documentation comments from all source files
const allDocs = sources.flatMap((sourcePath) => extractApiDocs(sourcePath))
Expand All @@ -14,8 +24,8 @@ function main({ sources, outputPath }: { sources: string[]; outputPath: string }
}

// Extract API docs from comments in the file
function extractApiDocs(file: string): string[] {
const apiDocs: any[] = []
function extractApiDocs(file: string): ApiDoc[] {
const apiDocs: ApiDoc[] = []

// get the content from the api routes
const content = readFileSync(file, 'utf8')
Expand Down Expand Up @@ -114,7 +124,7 @@ function extractExample(commentBlock: string): string {
}

// Generate markdown from parsed documentation
function generateMarkdown(apiDocs: any[]): string {
function generateMarkdown(apiDocs: ApiDoc[]): string {
let markdown = '## Reference: API endpoints\n\n'

for (const doc of apiDocs) {
Expand Down
6 changes: 3 additions & 3 deletions src/landings/components/SidebarProduct.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { NavList } from '@primer/react'

import { ProductTreeNode, useMainContext } from '@/frame/components/context/MainContext'
Expand Down Expand Up @@ -172,8 +172,8 @@ function RestNavListItem({ category }: { category: ProductTreeNode }) {
<NavList.Item
defaultOpen={routePath.includes(childPage.href)}
key={childPage.href}
// Using any because Primer React's NavList doesn't export proper event types
onClick={(event: any) => {
// Using React.MouseEvent because Primer React's NavList doesn't export proper event types
onClick={(event: React.MouseEvent<HTMLElement>) => {
event.preventDefault()
push(childPage.href)
}}
Expand Down
25 changes: 18 additions & 7 deletions src/landings/pages/product.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useEffect } from 'react'
import { GetServerSideProps } from 'next'
import type { Response } from 'express'
import { useRouter } from 'next/router'

import type { ExtendedRequest } from '@/types'
import type { JourneyTrack } from '@/journeys/lib/journey-path-resolver'

// "legacy" javascript needed to maintain existing functionality
// typically operating on elements **within** an article.
import copyCode from '@/frame/components/lib/copy-code'
Expand Down Expand Up @@ -132,8 +136,8 @@ const GlobalPage = ({
export default GlobalPage

export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
const req = context.req as any
const res = context.res as any
const req = context.req as unknown as ExtendedRequest
const res = context.res as unknown as Response

const props: Props = {
mainContext: await getMainContext(req, res),
Expand All @@ -151,8 +155,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>

// journey tracks are resolved in middleware and added to the request
// so we need to add them to the journey context here
if ((req.context.page as any).resolvedJourneyTracks) {
props.journeyContext.journeyTracks = (req.context.page as any).resolvedJourneyTracks
const page = req.context?.page as { resolvedJourneyTracks?: JourneyTrack[] } | undefined
if (page?.resolvedJourneyTracks) {
props.journeyContext.journeyTracks = page.resolvedJourneyTracks
}

additionalUINamespaces.push('journey_landing', 'product_landing')
Expand All @@ -161,15 +166,21 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
additionalUINamespaces.push('product_landing', 'carousels')
} else if (relativePath?.endsWith('index.md')) {
if (currentLayoutName === 'category-landing') {
props.categoryLandingContext = getCategoryLandingContextFromRequest(req)
props.categoryLandingContext = getCategoryLandingContextFromRequest(
req as unknown as Parameters<typeof getCategoryLandingContextFromRequest>[0],
)
} else {
props.tocLandingContext = getTocLandingContextFromRequest(req)
props.tocLandingContext = getTocLandingContextFromRequest(
req as unknown as Parameters<typeof getTocLandingContextFromRequest>[0],
)
}
} else if (props.mainContext.page) {
// All articles that might have hover cards needs this
additionalUINamespaces.push('popovers')

props.articleContext = getArticleContextFromRequest(req)
props.articleContext = getArticleContextFromRequest(
req as unknown as Parameters<typeof getArticleContextFromRequest>[0],
)
if (props.articleContext.currentJourneyTrack?.trackId) {
additionalUINamespaces.push('journey_track_nav')
}
Expand Down
2 changes: 1 addition & 1 deletion src/languages/lib/translation-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function createTranslationFunctions(uiData: UIStrings, namespaces: string
return {} as UIStrings
}
},
t: (strings: TemplateStringsArray | string, ...values: Array<any>) => {
t: (strings: TemplateStringsArray | string, ...values: Array<unknown>) => {
const key = typeof strings === 'string' ? strings : String.raw(strings, ...values)
// Provide specific fallbacks for common 404 page keys
const commonFallbacks: Record<string, string> = {
Expand Down
35 changes: 20 additions & 15 deletions src/rest/tests/openapi-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import walk from 'walk-sync'
import { isPlainObject, difference } from 'lodash-es'

import { isApiVersioned, allVersions } from '@/versions/lib/all-versions'
import getRest, { getRestCategories } from '../lib/index'
import getRest, { getRestCategories, type RestOperationCategory } from '../lib/index'
import readFrontmatter from '@/frame/lib/read-frontmatter'
import frontmatter from '@/frame/lib/frontmatter'
import getApplicableVersions from '../../versions/lib/get-applicable-versions'
import { getAutomatedMarkdownFiles } from '../scripts/test-open-api-schema'
import { nonAutomatedRestPaths } from '../lib/config'
import type { Operation } from '@/rest/components/types'

const schemasPath = 'src/rest/data'

// Operations have dynamic structure from OpenAPI schema - using any to avoid complex type definitions
async function getFlatListOfOperations(version: string): Promise<any[]> {
const flatList = []
async function getFlatListOfOperations(version: string): Promise<Operation[]> {
const flatList: Operation[] = []

if (isApiVersioned(version)) {
for (const apiVersion of allVersions[version].apiVersions) {
Expand All @@ -38,16 +38,17 @@ async function getFlatListOfOperations(version: string): Promise<any[]> {
describe('markdown for each rest version', () => {
// Unique set of all categories across all versions of the OpenAPI schema
const allCategories = new Set<string>()
// Entire schema including categories and subcategories - using any due to dynamic OpenAPI structure
const openApiSchema: Record<string, any> = {}
// Entire schema including categories and subcategories, keyed by version then category
const openApiSchema: Record<string, Record<string, RestOperationCategory>> = {}
// All applicable version of categories based on frontmatter in the categories index.md file
const categoryApplicableVersions: Record<string, any> = {}
const categoryApplicableVersions: Record<string, string[]> = {}

function getApplicableVersionFromFile(file: string) {
const currentFile = fs.readFileSync(file, 'utf8')
// Frontmatter data structure is dynamic based on file content
const { data } = frontmatter(currentFile) as { data: any }
return getApplicableVersions(data.versions, file)
const fm = frontmatter(currentFile) as unknown as {
data?: { versions?: string | Record<string, string | string[]> }
}
return getApplicableVersions(fm.data?.versions, file)
}

function getCategorySubcategory(file: string) {
Expand Down Expand Up @@ -127,12 +128,15 @@ describe('markdown for each rest version', () => {
describe('rest file structure', () => {
test('children of content/rest/index.md are in alphabetical order', async () => {
const indexContent = fs.readFileSync('content/rest/index.md', 'utf8')
// Frontmatter data structure is dynamic based on file content
const { data } = readFrontmatter(indexContent) as { data: any }
const fm = readFrontmatter(indexContent) as unknown as {
data?: { children?: string[] }
}
const children = fm.data?.children
expect(Array.isArray(children)).toBe(true)
const nonAutomatedChildren = nonAutomatedRestPaths.map((child: string) =>
child.replace('/rest', ''),
)
const sortableChildren = data.children.filter(
const sortableChildren = (children as string[]).filter(
(child: string) => !nonAutomatedChildren.includes(child),
)
expect(sortableChildren).toStrictEqual([...sortableChildren].sort())
Expand Down Expand Up @@ -203,11 +207,12 @@ describe('code examples are defined', () => {
}

const operation = await findOperation(version, 'GET', '/repos/{owner}/{repo}')
expect(operation).toBeDefined()
if (!operation) continue
expect(operation.serverUrl).toBe(domain)
expect(isPlainObject(operation)).toBe(true)
expect(operation.codeExamples).toBeDefined()
// Code examples have dynamic structure from OpenAPI schema
for (const example of operation.codeExamples as any[]) {
for (const example of operation.codeExamples) {
expect(isPlainObject(example.request)).toBe(true)
expect(isPlainObject(example.response)).toBe(true)
}
Expand Down
22 changes: 15 additions & 7 deletions src/search/components/input/AskAIResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,20 @@ export function AskAIResults({
let leftover = '' // <= carry‑over buffer
setInitialLoading(false)

const processLine = (parsedLine: any) => {
type ParsedLine = {
chunkType?: string
conversation_id?: string
sources?: AIReference[]
text?: string
errors?: unknown
}

const processLine = (parsedLine: ParsedLine) => {
switch (parsedLine.chunkType) {
// A conversation ID will still be sent when a question cannot be answered
case 'CONVERSATION_ID':
conversationIdBuffer = parsedLine.conversation_id
setConversationId(parsedLine.conversation_id)
conversationIdBuffer = parsedLine.conversation_id ?? ''
setConversationId(parsedLine.conversation_id ?? '')
break

case 'NO_CONTENT_SIGNAL':
Expand All @@ -251,7 +259,7 @@ export function AskAIResults({
case 'SOURCES':
if (!isCancelled) {
sourcesBuffer = uniqBy(
sourcesBuffer.concat(parsedLine.sources as AIReference[]),
sourcesBuffer.concat((parsedLine.sources ?? []) as AIReference[]),
'url',
)
setReferences(sourcesBuffer)
Expand All @@ -260,7 +268,7 @@ export function AskAIResults({

case 'MESSAGE_CHUNK':
if (!isCancelled) {
messageBuffer += parsedLine.text
messageBuffer += parsedLine.text ?? ''
setMessage(messageBuffer)
}
break
Expand Down Expand Up @@ -298,7 +306,7 @@ export function AskAIResults({
for (const raw of lines) {
if (!raw.trim()) continue

let parsedLine: any
let parsedLine: ParsedLine
try {
parsedLine = JSON.parse(raw)
if (parsedLine?.errors) {
Expand Down Expand Up @@ -331,7 +339,7 @@ export function AskAIResults({
console.warn('Failed to parse tail JSON:', leftover, err)
}
}
} catch (error: any) {
} catch (error: unknown) {
if (!isCancelled) {
console.error('Failed to fetch search results:', error)
setAISearchError()
Expand Down
22 changes: 17 additions & 5 deletions src/search/middleware/search-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,35 @@ router.get('/combined-search/v1', catchMiddlewareError(combinedSearchRoute))
export async function handleGetSearchResultsError(
req: Request,
res: Response,
error: any,
options: any,
error: unknown,
options: unknown,
) {
const errorMessage = error instanceof Error ? error.message : String(error)
if (process.env.NODE_ENV === 'development') {
console.error(`Error calling getSearchResults(${options})`, error)
} else {
const reports = FailBot.report(error, { url: req.url, ...options })
const extra: Record<string, unknown> =
options && typeof options === 'object' && !Array.isArray(options)
? Object.fromEntries(
Object.entries(options as Record<string, unknown>).map(([k, v]) => [
k,
v && typeof v === 'object' ? JSON.stringify(v) : v,
]),
)
: { options: typeof options === 'object' ? JSON.stringify(options) : options }
extra.url = req.url
const errorForReport = error instanceof Error ? error : new Error(errorMessage)
const reports = FailBot.report(errorForReport, extra)
if (reports) await Promise.all(reports)
}
// Avoid "Cannot set headers after they are sent to the client" error
// if response was already partially sent before the error occurred
if (!res.headersSent) {
res.status(500).json({ error: error.message })
res.status(500).json({ error: errorMessage })
} else {
logger.warn('Response headers already sent; unable to send error response.', {
url: req.url,
message: error?.message,
message: errorMessage,
})
}
}
Expand Down
Loading