Skip to content

Commit 0ce39ea

Browse files
iscai-msftCopilot
andcommitted
feat(typespec-metadata): add apiVersion field using TCGC resolution
Add apiVersion to LanguagePackageMetadata interface, resolved via createSdkContext from @azure-tools/typespec-client-generator-core. Resolution logic: - Map size > 1 → "multiple-versions" - Map size == 1 → the single value ("all" or actual version string) - Empty/undefined → undefined Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c95a24d5-2785-44e4-9599-ac2f5cb37a2c
1 parent aa8cf95 commit 0ce39ea

5 files changed

Lines changed: 122 additions & 2 deletions

File tree

packages/typespec-metadata/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@
4747
"yaml": "catalog:"
4848
},
4949
"peerDependencies": {
50+
"@azure-tools/typespec-client-generator-core": "workspace:^",
5051
"@typespec/compiler": "workspace:^"
5152
},
5253
"devDependencies": {
54+
"@azure-tools/typespec-client-generator-core": "workspace:^",
5355
"@types/node": "catalog:",
5456
"@typespec/compiler": "workspace:^",
5557
"@vitest/coverage-v8": "catalog:",

packages/typespec-metadata/src/collector.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ export interface LanguageCollectionResult {
321321
export async function collectLanguagePackages(
322322
program: Program,
323323
baseOutputDir: string,
324+
resolvedApiVersion?: string,
324325
): Promise<LanguageCollectionResult> {
325326
const optionMap = program.compilerOptions.options ?? {};
326327
const params = extractParameters(optionMap);
@@ -336,7 +337,7 @@ export async function collectLanguagePackages(
336337
}
337338

338339
return {
339-
languages: buildLanguageMetadata(optionMap, params, baseOutputDir, defaultServiceDir),
340+
languages: buildLanguageMetadata(optionMap, params, baseOutputDir, defaultServiceDir, resolvedApiVersion),
340341
sourceConfigPath: program.compilerOptions.config,
341342
};
342343
}
@@ -483,6 +484,7 @@ export function buildLanguageMetadata(
483484
params: Record<string, unknown>,
484485
baseOutputDir: string,
485486
defaultServiceDir?: string,
487+
resolvedApiVersion?: string,
486488
): Record<string, LanguagePackageMetadata[]> {
487489
const languagesDict: Record<string, LanguagePackageMetadata[]> = {};
488490

@@ -493,6 +495,7 @@ export function buildLanguageMetadata(
493495
params,
494496
baseOutputDir,
495497
defaultServiceDir,
498+
resolvedApiVersion,
496499
);
497500
const language = inferLanguageFromEmitterName(emitterName);
498501
if (!languagesDict[language]) {
@@ -510,6 +513,7 @@ function createLanguageMetadata(
510513
params: Record<string, unknown>,
511514
baseOutputDir: string,
512515
defaultServiceDir?: string,
516+
resolvedApiVersion?: string,
513517
): LanguagePackageMetadata {
514518
const normalizedOptions = normalizeOptionsObject(emitterOptions);
515519

@@ -585,6 +589,7 @@ function createLanguageMetadata(
585589
outputDir: relativeOutputDir,
586590
flavor: flavor ? String(flavor) : undefined,
587591
serviceDir,
592+
apiVersion: resolvedApiVersion,
588593
};
589594
}
590595

packages/typespec-metadata/src/emitter.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { emitFile, getDirectoryPath, resolvePath, type EmitContext } from "@typespec/compiler";
2+
import { createSdkContext } from "@azure-tools/typespec-client-generator-core";
23
import { stringify as stringifyYaml } from "yaml";
34
import packageJson from "../package.json" with { type: "json" };
45
import { buildSpecMetadata, collectLanguagePackages } from "./collector.js";
@@ -18,7 +19,23 @@ export async function $onEmit(context: EmitContext<MetadataEmitterOptions>): Pro
1819
// Get the common tsp-output directory (parent of this emitter's output dir)
1920
const commonOutputDir = getDirectoryPath(getDirectoryPath(context.emitterOutputDir));
2021

21-
const languageResult = await collectLanguagePackages(context.program, commonOutputDir);
22+
// Resolve API version using TCGC
23+
const sdkContext = await createSdkContext(context as any);
24+
const apiVersionsMap = sdkContext.sdkPackage.metadata.apiVersions;
25+
let resolvedApiVersion: string | undefined;
26+
if (apiVersionsMap && apiVersionsMap.size > 0) {
27+
if (apiVersionsMap.size > 1) {
28+
resolvedApiVersion = "multiple-versions";
29+
} else {
30+
resolvedApiVersion = [...apiVersionsMap.values()][0];
31+
}
32+
}
33+
34+
const languageResult = await collectLanguagePackages(
35+
context.program,
36+
commonOutputDir,
37+
resolvedApiVersion,
38+
);
2239

2340
const snapshot: MetadataSnapshot = {
2441
emitterVersion: SNAPSHOT_VERSION,

packages/typespec-metadata/src/metadata.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export interface LanguagePackageMetadata {
2020
flavor?: string;
2121
/** Service directory path for this language emitter. */
2222
serviceDir?: string;
23+
/**
24+
* Resolved API version for this emitter.
25+
* - `"all"` when configured for all versions
26+
* - `"multiple-versions"` for multi-service configs with more than one API version entry
27+
* - An actual resolved version string (e.g. `"2023-10-01"`) for a single version
28+
* - `undefined` when no API version information is available
29+
*/
30+
apiVersion?: string;
2331
}
2432

2533
export interface MetadataSnapshot {

packages/typespec-metadata/test/collector.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,3 +681,91 @@ describe("multiple emitters per language", () => {
681681
expect(result["unknown"][1].emitterName).toBe("@typespec/json-schema");
682682
});
683683
});
684+
685+
describe("apiVersion extraction", () => {
686+
it("passes through 'all' as the resolved apiVersion", () => {
687+
const optionMap = {
688+
"@azure-tools/typespec-python": {
689+
"package-name": "azure-keyvault-secrets",
690+
},
691+
};
692+
693+
const result = buildLanguageMetadata(optionMap, {}, "/base/tsp-output", undefined, "all");
694+
695+
expect(result["python"][0].apiVersion).toBe("all");
696+
});
697+
698+
it("passes through an actual resolved version string", () => {
699+
const optionMap = {
700+
"@azure-tools/typespec-python": {
701+
"package-name": "azure-keyvault-secrets",
702+
},
703+
};
704+
705+
const result = buildLanguageMetadata(
706+
optionMap,
707+
{},
708+
"/base/tsp-output",
709+
undefined,
710+
"2023-10-01",
711+
);
712+
713+
expect(result["python"][0].apiVersion).toBe("2023-10-01");
714+
});
715+
716+
it("passes through 'multiple-versions' for multi-service configs", () => {
717+
const optionMap = {
718+
"@azure-tools/typespec-python": {
719+
"package-name": "azure-keyvault-secrets",
720+
},
721+
};
722+
723+
const result = buildLanguageMetadata(
724+
optionMap,
725+
{},
726+
"/base/tsp-output",
727+
undefined,
728+
"multiple-versions",
729+
);
730+
731+
expect(result["python"][0].apiVersion).toBe("multiple-versions");
732+
});
733+
734+
it("leaves apiVersion undefined when no resolved version is provided", () => {
735+
const optionMap = {
736+
"@azure-tools/typespec-python": {
737+
"package-name": "azure-keyvault-secrets",
738+
},
739+
};
740+
741+
const result = buildLanguageMetadata(optionMap, {}, "/base/tsp-output");
742+
743+
expect(result["python"][0].apiVersion).toBeUndefined();
744+
});
745+
746+
it("applies the same resolved apiVersion to all emitters", () => {
747+
const optionMap = {
748+
"@azure-tools/typespec-python": {
749+
"package-name": "azure-keyvault-secrets",
750+
},
751+
"@azure-tools/typespec-java": {
752+
"package-name": "com.azure:azure-keyvault-secrets",
753+
},
754+
"@azure-tools/typespec-csharp": {
755+
"package-name": "Azure.Security.KeyVault.Secrets",
756+
},
757+
};
758+
759+
const result = buildLanguageMetadata(
760+
optionMap,
761+
{},
762+
"/base/tsp-output",
763+
undefined,
764+
"2023-10-01",
765+
);
766+
767+
expect(result["python"][0].apiVersion).toBe("2023-10-01");
768+
expect(result["java"][0].apiVersion).toBe("2023-10-01");
769+
expect(result["csharp"][0].apiVersion).toBe("2023-10-01");
770+
});
771+
});

0 commit comments

Comments
 (0)