Skip to content

Commit 914dcbc

Browse files
committed
Implement and test feature decorators
1 parent 6d67d6c commit 914dcbc

File tree

9 files changed

+273
-5
lines changed

9 files changed

+273
-5
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
changeKind: feature
3+
packages:
4+
- "@azure-tools/typespec-autorest"
5+
- "@azure-tools/typespec-azure-resource-manager"
6+
---
7+
8+
Add support for multiple output files in typespec-autorest

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,9 @@ This allows sharing Azure Resource Manager resource types across specifications
572572
- [`@armOperationRoute`](#@armoperationroute)
573573
- [`@customAzureResource`](#@customazureresource)
574574
- [`@externalTypeRef`](#@externaltyperef)
575+
- [`@feature`](#@feature)
576+
- [`@featureOptions`](#@featureoptions)
577+
- [`@features`](#@features)
575578
- [`@renamePathParameter`](#@renamepathparameter)
576579

577580
#### `@armExternalType`
@@ -648,6 +651,63 @@ Specify an external reference that should be used when emitting this type.
648651
| ------- | ---------------- | ------------------------------------------------------------- |
649652
| jsonRef | `valueof string` | External reference(e.g. "../../common.json#/definitions/Foo") |
650653

654+
#### `@feature`
655+
656+
Decorator to associate a feature with a model, interface, or namespace
657+
658+
```typespec
659+
@Azure.ResourceManager.Legacy.feature(featureName: EnumMember)
660+
```
661+
662+
##### Target
663+
664+
The target to associate the feature with
665+
`Model | Interface | Namespace`
666+
667+
##### Parameters
668+
669+
| Name | Type | Description |
670+
| ----------- | ------------ | ---------------------------------------- |
671+
| featureName | `EnumMember` | The feature to associate with the target |
672+
673+
#### `@featureOptions`
674+
675+
Decorator to define options for a specific feature
676+
677+
```typespec
678+
@Azure.ResourceManager.Legacy.featureOptions(options: valueof Azure.ResourceManager.Legacy.ArmFeatureOptions)
679+
```
680+
681+
##### Target
682+
683+
The enum member that represents the feature
684+
`EnumMember`
685+
686+
##### Parameters
687+
688+
| Name | Type | Description |
689+
| ------- | ------------------------------------------------- | --------------------------- |
690+
| options | [valueof `ArmFeatureOptions`](#armfeatureoptions) | The options for the feature |
691+
692+
#### `@features`
693+
694+
Decorator to define a set of features
695+
696+
```typespec
697+
@Azure.ResourceManager.Legacy.features(features: Enum)
698+
```
699+
700+
##### Target
701+
702+
The service namespace
703+
`Namespace`
704+
705+
##### Parameters
706+
707+
| Name | Type | Description |
708+
| -------- | ------ | ----------------------------------- |
709+
| features | `Enum` | The enum that contains the features |
710+
651711
#### `@renamePathParameter`
652712

653713
Renames a path parameter in an Azure Resource Manager operation.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export type RenamePathParameterDecorator = (
8484
/**
8585
* Decorator to define a set of features
8686
*
87-
* @param target The enum that contains the features
87+
* @param target The service namespace
88+
* @param features The enum that contains the features
8889
*/
8990
export type FeaturesDecorator = (
9091
context: DecoratorContext,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ model ArmFeatureOptions {
7474

7575
/**
7676
* Decorator to define a set of features
77-
* @param target The enum that contains the features
77+
* @param target The service namespace
78+
* @param features The enum that contains the features
7879
*/
7980
extern dec features(target: Namespace, features: Enum);
8081

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,8 +1323,8 @@ export const $feature: FeatureDecorator = (
13231323
featureName: EnumMember,
13241324
) => {
13251325
const { program } = context;
1326-
const feature = (featureName.value ?? featureName.name) as string;
1327-
setResourceFeature(program, entity, feature);
1326+
const options = getFeatureOptions(program, featureName);
1327+
setResourceFeature(program, entity, options.featureName);
13281328
};
13291329

13301330
export const $features: FeaturesDecorator = (

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

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { getHttpOperation } from "@typespec/http";
44
import { ok, strictEqual } from "assert";
55
import { describe, expect, it } from "vitest";
66
import { ArmLifecycleOperationKind } from "../src/operations.js";
7-
import { ArmResourceDetails, getArmResources } from "../src/resource.js";
7+
import {
8+
ArmResourceDetails,
9+
getArmResources,
10+
getResourceFeature,
11+
getResourceFeatureSet,
12+
} from "../src/resource.js";
813
import { Tester } from "./tester.js";
914

1015
function assertLifecycleOperation(
@@ -459,6 +464,122 @@ describe("ARM resource model:", () => {
459464
strictEqual((armIdProp?.type as Model).name, "armResourceIdentifier");
460465
});
461466
});
467+
describe("features support", () => {
468+
it("sets standard features and feature options", async () => {
469+
const [result, diagnostics] = await Tester.compileAndDiagnose(t.code`
470+
471+
@Azure.ResourceManager.Legacy.features(Features)
472+
@versioned(Versions)
473+
@armProviderNamespace("Microsoft.Test")
474+
namespace ${t.namespace("MSTest")};
475+
/** Contoso API versions */
476+
enum Versions {
477+
/** 2021-10-01-preview version */
478+
v2025_11_19_preview: "2025-11-19-preview",
479+
}
480+
enum Features {
481+
/** Feature A */
482+
FeatureA: "FeatureA",
483+
/** Feature B */
484+
FeatureB: "FeatureB",
485+
}
486+
@Azure.ResourceManager.Legacy.feature(Features.FeatureA)
487+
model ${t.model("FooResource")} is TrackedResource<FooResourceProperties> {
488+
...ResourceNameParameter<FooResource>;
489+
}
490+
model FooResourceProperties {
491+
...DefaultProvisioningStateProperty;
492+
}
493+
494+
@Azure.ResourceManager.Legacy.feature(Features.FeatureB)
495+
model ${t.model("BarResource")} is ProxyResource<BarResourceProperties> {
496+
...ResourceNameParameter<BarResource>;
497+
}
498+
model BarResourceProperties {
499+
...DefaultProvisioningStateProperty;
500+
}
501+
`);
502+
expectDiagnosticEmpty(diagnostics);
503+
const features = getResourceFeatureSet(result.program, result.MSTest);
504+
expect(features).toBeDefined();
505+
ok(features);
506+
const keys = Array.from(features.keys());
507+
expect(keys).toEqual(["FeatureA", "FeatureB"]);
508+
expect(features?.get("FeatureA")).toEqual({
509+
featureName: "FeatureA",
510+
fileName: "featureA",
511+
description: "",
512+
});
513+
expect(features?.get("FeatureB")).toEqual({
514+
featureName: "FeatureB",
515+
fileName: "featureB",
516+
description: "",
517+
});
518+
519+
const fooFeature = getResourceFeature(result.program, result.FooResource);
520+
expect(fooFeature).toMatch("FeatureA");
521+
const barFeature = getResourceFeature(result.program, result.BarResource);
522+
expect(barFeature).toMatch("FeatureB");
523+
});
524+
it("allows customizing features and feature options", async () => {
525+
const [result, diagnostics] = await Tester.compileAndDiagnose(t.code`
526+
527+
@Azure.ResourceManager.Legacy.features(Features)
528+
@versioned(Versions)
529+
@armProviderNamespace("Microsoft.Test")
530+
namespace ${t.namespace("MSTest")};
531+
/** Contoso API versions */
532+
enum Versions {
533+
/** 2021-10-01-preview version */
534+
v2025_11_19_preview: "2025-11-19-preview",
535+
}
536+
enum Features {
537+
/** Feature A */
538+
@Azure.ResourceManager.Legacy.featureOptions(#{featureName: "FeatureA", fileName: "feature-a", description: "The data for feature A"})
539+
FeatureA: "Feature A",
540+
/** Feature B */
541+
@Azure.ResourceManager.Legacy.featureOptions(#{featureName: "FeatureB", fileName: "feature-b", description: "The data for feature B"})
542+
FeatureB: "Feature B",
543+
}
544+
@Azure.ResourceManager.Legacy.feature(Features.FeatureA)
545+
model ${t.model("FooResource")} is TrackedResource<FooResourceProperties> {
546+
...ResourceNameParameter<FooResource>;
547+
}
548+
model FooResourceProperties {
549+
...DefaultProvisioningStateProperty;
550+
}
551+
552+
@Azure.ResourceManager.Legacy.feature(Features.FeatureB)
553+
model ${t.model("BarResource")} is ProxyResource<BarResourceProperties> {
554+
...ResourceNameParameter<BarResource>;
555+
}
556+
model BarResourceProperties {
557+
...DefaultProvisioningStateProperty;
558+
}
559+
`);
560+
expectDiagnosticEmpty(diagnostics);
561+
const features = getResourceFeatureSet(result.program, result.MSTest);
562+
expect(features).toBeDefined();
563+
ok(features);
564+
const keys = Array.from(features.keys());
565+
expect(keys).toEqual(["FeatureA", "FeatureB"]);
566+
expect(features?.get("FeatureA")).toEqual({
567+
featureName: "FeatureA",
568+
fileName: "feature-a",
569+
description: "The data for feature A",
570+
});
571+
expect(features?.get("FeatureB")).toEqual({
572+
featureName: "FeatureB",
573+
fileName: "feature-b",
574+
description: "The data for feature B",
575+
});
576+
577+
const fooFeature = getResourceFeature(result.program, result.FooResource);
578+
expect(fooFeature).toMatch("FeatureA");
579+
const barFeature = getResourceFeature(result.program, result.BarResource);
580+
expect(barFeature).toMatch("FeatureB");
581+
});
582+
});
462583
describe("network security perimeter", () => {
463584
it("raises diagnostic when network security perimeter is used on default common-types version", async () => {
464585
const diagnostics = await Tester.diagnose(`

website/src/content/docs/docs/libraries/azure-resource-manager/reference/data-types.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,6 +3406,22 @@ model Azure.ResourceManager.Foundations.TenantScope<Resource>
34063406

34073407
## Azure.ResourceManager.Legacy
34083408

3409+
### `ArmFeatureOptions` {#Azure.ResourceManager.Legacy.ArmFeatureOptions}
3410+
3411+
Options for defining a feature and the associated file
3412+
3413+
```typespec
3414+
model Azure.ResourceManager.Legacy.ArmFeatureOptions
3415+
```
3416+
3417+
#### Properties
3418+
3419+
| Name | Type | Description |
3420+
| ----------- | -------- | ----------------------------------------- |
3421+
| featureName | `string` | The feature name |
3422+
| fileName | `string` | The associated file name for the features |
3423+
| description | `string` | The feature description |
3424+
34093425
### `ArmOperationOptions` {#Azure.ResourceManager.Legacy.ArmOperationOptions}
34103426

34113427
Route options for an operation

website/src/content/docs/docs/libraries/azure-resource-manager/reference/decorators.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,63 @@ Specify an external reference that should be used when emitting this type.
559559
| ------- | ---------------- | ------------------------------------------------------------- |
560560
| jsonRef | `valueof string` | External reference(e.g. "../../common.json#/definitions/Foo") |
561561

562+
### `@feature` {#@Azure.ResourceManager.Legacy.feature}
563+
564+
Decorator to associate a feature with a model, interface, or namespace
565+
566+
```typespec
567+
@Azure.ResourceManager.Legacy.feature(featureName: EnumMember)
568+
```
569+
570+
#### Target
571+
572+
The target to associate the feature with
573+
`Model | Interface | Namespace`
574+
575+
#### Parameters
576+
577+
| Name | Type | Description |
578+
| ----------- | ------------ | ---------------------------------------- |
579+
| featureName | `EnumMember` | The feature to associate with the target |
580+
581+
### `@featureOptions` {#@Azure.ResourceManager.Legacy.featureOptions}
582+
583+
Decorator to define options for a specific feature
584+
585+
```typespec
586+
@Azure.ResourceManager.Legacy.featureOptions(options: valueof Azure.ResourceManager.Legacy.ArmFeatureOptions)
587+
```
588+
589+
#### Target
590+
591+
The enum member that represents the feature
592+
`EnumMember`
593+
594+
#### Parameters
595+
596+
| Name | Type | Description |
597+
| ------- | --------------------------------------------------------------------------------------------- | --------------------------- |
598+
| options | [valueof `ArmFeatureOptions`](./data-types.md#Azure.ResourceManager.Legacy.ArmFeatureOptions) | The options for the feature |
599+
600+
### `@features` {#@Azure.ResourceManager.Legacy.features}
601+
602+
Decorator to define a set of features
603+
604+
```typespec
605+
@Azure.ResourceManager.Legacy.features(features: Enum)
606+
```
607+
608+
#### Target
609+
610+
The service namespace
611+
`Namespace`
612+
613+
#### Parameters
614+
615+
| Name | Type | Description |
616+
| -------- | ------ | ----------------------------------- |
617+
| features | `Enum` | The enum that contains the features |
618+
562619
### `@renamePathParameter` {#@Azure.ResourceManager.Legacy.renamePathParameter}
563620

564621
Renames a path parameter in an Azure Resource Manager operation.

website/src/content/docs/docs/libraries/azure-resource-manager/reference/index.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ npm install --save-peer @azure-tools/typespec-azure-resource-manager
326326
- [`@armOperationRoute`](./decorators.md#@Azure.ResourceManager.Legacy.armOperationRoute)
327327
- [`@customAzureResource`](./decorators.md#@Azure.ResourceManager.Legacy.customAzureResource)
328328
- [`@externalTypeRef`](./decorators.md#@Azure.ResourceManager.Legacy.externalTypeRef)
329+
- [`@feature`](./decorators.md#@Azure.ResourceManager.Legacy.feature)
330+
- [`@featureOptions`](./decorators.md#@Azure.ResourceManager.Legacy.featureOptions)
331+
- [`@features`](./decorators.md#@Azure.ResourceManager.Legacy.features)
329332
- [`@renamePathParameter`](./decorators.md#@Azure.ResourceManager.Legacy.renamePathParameter)
330333

331334
### Interfaces
@@ -348,6 +351,7 @@ npm install --save-peer @azure-tools/typespec-azure-resource-manager
348351

349352
### Models
350353

354+
- [`ArmFeatureOptions`](./data-types.md#Azure.ResourceManager.Legacy.ArmFeatureOptions)
351355
- [`ArmOperationOptions`](./data-types.md#Azure.ResourceManager.Legacy.ArmOperationOptions)
352356
- [`CustomResourceOptions`](./data-types.md#Azure.ResourceManager.Legacy.CustomResourceOptions)
353357
- [`DiscriminatedExtensionResource`](./data-types.md#Azure.ResourceManager.Legacy.DiscriminatedExtensionResource)

0 commit comments

Comments
 (0)