Skip to content

Commit 2ab9edd

Browse files
skywing918Kyle Zhang
andauthored
[TSV-C#] Add TSV rules for new C# emitters (Azure#38924)
* add HTTP client C# rules for emitter output directory and namespace validation * Enhance rule validation by introducing RuleFailureType enum and updating createFailedResult method to include failure types * Fix import order for RuleResult and RuleFailureType in sdk-tspconfig-validation.ts * Add validation behavior tests for CSharp emitters in sdk-tspconfig-validation.test.ts * Refactor test case creation functions for improved clarity and maintainability * Refactor RuleResult and related validation logic to remove RuleFailureType dependency * fix format issue * [TypeSpec Validation] Enhance validation logic for emitter options to handle variable resolution * Enhance validation logic for emitter output directory to support variable resolution and update test cases accordingly * Fix validation logic to correctly track failed sub-rule results in SdkTspConfigValidationRule * For @azure-tools/typespec-go data plane SDKs ignore validation for configuration not found errors * fix: remove unnecessary whitespace in SdkTspConfigValidationRule * test: add allowUndefined parameter to goDpInjectSpans and goDpEmitterOutputDir test cases * Refactor Tspconfig validation logic and clean up test cases for http-client-csharp * Enhance Tspconfig validation: add variable resolution for emitter options and improve test cases for http-client-csharp * Enhance Tspconfig validation: add support for variable resolution in emitter-output-dir and update test cases for http-client-csharp * Refactor Tspconfig validation rules for improved clarity and maintainability * Add emitter-output-dir for http-client-csharp in Tspconfig for EventHub and Search * revert * refactor: clean up test files and improve code structure * test: add test cases for emitter output directory in TypeSpec validation --------- Co-authored-by: Kyle Zhang <[email protected]>
1 parent 3528053 commit 2ab9edd

File tree

2 files changed

+185
-8
lines changed

2 files changed

+185
-8
lines changed

eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class TspconfigEmitterOptionsSubRuleBase extends TspconfigSubRuleBase {
232232
// Format 1: {output-dir}/{service-dir}/azure-mgmt-advisor
233233
// Format 2: {service-dir}/azure-mgmt-advisor where service-dir might include {output-dir}
234234
// Format 3: {output-dir}/{service-dir}/azadmin/settings where we need to validate "azadmin/settings"
235+
// Format 4: {output-dir}/sdk/dellstorage/{xxxx} where we need to validate "{xxxx}"
235236

236237
let extractedPath: string;
237238
if (!actualValue.includes("/")) {
@@ -241,9 +242,15 @@ class TspconfigEmitterOptionsSubRuleBase extends TspconfigSubRuleBase {
241242
const filteredParts = pathParts.filter(
242243
(part) => !(part === "{output-dir}" || part === "{service-dir}"),
243244
);
244-
extractedPath = filteredParts.join("/");
245-
}
246245

246+
// If the last part is a variable (e.g., {namespace}, {package-name}, {xxxx}), use it as extractedPath
247+
const lastPart = pathParts[pathParts.length - 1];
248+
if (lastPart.startsWith("{") && lastPart.endsWith("}")) {
249+
extractedPath = lastPart;
250+
} else {
251+
extractedPath = filteredParts.join("/");
252+
}
253+
}
247254
// Resolve variables in the extracted path
248255
return this.resolveVariables(extractedPath, config);
249256
}
@@ -256,13 +263,25 @@ class TspconfigEmitterOptionsSubRuleBase extends TspconfigSubRuleBase {
256263
`Please add "options.${this.emitterName}.${this.keyToValidate}" with expected value "${this.expectedValue}"`,
257264
);
258265

259-
const actualValue = option as unknown as undefined | string | boolean;
266+
let actualValue = option as unknown as undefined | string | boolean;
267+
268+
// Resolve variables if the value is a string
269+
if (typeof actualValue === "string" && actualValue.includes("{")) {
270+
const { resolved, error } = this.resolveVariables(actualValue, config);
271+
if (error) {
272+
return this.createFailedResult(
273+
error,
274+
`Please define the variable in your configuration or use a direct value`,
275+
);
276+
}
277+
actualValue = resolved;
278+
}
279+
260280
if (!this.validateValue(actualValue, this.expectedValue))
261281
return this.createFailedResult(
262282
`The value of options.${this.emitterName}.${this.keyToValidate} "${actualValue}" does not match "${this.expectedValue}"`,
263283
`Please update the value of "options.${this.emitterName}.${this.keyToValidate}" to match "${this.expectedValue}"`,
264284
);
265-
266285
return { success: true };
267286
}
268287

@@ -708,6 +727,51 @@ export class TspConfigPythonDpEmitterOutputDirSubRule extends TspconfigEmitterOp
708727
}
709728
}
710729

730+
// ----- CSharp sub rules -----
731+
export class TspConfigCsharpDpEmitterOutputDirSubRule extends TspconfigEmitterOptionsEmitterOutputDirSubRuleBase {
732+
constructor() {
733+
super("@azure-typespec/http-client-csharp", "emitter-output-dir", new RegExp(/^Azure\./));
734+
}
735+
protected skip(_: any, folder: string) {
736+
return skipForManagementPlane(folder);
737+
}
738+
}
739+
740+
export class TspConfigCsharpDpNamespaceSubRule extends TspconfigEmitterOptionsSubRuleBase {
741+
constructor() {
742+
super("@azure-typespec/http-client-csharp", "namespace", new RegExp(/^Azure\./));
743+
}
744+
protected skip(_: any, folder: string) {
745+
return skipForManagementPlane(folder);
746+
}
747+
}
748+
749+
export class TspConfigCsharpMgmtEmitterOutputDirSubRule extends TspconfigEmitterOptionsEmitterOutputDirSubRuleBase {
750+
constructor() {
751+
super(
752+
"@azure-typespec/http-client-csharp-mgmt",
753+
"emitter-output-dir",
754+
new RegExp(/^Azure\.ResourceManager\./),
755+
);
756+
}
757+
protected skip(_: any, folder: string) {
758+
return skipForDataPlane(folder);
759+
}
760+
}
761+
762+
export class TspConfigCsharpMgmtNamespaceSubRule extends TspconfigEmitterOptionsSubRuleBase {
763+
constructor() {
764+
super(
765+
"@azure-typespec/http-client-csharp-mgmt",
766+
"namespace",
767+
new RegExp(/^Azure\.ResourceManager\./),
768+
);
769+
}
770+
protected skip(_: any, folder: string) {
771+
return skipForDataPlane(folder);
772+
}
773+
}
774+
711775
/**
712776
* Required rules: When a tspconfig.yaml exists, any applicable rule in the requiredRules array
713777
* that fails validation will cause the entire SdkTspConfigValidationRule to fail. For example,
@@ -749,6 +813,10 @@ export const requiredRules = [
749813
* validation failures will affect the overall validation result.
750814
*/
751815
export const optionalRules: TspconfigEmitterOptionsSubRuleBase[] = [
816+
new TspConfigCsharpDpEmitterOutputDirSubRule(),
817+
new TspConfigCsharpDpNamespaceSubRule(),
818+
new TspConfigCsharpMgmtNamespaceSubRule(),
819+
new TspConfigCsharpMgmtEmitterOutputDirSubRule(),
752820
new TspConfigGoDpServiceDirMatchPatternSubRule(),
753821
new TspConfigGoDpEmitterOutputDirMatchPatternSubRule(),
754822
new TspConfigGoDpModuleMatchPatternSubRule(),

eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { stringify } from "yaml";
1010
import {
1111
SdkTspConfigValidationRule,
1212
TspConfigCommonAzServiceDirMatchPatternSubRule,
13+
TspConfigCsharpDpEmitterOutputDirSubRule,
14+
TspConfigCsharpDpNamespaceSubRule,
15+
TspConfigCsharpMgmtEmitterOutputDirSubRule,
16+
TspConfigCsharpMgmtNamespaceSubRule,
1317
TspConfigGoDpContainingModuleMatchPatternSubRule,
1418
TspConfigGoDpEmitterOutputDirMatchPatternSubRule,
1519
TspConfigGoDpModuleMatchPatternSubRule,
@@ -572,6 +576,42 @@ const pythonDpEmitterOutputTestCases = createEmitterOptionTestCases(
572576
[new TspConfigPythonDpEmitterOutputDirSubRule()],
573577
);
574578

579+
const csharpDpNamespaceTestCases = createEmitterOptionTestCases(
580+
"@azure-typespec/http-client-csharp",
581+
"",
582+
"namespace",
583+
"Azure.AI.Vision.Face",
584+
"AI.Vision.Face",
585+
[new TspConfigCsharpDpNamespaceSubRule()],
586+
);
587+
588+
const csharpDpEmitterOutputDirTestCases = createEmitterOptionTestCases(
589+
"@azure-typespec/http-client-csharp",
590+
"",
591+
"emitter-output-dir",
592+
"{output-dir}/{service-dir}/Azure.AI.Vision.Face",
593+
"{output-dir}/{service-dir}/AI.Vision.Face",
594+
[new TspConfigCsharpDpEmitterOutputDirSubRule()],
595+
);
596+
597+
const csharpMgmtNamespaceTestCases = createEmitterOptionTestCases(
598+
"@azure-typespec/http-client-csharp-mgmt",
599+
managementTspconfigFolder,
600+
"namespace",
601+
"Azure.ResourceManager.compute",
602+
"Azure.compute", // Invalid: missing "resourcemanager"
603+
[new TspConfigCsharpMgmtNamespaceSubRule()],
604+
);
605+
606+
const csharpMgmtEmitterOutputDirTestCases = createEmitterOptionTestCases(
607+
"@azure-typespec/http-client-csharp-mgmt",
608+
managementTspconfigFolder,
609+
"emitter-output-dir",
610+
"{output-dir}/{service-dir}/Azure.ResourceManager.compute",
611+
"{output-dir}/{service-dir}/ResourceManager.compute", // Invalid: missing "Azure."
612+
[new TspConfigCsharpMgmtEmitterOutputDirSubRule()],
613+
);
614+
575615
// Test cases for emitter-output-dir with namespace variable resolution
576616
const emitterOutputDirWithNamespaceVariableTestCases: Case[] = [
577617
{
@@ -610,17 +650,81 @@ options:
610650
subRules: [new TspConfigJavaMgmtEmitterOutputDirMatchPatternSubRule()],
611651
},
612652
{
613-
description: "Validate Ts mgmt recursive variable resolution (namespace -> package-name)",
653+
description: "Validate http-client-csharp namespace with {package-name} variable",
654+
folder: "",
655+
tspconfigContent: `
656+
options:
657+
"@azure-typespec/http-client-csharp":
658+
package-name: "Azure.MyService"
659+
namespace: "{package-name}"
660+
`,
661+
success: true,
662+
subRules: [new TspConfigCsharpDpNamespaceSubRule()],
663+
},
664+
{
665+
description: "Validate http-client-csharp namespace with invalid {package-name} variable value",
666+
folder: "",
667+
tspconfigContent: `
668+
options:
669+
"@azure-typespec/http-client-csharp":
670+
package-name: "MyService"
671+
namespace: "{package-name}"
672+
`,
673+
success: false,
674+
subRules: [new TspConfigCsharpDpNamespaceSubRule()],
675+
},
676+
{
677+
description: "Validate http-client-csharp-mgmt emitter-output-dir with {package-name} variable",
678+
folder: managementTspconfigFolder,
679+
tspconfigContent: `
680+
options:
681+
"@azure-typespec/http-client-csharp-mgmt":
682+
package-name: "Azure.ResourceManager.MyService"
683+
emitter-output-dir: "{output-dir}/{service-dir}/{package-name}"
684+
`,
685+
success: true,
686+
subRules: [new TspConfigCsharpMgmtEmitterOutputDirSubRule()],
687+
},
688+
{
689+
description:
690+
"Validate http-client-csharp-mgmt recursive variable resolution (namespace -> package-name)",
614691
folder: managementTspconfigFolder,
615692
tspconfigContent: `
616693
options:
617-
"@azure-tools/typespec-ts":
618-
package-name: "arm-aaa-bbb"
694+
"@azure-typespec/http-client-csharp-mgmt":
695+
package-name: "Azure.ResourceManager.Compute.Recommender"
619696
namespace: "{package-name}"
620697
emitter-output-dir: "{output-dir}/{service-dir}/{namespace}"
621698
`,
622699
success: true,
623-
subRules: [new TspConfigTsMgmtModularEmitterOutputDirSubRule()],
700+
subRules: [new TspConfigCsharpMgmtEmitterOutputDirSubRule()],
701+
},
702+
{
703+
description:
704+
"Validate http-client-csharp-mgmt emitter-output-dir with custom service-dir and recursive variable resolution",
705+
folder: managementTspconfigFolder,
706+
tspconfigContent: `
707+
options:
708+
"@azure-typespec/http-client-csharp-mgmt":
709+
package-name: "Azure.ResourceManager.Compute.Recommender"
710+
namespace: "{package-name}"
711+
emitter-output-dir: "{output-dir}/sdk/dellstorage/{namespace}"
712+
`,
713+
success: true,
714+
subRules: [new TspConfigCsharpMgmtEmitterOutputDirSubRule()],
715+
},
716+
{
717+
description:
718+
"Validate http-client-csharp-mgmt emitter-output-dir fails when last segment is invalid",
719+
folder: managementTspconfigFolder,
720+
tspconfigContent: `
721+
options:
722+
"@azure-typespec/http-client-csharp-mgmt":
723+
namespace: "Azure.ResourceManager.Dell.Storage"
724+
emitter-output-dir: "{output-dir}/sdk/dellstorage/Dell.Storage"
725+
`,
726+
success: false,
727+
subRules: [new TspConfigCsharpMgmtEmitterOutputDirSubRule()],
624728
},
625729
];
626730

@@ -800,6 +904,11 @@ describe("tspconfig", function () {
800904
// Test cases for optional rules when emitter is not configured
801905
...optionalRulesWithoutEmitterConfigTestCases,
802906
...optionalRulesWithEmitterConfigTestCases,
907+
// csharp
908+
...csharpDpNamespaceTestCases,
909+
...csharpMgmtNamespaceTestCases,
910+
...csharpDpEmitterOutputDirTestCases,
911+
...csharpMgmtEmitterOutputDirTestCases,
803912
// go data plane
804913
...goDpEmitterOutputDirTestCases,
805914
...goDpServiceDirTestCases,

0 commit comments

Comments
 (0)