History

Serve Microsoft Graph selections from precomputed spec slices

Rhys Sullivan committed 2 months ago

Commit aa423aa1 parent 000747410 files · +714 −3Browse files

Showing the changes to packages/plugins/openapi/scripts (1 of 10 files). Show all

Unified · Split

packages/plugins/openapi/scripts/generate-graph-slices.ts+109−0
@@ -0,0 +1,109 @@
1/**
2 * Generate the Microsoft Graph slice release assets.
3 *
4 * bun scripts/generate-graph-slices.ts [--source <path-or-url>] [--out <dir>]
5 *
6 * Fetches (or reads) the Graph OpenAPI monolith, builds one slice per catalog
7 * preset plus the default bundle via `slice-build.ts`, validates every slice
8 * against the runtime's streamable profile, and writes `<asset>.yaml` files
9 * plus `manifest.json` to the output directory. The graph-slices workflow runs
10 * this and uploads the output to the `graph-slices` release tag; runtime
11 * resolution lives in `src/providers/microsoft/slices.ts`.
12 *
13 * Offline-only: this whole-parses the 43MB source, which only works where
14 * memory is free (CI runner / dev machine), never in a Workers isolate.
15 */
16import { createHash } from "node:crypto";
17import { mkdir, readFile, writeFile } from "node:fs/promises";
18import { join } from "node:path";
19
20import { structuralSplit } from "../src/sdk/split";
21import {
22 MICROSOFT_GRAPH_DEFAULT_PRESET_IDS,
23 MICROSOFT_GRAPH_OPENAPI_URL,
24 microsoftGraphScopePresets,
25} from "../src/providers/microsoft/presets";
26import { MICROSOFT_GRAPH_DEFAULT_SLICE_ASSET } from "../src/providers/microsoft/slices";
27import {
28 buildGraphSliceDocument,
29 parseGraphSourceDocument,
30} from "../src/providers/microsoft/slice-build";
31
32const argValue = (flag: string): string | undefined => {
33 const index = process.argv.indexOf(flag);
34 return index !== -1 ? process.argv[index + 1] : undefined;
35};
36
37const source = argValue("--source") ?? MICROSOFT_GRAPH_OPENAPI_URL;
38const outDir = argValue("--out") ?? "graph-slices-out";
39
40const readSource = async (): Promise<string> => {
41 if (source.startsWith("http://") || source.startsWith("https://")) {
42 const response = await fetch(source);
43 if (!response.ok) {
44 throw new Error(`Failed to fetch Graph source: HTTP ${response.status}`);
45 }
46 return response.text();
47 }
48 return readFile(source, "utf8");
49};
50
51const sourceText = await readSource();
52const sourceSha256 = createHash("sha256").update(sourceText).digest("hex");
53const doc = parseGraphSourceDocument(sourceText);
54if (!doc) {
55 throw new Error("Microsoft Graph source did not parse to an object");
56}
57
58const selections: readonly { readonly asset: string; readonly presetIds: readonly string[] }[] = [
59 ...microsoftGraphScopePresets.map((preset) => ({ asset: preset.id, presetIds: [preset.id] })),
60 {
61 asset: MICROSOFT_GRAPH_DEFAULT_SLICE_ASSET,
62 presetIds: MICROSOFT_GRAPH_DEFAULT_PRESET_IDS,
63 },
64];
65
66await mkdir(outDir, { recursive: true });
67
68const manifestAssets: Record<
69 string,
70 {
71 readonly bytes: number;
72 readonly paths: number;
73 readonly operations: number;
74 readonly schemas: number;
75 }
76> = {};
77
78for (const { asset, presetIds } of selections) {
79 const slice = buildGraphSliceDocument(doc, presetIds);
80 if (slice.operationCount === 0) {
81 throw new Error(`Slice "${asset}" kept zero operations — preset filter or source drifted`);
82 }
83 const structure = structuralSplit(slice.specText);
84 if (!structure) {
85 throw new Error(`Slice "${asset}" is not in the streamable block-YAML profile`);
86 }
87 if (structure.pathItems.length !== slice.pathCount) {
88 throw new Error(
89 `Slice "${asset}" splitter sees ${structure.pathItems.length} path-items, expected ${slice.pathCount}`,
90 );
91 }
92 await writeFile(join(outDir, `${asset}.yaml`), slice.specText);
93 manifestAssets[asset] = {
94 bytes: Buffer.byteLength(slice.specText),
95 paths: slice.pathCount,
96 operations: slice.operationCount,
97 schemas: slice.schemaCount,
98 };
99 console.log(
100 `${asset}: ${(Buffer.byteLength(slice.specText) / 1024 / 1024).toFixed(2)}MB, ` +
101 `${slice.pathCount} paths, ${slice.operationCount} operations, ${slice.schemaCount} schemas`,
102 );
103}
104
105await writeFile(
106 join(outDir, "manifest.json"),
107 `${JSON.stringify({ source, sourceSha256, generatedAt: new Date().toISOString(), assets: manifestAssets }, null, 2)}\n`,
108);
109console.log(`wrote ${selections.length} slices + manifest.json to ${outDir}`);