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+ */
16+import { createHash } from "node:crypto";
17+import { mkdir, readFile, writeFile } from "node:fs/promises";
18+import { join } from "node:path";
19+
20+import { structuralSplit } from "../src/sdk/split";
21+import {
22+ MICROSOFT_GRAPH_DEFAULT_PRESET_IDS,
23+ MICROSOFT_GRAPH_OPENAPI_URL,
24+ microsoftGraphScopePresets,
25+} from "../src/providers/microsoft/presets";
26+import { MICROSOFT_GRAPH_DEFAULT_SLICE_ASSET } from "../src/providers/microsoft/slices";
27+import {
28+ buildGraphSliceDocument,
29+ parseGraphSourceDocument,
30+} from "../src/providers/microsoft/slice-build";
31+
32+const argValue = (flag: string): string | undefined => {
33+ const index = process.argv.indexOf(flag);
34+ return index !== -1 ? process.argv[index + 1] : undefined;
35+};
36+
37+const source = argValue("--source") ?? MICROSOFT_GRAPH_OPENAPI_URL;
38+const outDir = argValue("--out") ?? "graph-slices-out";
39+
40+const 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+
51+const sourceText = await readSource();
52+const sourceSha256 = createHash("sha256").update(sourceText).digest("hex");
53+const doc = parseGraphSourceDocument(sourceText);
54+if (!doc) {
55+ throw new Error("Microsoft Graph source did not parse to an object");
56+}
57+
58+const 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+
66+await mkdir(outDir, { recursive: true });
67+
68+const manifestAssets: Record<
69+ string,
70+ {
71+ readonly bytes: number;
72+ readonly paths: number;
73+ readonly operations: number;
74+ readonly schemas: number;
75+ }
76+> = {};
77+
78+for (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+
105+await writeFile(
106+ join(outDir, "manifest.json"),
107+ `${JSON.stringify({ source, sourceSha256, generatedAt: new Date().toISOString(), assets: manifestAssets }, null, 2)}\n`,
108+);
109+console.log(`wrote ${selections.length} slices + manifest.json to ${outDir}`);