|
1 | 1 | import fsp from 'fs/promises'; |
| 2 | +import path from 'path'; |
2 | 3 |
|
3 | 4 | import { GENERATORS, capitalize, exists, toAbsolutePath } from '../common.ts'; |
4 | 5 | import type { Language } from '../types.ts'; |
5 | 6 |
|
6 | | -import { getSnippetFile } from '../config.ts'; |
| 7 | +import { getCTSRequestDir, getSnippetFile } from '../config.ts'; |
7 | 8 | import type { CodeSamples, OpenAPICodeSample, SampleForOperation } from './types.ts'; |
8 | 9 |
|
| 10 | +// Reserved key used to store the preferred code sample (marked with isCodeSample: true in CTS JSON). |
| 11 | +export const CODE_SAMPLE_KEY = 'PREFERRED_SNIPPET'; |
| 12 | + |
9 | 13 | export function getCodeSampleLabel(language: Language): OpenAPICodeSample['label'] { |
10 | 14 | switch (language) { |
11 | 15 | case 'csharp': |
@@ -52,6 +56,53 @@ export function generateSnippetsJSON(codeSamples: CodeSamples): CodeSamples { |
52 | 56 | return codeSamples; |
53 | 57 | } |
54 | 58 |
|
| 59 | +// Reads CTS request JSON files and, for tests marked with `isCodeSample: true`, |
| 60 | +// tags the matching snippet with the reserved CODE_SAMPLE_KEY. |
| 61 | +// Throws if more than one test per operationId is marked as a code sample. |
| 62 | +export async function tagCodeSamples(clientName: string, codeSamples: CodeSamples): Promise<void> { |
| 63 | + const ctsDir = toAbsolutePath(getCTSRequestDir(clientName)); |
| 64 | + |
| 65 | + if (!(await exists(ctsDir))) { |
| 66 | + return; |
| 67 | + } |
| 68 | + |
| 69 | + const files = await fsp.readdir(ctsDir); |
| 70 | + |
| 71 | + for (const file of files) { |
| 72 | + // Use basename to strip any accidental path components returned by readdir. |
| 73 | + const safeFile = path.basename(file); |
| 74 | + if (!safeFile.endsWith('.json')) { |
| 75 | + continue; |
| 76 | + } |
| 77 | + |
| 78 | + const operationId = path.basename(safeFile, '.json'); |
| 79 | + const tests: Array<{ testName: string; isCodeSample?: boolean }> = JSON.parse( |
| 80 | + await fsp.readFile(path.join(ctsDir, safeFile), 'utf8'), |
| 81 | + ); |
| 82 | + |
| 83 | + const codeSampleTests = tests.filter((t) => t.isCodeSample === true); |
| 84 | + |
| 85 | + if (codeSampleTests.length > 1) { |
| 86 | + throw new Error( |
| 87 | + `Found ${codeSampleTests.length} tests with isCodeSample: true for operationId "${operationId}" in ${clientName}. Only one is allowed.`, |
| 88 | + ); |
| 89 | + } |
| 90 | + |
| 91 | + if (codeSampleTests.length === 0) { |
| 92 | + continue; |
| 93 | + } |
| 94 | + |
| 95 | + const testName = codeSampleTests[0].testName; |
| 96 | + |
| 97 | + for (const lang of Object.keys(codeSamples) as Language[]) { |
| 98 | + const samplesForOp = codeSamples[lang][operationId]; |
| 99 | + if (samplesForOp !== undefined && Object.hasOwn(samplesForOp, testName)) { |
| 100 | + samplesForOp[CODE_SAMPLE_KEY] = samplesForOp[testName]; |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
55 | 106 | // Reads the generated `docs/snippets/` file for every languages of the given `clientName` and builds an hashmap of snippets per operationId per language. |
56 | 107 | export async function transformGeneratedSnippetsToCodeSamples(clientName: string): Promise<CodeSamples> { |
57 | 108 | const codeSamples = Object.values(GENERATORS).reduce<CodeSamples>( |
@@ -126,5 +177,7 @@ export async function transformGeneratedSnippetsToCodeSamples(clientName: string |
126 | 177 | } |
127 | 178 | } |
128 | 179 |
|
| 180 | + await tagCodeSamples(clientName, codeSamples); |
| 181 | + |
129 | 182 | return codeSamples; |
130 | 183 | } |
0 commit comments