Skip to content

Commit 1848edb

Browse files
authored
Handle modern scope properties: readableScopes, writableScopes & new AllExceptExtension enum (#2574)
* all green * unused ScopeType * nit * addressed comments, all green * fix failing pr build * updated schema baseline
1 parent 424dba7 commit 1848edb

File tree

11 files changed

+1306
-104
lines changed

11 files changed

+1306
-104
lines changed

src/autorest.bicep/src/resources.ts

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import { ChoiceSchema, CodeModel, ComplexSchema, HttpMethod, HttpParameter, Http
55
import { Channel, AutorestExtensionHost } from "@autorest/extension-base";
66
import { keys, Dictionary, values, groupBy, uniqBy, chain, flatten } from 'lodash';
77
import { success, failure, Result } from './utils';
8-
import { ScopeType } from "bicep-types";
8+
import { ScopeType, AllExceptExtension } from "bicep-types";
99

1010
export interface PutExample {
1111
description: string;
1212
body: Record<string, unknown>;
1313
}
1414

1515
export interface ResourceDescriptor {
16-
scopeType: ScopeType;
1716
namespace: string;
1817
typeSegments: string[];
1918
apiVersion: string;
19+
readableScopes: ScopeType;
20+
writableScopes: ScopeType;
2021
constantName?: string;
21-
readonlyScopes?: ScopeType;
2222
}
2323

2424
export interface ProviderDefinition {
@@ -639,12 +639,12 @@ export function getProviderDefinitions(codeModel: CodeModel, host: AutorestExten
639639
const constantName = isPathVariable(resNameParam) ? undefined : resNameParam;
640640

641641
const descriptors: ResourceDescriptor[] = parseResult.value.map(type => ({
642-
scopeType,
643642
namespace,
644643
typeSegments: type,
645644
apiVersion,
645+
readableScopes: readable ? scopeType : ScopeType.None,
646+
writableScopes: writable ? scopeType : ScopeType.None,
646647
constantName,
647-
readonlyScopes: readable && !writable ? scopeType : undefined,
648648
}));
649649

650650
return success(descriptors);
@@ -754,37 +754,8 @@ export function getProviderDefinitions(codeModel: CodeModel, host: AutorestExten
754754
return ScopeType.Extension;
755755
}
756756

757-
// ambiguous - without any further information, we have to assume 'all'
758-
return ScopeType.Unknown;
759-
}
760-
761-
function mergeScopes(scopeA: ScopeType, scopeB: ScopeType) {
762-
// We have to assume any (unknown) scope if either scope is unknown
763-
// Bitwise OR will not handle this case correctly as 'unknown' is 0.
764-
if (scopeA == ScopeType.Unknown || scopeB == ScopeType.Unknown) {
765-
return ScopeType.Unknown;
766-
}
767-
768-
return scopeA | scopeB;
769-
}
770-
771-
function mergeReadonlyScopes(
772-
currentScopes: ScopeType,
773-
currentReadonlyScopes: ScopeType|undefined,
774-
newScopes: ScopeType,
775-
newReadonlyScopes: ScopeType|undefined
776-
) {
777-
function writableScopes(scopes: ScopeType, readonlyScopes: ScopeType|undefined) {
778-
return readonlyScopes !== undefined ? scopes ^ readonlyScopes : scopes;
779-
}
780-
const mergedScopes = mergeScopes(currentScopes, newScopes);
781-
if (mergedScopes === ScopeType.Unknown) {
782-
const writingPermittedSomewhere = currentScopes !== currentReadonlyScopes || newScopes !== newReadonlyScopes;
783-
return writingPermittedSomewhere ? undefined : ScopeType.Unknown;
784-
}
785-
786-
const mergedWritableScopes = writableScopes(currentScopes, currentReadonlyScopes) | writableScopes(newScopes, newReadonlyScopes);
787-
return mergedScopes === mergedWritableScopes ? undefined : mergedScopes ^ mergedWritableScopes;
757+
// ambiguous - fallback to all standard scopes except extension
758+
return AllExceptExtension;
788759
}
789760

790761
function collapseDefinitionScopes(resources: ResourceDefinition[]) {
@@ -799,8 +770,8 @@ export function getProviderDefinitions(codeModel: CodeModel, host: AutorestExten
799770
...definitionsByName[name],
800771
descriptor: {
801772
...curDescriptor,
802-
scopeType: mergeScopes(curDescriptor.scopeType, newDescriptor.scopeType),
803-
readonlyScopes: mergeReadonlyScopes(curDescriptor.scopeType, curDescriptor.readonlyScopes, newDescriptor.scopeType, newDescriptor.readonlyScopes),
773+
readableScopes: curDescriptor.readableScopes | newDescriptor.readableScopes,
774+
writableScopes: curDescriptor.writableScopes | newDescriptor.writableScopes,
804775
},
805776
};
806777
} else {
@@ -846,19 +817,19 @@ export function getProviderDefinitions(codeModel: CodeModel, host: AutorestExten
846817
hasComparableSchemata(atPath, d => d.putOperation?.requestSchema) &&
847818
hasComparableSchemata(atPath, d => d.getOperation?.responseSchema)
848819
) {
849-
let scopeType = atPath[0].descriptor.scopeType;
850-
let readonlyScopes = atPath[0].descriptor.readonlyScopes;
851-
for (let i = 1; i < atPath.length; i++) {
852-
const {scopeType: newScopes, readonlyScopes: newReadonlyScopes} = atPath[i].descriptor;
853-
scopeType = mergeScopes(scopeType, newScopes);
854-
readonlyScopes = mergeReadonlyScopes(scopeType, readonlyScopes, newScopes, newReadonlyScopes);
820+
// Merge all scopes from different constant name variants
821+
let readableScopes = ScopeType.None;
822+
let writableScopes = ScopeType.None;
823+
for (const resource of atPath) {
824+
readableScopes |= resource.descriptor.readableScopes;
825+
writableScopes |= resource.descriptor.writableScopes;
855826
}
856827

857828
definitionsByNormalizedPath[path] = [{
858829
descriptor: {
859830
...parameterized[0],
860-
scopeType,
861-
readonlyScopes,
831+
readableScopes,
832+
writableScopes,
862833
},
863834
putOperation: chain(atPath).map(r => r.putOperation).find().value(),
864835
getOperation: chain(atPath).map(r => r.getOperation).find().value(),

src/autorest.bicep/src/schema-generator.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -663,22 +663,25 @@ export function generateSchema(host: AutorestExtensionHost, definition: Provider
663663
generated[fullyQualifiedType.toLowerCase()] = schema;
664664
const definitionName = descriptor.typeSegments.join('_');
665665

666-
if (ScopeType.Tenant === (descriptor.scopeType & ScopeType.Tenant)) {
666+
// Use modern writableScopes only
667+
const writableScopes = descriptor.writableScopes;
668+
669+
if (writableScopes & ScopeType.Tenant) {
667670
schemaData.tenantResources[definitionName] = schema;
668671
}
669-
if (ScopeType.ManagementGroup === (descriptor.scopeType & ScopeType.ManagementGroup)) {
672+
if (writableScopes & ScopeType.ManagementGroup) {
670673
schemaData.mgResources[definitionName] = schema;
671674
}
672-
if (ScopeType.Subscription === (descriptor.scopeType & ScopeType.Subscription)) {
675+
if (writableScopes & ScopeType.Subscription) {
673676
schemaData.subResources[definitionName] = schema;
674677
}
675-
if (ScopeType.ResourceGroup === (descriptor.scopeType & ScopeType.ResourceGroup)) {
678+
if (writableScopes & ScopeType.ResourceGroup) {
676679
schemaData.rgResources[definitionName] = schema;
677680
}
678-
if (ScopeType.Extension === (descriptor.scopeType & ScopeType.Extension)) {
681+
if (writableScopes & ScopeType.Extension) {
679682
schemaData.extensionResources[definitionName] = schema;
680683
}
681-
if (descriptor.scopeType === ScopeType.Unknown) {
684+
if (writableScopes === ScopeType.None) {
682685
schemaData.unknownResources[definitionName] = schema;
683686
}
684687

src/autorest.bicep/src/type-generator.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
UriSchema,
2222
} from "@autorest/codemodel";
2323
import { Channel, AutorestExtensionHost } from "@autorest/extension-base";
24-
import { DiscriminatedObjectType, ObjectTypeProperty, ObjectTypePropertyFlags, ResourceFlags, TypeBaseKind, TypeFactory, TypeReference } from "bicep-types";
24+
import { DiscriminatedObjectType, ObjectTypeProperty, ObjectTypePropertyFlags, TypeBaseKind, TypeFactory, TypeReference } from "bicep-types";
2525
import { uniq, keys, Dictionary, chain } from 'lodash';
2626
import { getFullyQualifiedType, getNameSchema, getSerializedName, NameSchema, ProviderDefinition, ResourceDefinition, ResourceDescriptor, ResourceOperationDefintion } from "./resources";
2727
import { failure, success } from "./utils";
@@ -160,6 +160,13 @@ export function generateTypes(host: AutorestExtensionHost, definition: ProviderD
160160
return resourceDefinition;
161161
}
162162

163+
function getScopesFromDefinition(definition: ResourceDefinition) {
164+
return {
165+
readableScopes: definition.descriptor.readableScopes,
166+
writableScopes: definition.descriptor.writableScopes
167+
};
168+
}
169+
163170
function processResource(fullyQualifiedType: string, definitions: ResourceDefinition[]) {
164171
if (definitions.length > 1) {
165172
for (const definition of definitions) {
@@ -220,17 +227,13 @@ export function generateTypes(host: AutorestExtensionHost, definition: ProviderD
220227
}
221228

222229
const { descriptor, bodyType } = output;
223-
let flags = ResourceFlags.None;
224-
if (descriptor.readonlyScopes === descriptor.scopeType) {
225-
flags |= ResourceFlags.ReadOnly;
226-
}
227-
230+
const { readableScopes, writableScopes } = getScopesFromDefinition(definitions[0]);
228231
factory.addResourceType(
229232
`${getFullyQualifiedType(descriptor)}@${descriptor.apiVersion}`,
230-
descriptor.scopeType,
231-
descriptor.readonlyScopes !== descriptor.scopeType ? descriptor.readonlyScopes : undefined,
232233
bodyType,
233-
flags);
234+
readableScopes,
235+
writableScopes
236+
);
234237
}
235238

236239
for (const action of resourceActions) {

0 commit comments

Comments
 (0)