Skip to content

Commit 0dc60f5

Browse files
markcowlCopilot
andcommitted
Add @featureFile decorators in Azure.ResourceManager and fix template suppression propagation
- Add @featureFile, @featureFiles, @featureFileOptions decorators in Azure.ResourceManager namespace as alternatives to the Legacy @feature, @features, @featureOptions decorators - Fix arm-custom-resource-usage-discourage rule to propagate suppressions from model templates to their instantiations - Regenerate reference docs - Update tests to use new decorator names Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 679b073 commit 0dc60f5

20 files changed

Lines changed: 533 additions & 36 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
changeKind: feature
3+
packages:
4+
- "@azure-tools/typespec-azure-resource-manager"
5+
- "@azure-tools/typespec-autorest"
6+
- "@azure-tools/typespec-azure-rulesets"
7+
---
8+
9+
Add `@featureFile`, `@featureFiles`, and `@featureFileOptions` decorators in `Azure.ResourceManager` namespace as alternatives to the Legacy `@feature`, `@features`, and `@featureOptions` decorators. Add `arm-feature-file-usage-discourage` linting rule. Fix `arm-custom-resource-usage-discourage` rule to propagate suppressions from model templates to their instantiations.

packages/typespec-autorest/test/arm/arm.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ it("generates PATCH bodies for resource patch of common resource envelope mixins
509509
it("can split resources and operations by feature", async () => {
510510
const { privateLink, privateEndpoint } = await CompileOpenApiWithFeatures(
511511
`
512-
@Azure.ResourceManager.Legacy.features(Features)
512+
@Azure.ResourceManager.featureFiles(Features)
513513
@armProviderNamespace
514514
@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5)
515515
namespace Microsoft.PrivateLinkTest;
@@ -522,7 +522,7 @@ it("can split resources and operations by feature", async () => {
522522
523523
interface Operations extends Azure.ResourceManager.Operations {}
524524
525-
@Azure.ResourceManager.Legacy.feature(Features.privateEndpoint)
525+
@Azure.ResourceManager.featureFile(Features.privateEndpoint)
526526
@tenantResource
527527
model PrivateEndpointConnectionResource is ProxyResource<PrivateEndpointConnectionProperties> {
528528
@path
@@ -531,20 +531,20 @@ it("can split resources and operations by feature", async () => {
531531
name: string;
532532
}
533533
534-
@Azure.ResourceManager.Legacy.feature(Features.privateEndpoint)
534+
@Azure.ResourceManager.featureFile(Features.privateEndpoint)
535535
@armResourceOperations(PrivateEndpointConnectionResource)
536536
interface PrivateEndpointConnections {
537537
#suppress "deprecated" "PrivateLinkResourceListResultV5 validation"
538538
listConnections is ArmResourceListByParent<PrivateEndpointConnectionResource,
539539
Response = ArmResponse<Azure.ResourceManager.CommonTypes.PrivateEndpointConnectionListResultV5>>;
540540
}
541541
542-
@Azure.ResourceManager.Legacy.feature(Features.privateLink)
542+
@Azure.ResourceManager.featureFile(Features.privateLink)
543543
model PrivateLinkResource is ProxyResource<PrivateLinkResourceProperties> {
544544
...PrivateLinkResourceParameter;
545545
}
546546
547-
@Azure.ResourceManager.Legacy.feature(Features.privateLink)
547+
@Azure.ResourceManager.featureFile(Features.privateLink)
548548
@armResourceOperations(PrivateLinkResource)
549549
interface PrivateLinkResources {
550550
#suppress "deprecated" "PrivateLinkResourceListResultV5 validation"
@@ -576,49 +576,49 @@ it("can represent type references within and between features", async () => {
576576
const { featureA, featureB, shared } = await CompileOpenApiWithFeatures(
577577
`
578578
579-
@Azure.ResourceManager.Legacy.features(Features)
579+
@Azure.ResourceManager.featureFiles(Features)
580580
@armProviderNamespace("Microsoft.Test")
581581
namespace Microsoft.Test;
582582
enum Features {
583-
@Azure.ResourceManager.Legacy.featureOptions(#{featureName: "Common", fileName: "shared", description: "The data for common features"})
583+
@Azure.ResourceManager.featureFileOptions(#{featureName: "Common", fileName: "shared", description: "The data for common features"})
584584
Common: "Common",
585-
@Azure.ResourceManager.Legacy.featureOptions(#{featureName: "FeatureA", fileName: "featureA", description: "The data for feature A"})
585+
@Azure.ResourceManager.featureFileOptions(#{featureName: "FeatureA", fileName: "featureA", description: "The data for feature A"})
586586
FeatureA: "Feature A",
587-
@Azure.ResourceManager.Legacy.featureOptions(#{featureName: "FeatureB", fileName: "featureB", description: "The data for feature B"})
587+
@Azure.ResourceManager.featureFileOptions(#{featureName: "FeatureB", fileName: "featureB", description: "The data for feature B"})
588588
FeatureB: "Feature B",
589589
}
590590
@secret
591591
scalar secretString extends string;
592592
593-
@Azure.ResourceManager.Legacy.feature(Features.FeatureA)
593+
@Azure.ResourceManager.featureFile(Features.FeatureA)
594594
model FooResource is TrackedResource<FooResourceProperties> {
595595
...ResourceNameParameter<FooResource>;
596596
}
597597
598-
@Azure.ResourceManager.Legacy.feature(Features.FeatureA)
598+
@Azure.ResourceManager.featureFile(Features.FeatureA)
599599
model FooResourceProperties {
600600
...DefaultProvisioningStateProperty;
601601
password: secretString;
602602
}
603603
604-
@Azure.ResourceManager.Legacy.feature(Features.FeatureB)
604+
@Azure.ResourceManager.featureFile(Features.FeatureB)
605605
model BarResource is ProxyResource<BarResourceProperties> {
606606
...ResourceNameParameter<BarResource>;
607607
}
608-
@Azure.ResourceManager.Legacy.feature(Features.FeatureB)
608+
@Azure.ResourceManager.featureFile(Features.FeatureB)
609609
model BarResourceProperties {
610610
...DefaultProvisioningStateProperty;
611611
password: secretString;
612612
}
613613
614-
@Azure.ResourceManager.Legacy.feature(Features.FeatureA)
614+
@Azure.ResourceManager.featureFile(Features.FeatureA)
615615
@armResourceOperations
616616
interface Foos extends Azure.ResourceManager.TrackedResourceOperations<FooResource, FooResourceProperties> {}
617617
618-
@Azure.ResourceManager.Legacy.feature(Features.FeatureB)
618+
@Azure.ResourceManager.featureFile(Features.FeatureB)
619619
@armResourceOperations
620620
interface Bars extends Azure.ResourceManager.TrackedResourceOperations<BarResource, BarResourceProperties> {}
621-
@@Azure.ResourceManager.Legacy.feature(Bars.get, Features.FeatureA);
621+
@@Azure.ResourceManager.featureFile(Bars.get, Features.FeatureA);
622622
`,
623623
["featureA", "featureB", "shared"],
624624
{ preset: "azure" },

packages/typespec-azure-resource-manager/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Available ruleSets:
4949
| [`@azure-tools/typespec-azure-resource-manager/version-progression`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/version-progression) | Validate that ARM service versions all use unique dates and are declared in strictly increasing chronological order. |
5050
| [`@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-custom-resource-no-key) | Validate that custom resource contains a key property. |
5151
| [`@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-custom-resource-usage-discourage) | Verify the usage of @customAzureResource decorator. |
52+
| [`@azure-tools/typespec-azure-resource-manager/arm-feature-file-usage-discourage`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-feature-file-usage-discourage) | Verify the usage of @featureFiles decorator. |
5253
| [`@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/beyond-nesting-levels) | Tracked Resources must use 3 or fewer levels of nesting. |
5354
| [`@azure-tools/typespec-azure-resource-manager/arm-resource-operation`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/arm-resource-operation) | Validate ARM Resource operations. |
5455
| [`@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-resource-delete-operation) | Check for resources that must have a delete operation. |
@@ -87,6 +88,9 @@ Available ruleSets:
8788
- [`@armResourceUpdate`](#@armresourceupdate)
8889
- [`@armVirtualResource`](#@armvirtualresource)
8990
- [`@extensionResource`](#@extensionresource)
91+
- [`@featureFile`](#@featurefile)
92+
- [`@featureFileOptions`](#@featurefileoptions)
93+
- [`@featureFiles`](#@featurefiles)
9094
- [`@identifiers`](#@identifiers)
9195
- [`@locationResource`](#@locationresource)
9296
- [`@resourceBaseType`](#@resourcebasetype)
@@ -398,6 +402,63 @@ See more details on [different Azure Resource Manager resource type here.](https
398402

399403
None
400404

405+
#### `@featureFile`
406+
407+
Decorator to associate a feature file with a model, interface, or namespace
408+
409+
```typespec
410+
@Azure.ResourceManager.featureFile(featureName: EnumMember)
411+
```
412+
413+
##### Target
414+
415+
The target to associate the feature file with
416+
`Model | Operation | Interface | Namespace`
417+
418+
##### Parameters
419+
420+
| Name | Type | Description |
421+
| ----------- | ------------ | ---------------------------------------- |
422+
| featureName | `EnumMember` | The feature to associate with the target |
423+
424+
#### `@featureFileOptions`
425+
426+
Decorator to define options for a specific feature file
427+
428+
```typespec
429+
@Azure.ResourceManager.featureFileOptions(options: valueof Azure.ResourceManager.ArmFeatureFileOptions)
430+
```
431+
432+
##### Target
433+
434+
The enum member that represents the feature
435+
`EnumMember`
436+
437+
##### Parameters
438+
439+
| Name | Type | Description |
440+
| ------- | --------------------------------------------------------- | -------------------------------- |
441+
| options | [valueof `ArmFeatureFileOptions`](#armfeaturefileoptions) | The options for the feature file |
442+
443+
#### `@featureFiles`
444+
445+
Decorator to define a set of feature files for splitting output
446+
447+
```typespec
448+
@Azure.ResourceManager.featureFiles(features: Enum)
449+
```
450+
451+
##### Target
452+
453+
The service namespace
454+
`Namespace`
455+
456+
##### Parameters
457+
458+
| Name | Type | Description |
459+
| -------- | ------ | ----------------------------------- |
460+
| features | `Enum` | The enum that contains the features |
461+
401462
#### `@identifiers`
402463

403464
This decorator is used to indicate the identifying properties of objects in the array, e.g. size

packages/typespec-azure-resource-manager/generated-defs/Azure.ResourceManager.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
DecoratorContext,
33
DecoratorValidatorCallbacks,
4+
Enum,
45
EnumMember,
56
EnumValue,
67
Interface,
@@ -17,6 +18,14 @@ export interface ResourceOperationOptions {
1718
readonly omitTags?: boolean;
1819
}
1920

21+
export interface ArmFeatureFileOptions {
22+
readonly featureName: string;
23+
readonly fileName: string;
24+
readonly description: string;
25+
readonly title?: string;
26+
readonly termsOfService?: string;
27+
}
28+
2029
/**
2130
* Marks the operation as being a collection action
2231
*/
@@ -348,6 +357,42 @@ export type ResourceBaseTypeDecorator = (
348357
baseTypeIt: Type,
349358
) => DecoratorValidatorCallbacks | void;
350359

360+
/**
361+
* Decorator to define a set of feature files for splitting output
362+
*
363+
* @param target The service namespace
364+
* @param features The enum that contains the features
365+
*/
366+
export type FeatureFilesDecorator = (
367+
context: DecoratorContext,
368+
target: Namespace,
369+
features: Enum,
370+
) => DecoratorValidatorCallbacks | void;
371+
372+
/**
373+
* Decorator to define options for a specific feature file
374+
*
375+
* @param target The enum member that represents the feature
376+
* @param options The options for the feature file
377+
*/
378+
export type FeatureFileOptionsDecorator = (
379+
context: DecoratorContext,
380+
target: EnumMember,
381+
options: ArmFeatureFileOptions,
382+
) => DecoratorValidatorCallbacks | void;
383+
384+
/**
385+
* Decorator to associate a feature file with a model, interface, or namespace
386+
*
387+
* @param target The target to associate the feature file with
388+
* @param featureName The feature to associate with the target
389+
*/
390+
export type FeatureFileDecorator = (
391+
context: DecoratorContext,
392+
target: Model | Operation | Interface | Namespace,
393+
featureName: EnumMember,
394+
) => DecoratorValidatorCallbacks | void;
395+
351396
export type AzureResourceManagerDecorators = {
352397
armResourceCollectionAction: ArmResourceCollectionActionDecorator;
353398
armProviderNameValue: ArmProviderNameValueDecorator;
@@ -372,4 +417,7 @@ export type AzureResourceManagerDecorators = {
372417
armCommonTypesVersion: ArmCommonTypesVersionDecorator;
373418
armVirtualResource: ArmVirtualResourceDecorator;
374419
resourceBaseType: ResourceBaseTypeDecorator;
420+
featureFiles: FeatureFilesDecorator;
421+
featureFileOptions: FeatureFileOptionsDecorator;
422+
featureFile: FeatureFileDecorator;
375423
};

packages/typespec-azure-resource-manager/lib/decorators.tsp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,44 @@ extern dec resourceBaseType(
274274
* ```
275275
*/
276276
extern dec identifiers(entity: ModelProperty | Array<unknown>, properties: valueof string[]);
277+
278+
/**
279+
* Options for defining a feature file and its associated output
280+
*/
281+
model ArmFeatureFileOptions {
282+
/** The feature name */
283+
featureName: string;
284+
285+
/** The associated file name for the features */
286+
fileName: string;
287+
288+
/** The feature description in Swagger */
289+
description: string;
290+
291+
/** The feature title in Swagger */
292+
title?: string;
293+
294+
/** The feature terms of service in Swagger */
295+
termsOfService?: string;
296+
}
297+
298+
/**
299+
* Decorator to define a set of feature files for splitting output
300+
* @param target The service namespace
301+
* @param features The enum that contains the features
302+
*/
303+
extern dec featureFiles(target: Namespace, features: Enum);
304+
305+
/**
306+
* Decorator to define options for a specific feature file
307+
* @param target The enum member that represents the feature
308+
* @param options The options for the feature file
309+
*/
310+
extern dec featureFileOptions(target: EnumMember, options: valueof ArmFeatureFileOptions);
311+
312+
/**
313+
* Decorator to associate a feature file with a model, interface, or namespace
314+
* @param target The target to associate the feature file with
315+
* @param featureName The feature to associate with the target
316+
*/
317+
extern dec featureFile(target: Model | Operation | Interface | Namespace, featureName: EnumMember);

packages/typespec-azure-resource-manager/src/linter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { armCommonTypesVersionRule } from "./rules/arm-common-types-version.js";
55
import { armCustomResourceNoKey } from "./rules/arm-custom-resource-no-key.js";
66
import { armCustomResourceUsageDiscourage } from "./rules/arm-custom-resource-usage-discourage.js";
77
import { armDeleteResponseCodesRule } from "./rules/arm-delete-response-codes.js";
8+
import { armFeatureFileUsageDiscourage } from "./rules/arm-feature-file-usage-discourage.js";
89
import { armNoPathCasingConflictsRule } from "./rules/arm-no-path-casing-conflicts.js";
910
import { armNoRecordRule } from "./rules/arm-no-record.js";
1011
import { armPostResponseCodesRule } from "./rules/arm-post-response-codes.js";
@@ -61,6 +62,7 @@ const rules = [
6162
versionProgressionRule,
6263
armCustomResourceNoKey,
6364
armCustomResourceUsageDiscourage,
65+
armFeatureFileUsageDiscourage,
6466
beyondNestingRule,
6567
coreOperationsRule,
6668
deleteOperationMissingRule,

packages/typespec-azure-resource-manager/src/resource.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
ArmResourceOperationsDecorator,
3434
ArmVirtualResourceDecorator,
3535
ExtensionResourceDecorator,
36+
FeatureFileDecorator,
37+
FeatureFileOptionsDecorator,
38+
FeatureFilesDecorator,
3639
IdentifiersDecorator,
3740
LocationResourceDecorator,
3841
ResourceBaseTypeDecorator,
@@ -1519,6 +1522,10 @@ export const [getResourceFeatureSet, setResourceFeatureSet] = useStateMap<
15191522
Map<string, ArmFeatureOptions>
15201523
>(ArmStateKeys.armFeatureSet);
15211524

1525+
export const [getFeatureFileSet, setFeatureFileSet] = useStateMap<Namespace, boolean>(
1526+
ArmStateKeys.armFeatureFileSet,
1527+
);
1528+
15221529
export const [getResourceFeatureOptions, setResourceFeatureOptions] = useStateMap<
15231530
EnumMember,
15241531
ArmFeatureOptions
@@ -1659,3 +1666,16 @@ export const $featureOptions: FeatureOptionsDecorator = (
16591666
) => {
16601667
setResourceFeatureOptions(context.program, entity, options);
16611668
};
1669+
1670+
// New Azure.ResourceManager namespace decorators
1671+
export const $featureFile: FeatureFileDecorator = $feature as unknown as FeatureFileDecorator;
1672+
export const $featureFiles: FeatureFilesDecorator = (
1673+
context: DecoratorContext,
1674+
entity: Namespace,
1675+
features: Enum,
1676+
) => {
1677+
setFeatureFileSet(context.program, entity, true);
1678+
$features(context, entity, features);
1679+
};
1680+
export const $featureFileOptions: FeatureFileOptionsDecorator =
1681+
$featureOptions as unknown as FeatureFileOptionsDecorator;

0 commit comments

Comments
 (0)