forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinject-models-schema.ts
More file actions
97 lines (82 loc) · 3.63 KB
/
Copy pathinject-models-schema.ts
File metadata and controls
97 lines (82 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import yaml from 'js-yaml'
import dereferenceJsonSchema from 'dereference-json-schema'
import { existsSync } from 'fs'
import { readFile, readdir } from 'fs/promises'
// OpenAPI 3.0 schema interface with the properties we need to access
// The dereference-json-schema library returns a DereferencedJSONSchema type
// but the actual object contains OpenAPI-specific properties that aren't in that type
interface OpenAPISchema {
openapi?: string
info?: any
servers?: any[]
paths?: Record<string, Record<string, any>>
[key: string]: any
}
export const MODELS_GATEWAY_ROOT = 'models-gateway'
const MODELS_GATEWAY_PATH = 'docs/api'
// The github-models REST API OpenAPI descriptions live in a separate repo, github/models-gateway.
// We "inject" the descriptions from that repo into the core GitHub API descriptions so that
// from the perspective of our app code,
// models descriptions are part of the same REST API schema and don't need additional processing
export async function injectModelsSchema(schema: any, schemaName: string): Promise<any> {
if (!schemaName.includes('fpt')) {
return schema
}
const modelEndpoints = (
await readdir(`./${MODELS_GATEWAY_ROOT}/${MODELS_GATEWAY_PATH}`, {
recursive: true,
})
).filter((name) => name.endsWith('.yaml') || name.endsWith('.yml'))
for (let endpointPath of modelEndpoints) {
endpointPath = `./${MODELS_GATEWAY_ROOT}/${MODELS_GATEWAY_PATH}/${endpointPath}`
if (!existsSync(endpointPath)) {
console.warn(
`⚠️ Models gateway YAML file not found at ${endpointPath}. Skipping injection for ${schemaName}.`,
)
continue
}
const yamlContent = await readFile(endpointPath, 'utf8')
const loadedYaml = yaml.load(yamlContent) as {
openapi: string
info: any
servers: any[]
paths: { [x: string]: any }
}
const deferencedYaml = dereferenceJsonSchema.dereferenceSync(loadedYaml)
// Copy over top-level OpenAPI fields
// Cast to OpenAPISchema because dereference-json-schema doesn't include OpenAPI-specific properties in its type
const openApiYaml = deferencedYaml as OpenAPISchema
schema.openapi = schema.openapi || openApiYaml.openapi
schema.info = schema.info || openApiYaml.info
schema.servers = schema.servers || openApiYaml.servers
// Process each path and operation in the YAML
for (const path of Object.keys(openApiYaml.paths || {})) {
for (const operation of Object.keys(openApiYaml.paths![path])) {
const operationObject = openApiYaml.paths![path][operation]
// Use values from the YAML where possible
const name = operationObject.summary || ''
console.log(`⏳ Processing operation: ${name} (${path} ${operation})`)
// Create enhanced operation with custom fields needed for our REST docs
// The spread operator preserves all original OpenAPI fields
const enhancedOperation = {
...operationObject,
// Add custom fields for our docs processing
verb: operation,
requestPath: path,
// Override tags with default if not present
tags: operationObject.tags || ['models'],
}
// Preserve operation-level servers if present
// !Needed! to use models.github.ai instead of api.github.com
if (openApiYaml.servers) {
enhancedOperation.servers = openApiYaml.servers
}
// Add the enhanced operation to the schema
schema.paths[path] = schema.paths[path] || {}
schema.paths[path][operation] = enhancedOperation
console.log(`✅ Processed operation: ${name} (${path} ${operation})`)
}
}
}
return schema
}