コード例 #1
0
        public void GetResourceBodyCompletionSnippets_WithDiscriminatedObjectTypeAndNoRequiredProperties_ShouldReturnEmptySnippet()
        {
            SnippetsProvider snippetsProvider = new SnippetsProvider();

            var objectTypeA = new ObjectType("objA", TypeSymbolValidationFlags.Default, new[]
            {
                new TypeProperty("discKey", new StringLiteralType("keyA")),
                new TypeProperty("keyAProp", LanguageConstants.String),
            }, null);

            var objectTypeB = new ObjectType("objB", TypeSymbolValidationFlags.Default, new[]
            {
                new TypeProperty("discKey", new StringLiteralType("keyB")),
                new TypeProperty("keyBProp", LanguageConstants.String),
            }, null);

            var discriminatedObjectType = new DiscriminatedObjectType("discObj", TypeSymbolValidationFlags.Default, "discKey", new[] { objectTypeA, objectTypeB });

            TypeSymbol typeSymbol = new ResourceType(
                ResourceTypeReference.Parse("microsoft.aadiam/azureADMetrics@2020-07-01-preview"),
                ResourceScope.ResourceGroup,
                discriminatedObjectType);

            IEnumerable <Snippet> snippets = snippetsProvider.GetResourceBodyCompletionSnippets(typeSymbol, false);

            snippets.Should().SatisfyRespectively(
                x =>
            {
                x.Prefix.Should().Be("{}");
                x.Detail.Should().Be("{}");
                x.CompletionPriority.Should().Be(CompletionPriority.Medium);
                x.Text.Should().Be("{\n\t$0\n}");
            });
        }
コード例 #2
0
        private static ResourceTypeComponents DiscriminatedPropertiesTestsType()
        {
            var resourceType = ResourceTypeReference.Parse("Test.Rp/discriminatedPropertiesTests@2020-01-01");

            var propsA = new ObjectType(
                "PropertiesA",
                TypeSymbolValidationFlags.WarnOnTypeMismatch,
                new[] {
                new TypeProperty("propType", new StringLiteralType("PropertiesA"), TypePropertyFlags.None, "..."),
                new TypeProperty("propA", LanguageConstants.String, TypePropertyFlags.None, "This is the description for propA!"),
            },
                null);

            var propsB = new ObjectType(
                "PropertiesB",
                TypeSymbolValidationFlags.WarnOnTypeMismatch,
                new[] {
                new TypeProperty("propType", new StringLiteralType("PropertiesB"), TypePropertyFlags.None, "..."),
                new TypeProperty("propB", LanguageConstants.String, TypePropertyFlags.None, "This is the description for propB!"),
            },
                null);

            var propertiesType = new DiscriminatedObjectType(
                "properties",
                TypeSymbolValidationFlags.Default,
                "propType",
                new[] { propsA, propsB });

            return(new ResourceTypeComponents(resourceType, ResourceScope.ResourceGroup, new ObjectType(resourceType.FormatName(), TypeSymbolValidationFlags.Default,
                                                                                                        GetCommonResourceProperties(resourceType).Concat(new[] {
                new TypeProperty("properties", propertiesType, TypePropertyFlags.Required, "properties property"),
            }), null)));
        }
コード例 #3
0
        private void HandlePolymorphicType(DiscriminatedObjectType discriminatedObjectType, CompositeType putType, CompositeType getType)
        {
            var putSubTypes = (putType != null ? codeModel.ModelTypes.Where(type => type.BaseModelType == putType) : Enumerable.Empty <CompositeType>())
                              .ToDictionary(x => x.SerializedName);
            var getSubTypes = (getType != null ? codeModel.ModelTypes.Where(type => type.BaseModelType == getType) : Enumerable.Empty <CompositeType>())
                              .ToDictionary(x => x.SerializedName);

            foreach (var subTypeName in putSubTypes.Keys.Concat(getSubTypes.Keys.Where(x => !putSubTypes.ContainsKey(x))))
            {
                putSubTypes.TryGetValue(subTypeName, out var putSubType);
                getSubTypes.TryGetValue(subTypeName, out var getSubType);

                // Sub-types are never referenced directly in the Swagger
                // discriminator scenario, so they wouldn't be added to the
                // produced resource schema. By calling ParseCompositeType() on the
                // sub-type we add the sub-type to the resource schema.
                var polymorphicType = ParseCompositeType(putSubType, getSubType, false);

                if (namedDefinitions[subTypeName] is ObjectType objectType)
                {
                    var discriminatorEnum = GetDiscriminatorType(putSubType, getSubType);
                    objectType.Properties[discriminatedObjectType.Discriminator] = new ObjectProperty
                    {
                        Type  = factory.GetReference(discriminatorEnum),
                        Flags = ObjectPropertyFlags.Required,
                    };
                }

                discriminatedObjectType.Elements[subTypeName] = factory.GetReference(polymorphicType);
            }
        }
コード例 #4
0
        public static ResourceType SetBicepResourceProperties(ResourceType resourceType, bool isExistingResource)
        {
            var bodyType = resourceType.Body.Type;

            switch (bodyType)
            {
            case ObjectType bodyObjectType:
                bodyType = SetBicepResourceProperties(bodyObjectType, resourceType.ValidParentScopes, isExistingResource);
                break;

            case DiscriminatedObjectType bodyDiscriminatedType:
                var bodyTypes = bodyDiscriminatedType.UnionMembersByKey.Values.ToList()
                                .Select(x => x.Type as ObjectType ?? throw new ArgumentException($"Resource {resourceType.Name} has unexpected body type {bodyType.GetType()}"));
                bodyTypes = bodyTypes.Select(x => SetBicepResourceProperties(x, resourceType.ValidParentScopes, isExistingResource));
                bodyType  = new DiscriminatedObjectType(
                    bodyDiscriminatedType.Name,
                    bodyDiscriminatedType.ValidationFlags,
                    bodyDiscriminatedType.DiscriminatorKey,
                    bodyTypes);
                break;

            default:
                // we exhaustively test deserialization of every resource type during CI, and this happens in a deterministic fashion,
                // so this exception should never occur in the released product
                throw new ArgumentException($"Resource {resourceType.Name} has unexpected body type {bodyType.GetType()}");
            }

            return(new ResourceType(resourceType.TypeReference, resourceType.ValidParentScopes, bodyType));
        }
コード例 #5
0
        public void GetResourceBodyCompletionSnippets_WithDiscriminatedObjectTypeAndRequiredProperties_ShouldReturnRequiredPropertiesSnippet()
        {
            SnippetsProvider snippetsProvider = new SnippetsProvider();

            var objectTypeA = new ObjectType("objA", TypeSymbolValidationFlags.Default, new[]
            {
                new TypeProperty("discKey", new StringLiteralType("keyA")),
                new TypeProperty("name", new StringLiteralType("keyA"), TypePropertyFlags.Required),
                new TypeProperty("location", LanguageConstants.String, TypePropertyFlags.Required),
                new TypeProperty("id", LanguageConstants.String)
            }, null);

            var objectTypeB = new ObjectType("objB", TypeSymbolValidationFlags.Default, new[]
            {
                new TypeProperty("discKey", new StringLiteralType("keyB")),
                new TypeProperty("name", LanguageConstants.String, TypePropertyFlags.Required),
                new TypeProperty("kind", new StringLiteralType("discKey"), TypePropertyFlags.ReadOnly),
                new TypeProperty("hostPoolType", LanguageConstants.String)
            }, null);

            var discriminatedObjectType = new DiscriminatedObjectType("discObj", TypeSymbolValidationFlags.Default, "discKey", new[] { objectTypeA, objectTypeB });

            TypeSymbol typeSymbol = new ResourceType(
                ResourceTypeReference.Parse("microsoft.aadiam/azureADMetrics@2020-07-01-preview"),
                ResourceScope.ResourceGroup,
                discriminatedObjectType);

            IEnumerable <Snippet> snippets = snippetsProvider.GetResourceBodyCompletionSnippets(typeSymbol, false);

            snippets.Should().SatisfyRespectively(
                x =>
            {
                x.Prefix.Should().Be("{}");
                x.Detail.Should().Be("{}");
                x.CompletionPriority.Should().Be(CompletionPriority.Medium);
                x.Text.Should().Be("{\n\t$0\n}");
            },
                x =>
            {
                x.Prefix.Should().Be("required-properties-keyA");
                x.Detail.Should().Be("Required properties");
                x.CompletionPriority.Should().Be(CompletionPriority.Medium);
                x.Text.Should().BeEquivalentToIgnoringNewlines(@"{
	name: 'keyA'
	location: $1
	$0
}");
            },
                x =>
            {
                x.Prefix.Should().Be("required-properties-keyB");
                x.Detail.Should().Be("Required properties");
                x.CompletionPriority.Should().Be(CompletionPriority.Medium);
                x.Text.Should().BeEquivalentToIgnoringNewlines(@"{
	name: $1
	$0
}");
            });
        }
コード例 #6
0
        public override void Write(Utf8JsonWriter writer, TypeBase value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            var propertyEnum = value switch {
                BuiltInType _ => TypeBaseKind.BuiltInType,
                ObjectType _ => TypeBaseKind.ObjectType,
                ArrayType _ => TypeBaseKind.ArrayType,
                ResourceType _ => TypeBaseKind.ResourceType,
                UnionType _ => TypeBaseKind.UnionType,
                StringLiteralType _ => TypeBaseKind.StringLiteralType,
                DiscriminatedObjectType _ => TypeBaseKind.DiscriminatedObjectType,
                ResourceFunctionType _ => TypeBaseKind.ResourceFunctionType,
                _ => throw new JsonException(),
            };

            writer.WritePropertyName(((int)propertyEnum).ToString());

            switch (value)
            {
            case BuiltInType builtInType:
                JsonSerializer.Serialize(writer, builtInType, serializerOptions);
                break;

            case ObjectType objectType:
                JsonSerializer.Serialize(writer, objectType, serializerOptions);
                break;

            case ArrayType arrayType:
                JsonSerializer.Serialize(writer, arrayType, serializerOptions);
                break;

            case ResourceType resourceType:
                JsonSerializer.Serialize(writer, resourceType, serializerOptions);
                break;

            case UnionType unionType:
                JsonSerializer.Serialize(writer, unionType, serializerOptions);
                break;

            case StringLiteralType stringLiteralType:
                JsonSerializer.Serialize(writer, stringLiteralType, serializerOptions);
                break;

            case DiscriminatedObjectType discriminatedObjectType:
                JsonSerializer.Serialize(writer, discriminatedObjectType, serializerOptions);
                break;

            case ResourceFunctionType resourceFunctionType:
                JsonSerializer.Serialize(writer, resourceFunctionType, serializerOptions);
                break;

            default:
                throw new JsonException();
            }

            writer.WriteEndObject();
        }
コード例 #7
0
        private void WriteKeywordNestedFunctionDefinitions(DiscriminatedObjectType discriminatedType, string discriminatorName)
        {
            // Define the body and discriminator parameters
            // Define all the common base properties as functions
            bool needNewline = false;

            foreach (KeyValuePair <string, ObjectProperty> baseProperty in discriminatedType.BaseProperties)
            {
                if (needNewline)
                {
                    _writer.WriteLine();
                }

                WriteKeywordDefinition(baseProperty.Key, baseProperty.Value.Type.Type);

                needNewline = true;
            }

            WriteDiscriminatedKeywordDefinitions(discriminatorName, discriminatedType.Discriminator, (IReadOnlyDictionary <string, ITypeReference>)discriminatedType.Elements);
        }
コード例 #8
0
        public void DiscriminatedObjectType_should_be_correctly_instantiated()
        {
            var objectA = new ObjectType("objA", TypeSymbolValidationFlags.Default, new []
            {
                new TypeProperty("discKey", new StringLiteralType("keyA")),
                new TypeProperty("keyAProp", LanguageConstants.String),
            }, null);

            var objectB = new ObjectType("objB", TypeSymbolValidationFlags.Default, new []
            {
                new TypeProperty("discKey", new StringLiteralType("keyB")),
                new TypeProperty("keyBProp", LanguageConstants.String),
            }, null);

            var discObj = new DiscriminatedObjectType("discObj", TypeSymbolValidationFlags.Default, "discKey", new [] { objectA, objectB });

            discObj.UnionMembersByKey.Keys.Should().BeEquivalentTo("'keyA'", "'keyB'");
            discObj.TypeKind.Should().Be(TypeKind.DiscriminatedObject);

            discObj.UnionMembersByKey[new StringLiteralType("keyA").Name].Type.Should().Be(objectA);
            discObj.UnionMembersByKey[new StringLiteralType("keyB").Name].Type.Should().Be(objectB);
        }
コード例 #9
0
        private void WriteKeywordDefinitionBody(string armKey, DiscriminatedObjectType discriminatedType, int arrayDepth)
        {
            // For discriminated objects, we assume we basically have a base object
            // and a list of conceptual "subclasses" each with an associated discriminator.
            // We solve this as follows:
            //  - Add the "discriminator" as a mandatory enum parameter
            //  - Define the functions for the base properties
            //  - Conditionally define the subclass properties
            //  - Invoke the call as normal

            string discriminatorName = SanitizeStringForVariableName(discriminatedType.Discriminator);

            WriteKeywordBodyParamBlock(discriminatorName, (IReadOnlyCollection <string>)discriminatedType.Elements.Keys);

            WriteKeywordNestedFunctionDefinitions(discriminatedType, discriminatorName);

            WritePrimitiveInvocation(
                armKey,
                KeywordArgumentKind.Body,
                arrayDepth,
                discriminatorKey: discriminatedType.Discriminator,
                discriminatorVariable: discriminatorName);
        }
コード例 #10
0
        private static ResourceTypeComponents DiscriminatorTestsType()
        {
            var resourceType = ResourceTypeReference.Parse("Test.Rp/discriminatorTests@2020-01-01");

            var bodyAProps = new ObjectType(
                "BodyAProperties",
                TypeSymbolValidationFlags.WarnOnTypeMismatch,
                new[] {
                new TypeProperty("propA", LanguageConstants.String, TypePropertyFlags.None, "This is the description for propA!"),
            },
                null);

            var bodyBProps = new ObjectType(
                "BodyBProperties",
                TypeSymbolValidationFlags.WarnOnTypeMismatch,
                new[] {
                new TypeProperty("propB", LanguageConstants.String, TypePropertyFlags.None, "This is the description for propB!"),
            },
                null);

            var bodyType = new DiscriminatedObjectType(
                resourceType.FormatName(),
                TypeSymbolValidationFlags.Default,
                "kind",
                new[] {
                new ObjectType("BodyA", TypeSymbolValidationFlags.Default, GetCommonResourceProperties(resourceType).Concat(new [] {
                    new TypeProperty("kind", new StringLiteralType("BodyA"), TypePropertyFlags.None, "This is the kind of body A"),
                    new TypeProperty("properties", bodyAProps, TypePropertyFlags.None, "These are the properties for body A"),
                }), null),
                new ObjectType("BodyB", TypeSymbolValidationFlags.Default, GetCommonResourceProperties(resourceType).Concat(new [] {
                    new TypeProperty("kind", new StringLiteralType("BodyB"), TypePropertyFlags.None, "This is the kind of body B"),
                    new TypeProperty("properties", bodyBProps, TypePropertyFlags.None, "These are the properties for body B"),
                }), null),
            });

            return(new ResourceTypeComponents(resourceType, ResourceScope.ResourceGroup, bodyType));
        }
コード例 #11
0
ファイル: SemanticModel.cs プロジェクト: wpouseele/bicep
            static PropertySymbol?GetPropertySymbol(TypeSymbol?baseType, string property)
            {
                if (baseType is null)
                {
                    return(null);
                }

                var typeProperty = TypeAssignmentVisitor.UnwrapType(baseType) switch {
                    ObjectType x => x.Properties.TryGetValue(property, out var tp) ? tp : null,
                    DiscriminatedObjectType x => x.TryGetDiscriminatorProperty(property),
                    _ => null
                };

                if (typeProperty is null)
                {
                    return(null);
                }

                return(new PropertySymbol(property, typeProperty.Description, typeProperty.TypeReference.Type));
            }

            switch (syntax)
            {
            case InstanceFunctionCallSyntax ifc:
            {
                var baseType = GetTypeInfo(ifc.BaseExpression);
                switch (baseType)
                {
                case NamespaceType namespaceType when SyntaxTree.Hierarchy.GetParent(ifc) is DecoratorSyntax:
                    return(namespaceType.DecoratorResolver.TryGetSymbol(ifc.Name));

                case ObjectType objectType:
                    return(objectType.MethodResolver.TryGetSymbol(ifc.Name));
                }

                return(null);
            }

            case PropertyAccessSyntax propertyAccess:
            {
                var baseType = GetDeclaredType(propertyAccess.BaseExpression);
                var property = propertyAccess.PropertyName.IdentifierName;

                return(GetPropertySymbol(baseType, property));
            }

            case ObjectPropertySyntax objectProperty:
            {
                if (Binder.GetParent(objectProperty) is not {
                    } parentSyntax)
                {
                    return(null);
                }

                var baseType = GetDeclaredType(parentSyntax);
                if (objectProperty.TryGetKeyText() is not {
                    } property)
                {
                    return(null);
                }

                return(GetPropertySymbol(baseType, property));
            }
            }

            return(this.Binder.GetSymbolInfo(syntax));
        }
コード例 #12
0
        public void DiscriminatedObjectType_raises_appropriate_diagnostics_for_matches()
        {
            var discriminatedType = new DiscriminatedObjectType(
                "discObj",
                "myDiscriminator",
                new []
            {
                new NamedObjectType("typeA", new []
                {
                    new TypeProperty("myDiscriminator", new StringLiteralType("valA")),
                    new TypeProperty("fieldA", LanguageConstants.Any, TypePropertyFlags.Required),
                }, null),
                new NamedObjectType("typeB", new []
                {
                    new TypeProperty("myDiscriminator", new StringLiteralType("valB")),
                    new TypeProperty("fieldB", LanguageConstants.Any, TypePropertyFlags.Required),
                }, null),
            });

            // no discriminator field supplied
            var obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("fieldA", TestSyntaxFactory.CreateString("someVal")),
            });

            var errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, discriminatedType);

            errors.Should().SatisfyRespectively(
                x => {
                x.Message.Should().Be("The property 'myDiscriminator' requires a value of type 'valA' | 'valB', but none was supplied.");
            });

            // incorrect type specified for the discriminator field
            obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("myDiscriminator", TestSyntaxFactory.CreateObject(Enumerable.Empty <ObjectPropertySyntax>())),
                TestSyntaxFactory.CreateProperty("fieldB", TestSyntaxFactory.CreateString("someVal")),
            });

            errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, discriminatedType);
            errors.Should().SatisfyRespectively(
                x => {
                x.Message.Should().Be("The property 'myDiscriminator' expected a value of type 'valA' | 'valB' but the provided value is of type object.");
            });

            // discriminator value that matches neither option supplied
            obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("myDiscriminator", TestSyntaxFactory.CreateString("valC")),
            });

            errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, discriminatedType);
            errors.Should().SatisfyRespectively(
                x => {
                x.Message.Should().Be("The property 'myDiscriminator' expected a value of type 'valA' | 'valB' but the provided value is of type 'valC'.");
            });

            // missing required property for the 'valB' branch
            obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("myDiscriminator", TestSyntaxFactory.CreateString("valB")),
            });

            errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, discriminatedType);
            errors.Should().SatisfyRespectively(
                x => {
                x.Message.Should().Be("The specified object is missing the following required properties: fieldB.");
            });

            // supplied the required property for the 'valB' branch
            obj = TestSyntaxFactory.CreateObject(new []
            {
                TestSyntaxFactory.CreateProperty("myDiscriminator", TestSyntaxFactory.CreateString("valB")),
                TestSyntaxFactory.CreateProperty("fieldB", TestSyntaxFactory.CreateString("someVal")),
            });

            errors = TypeValidator.GetExpressionAssignmentDiagnostics(CreateTypeManager(), obj, discriminatedType);
            errors.Should().BeEmpty();
        }
コード例 #13
0
 protected abstract TResult VisitDiscriminatedObject(DiscriminatedObjectType armDiscriminatedObject);
コード例 #14
0
 protected override DslKeywordSchema VisitDiscriminatedObject(DiscriminatedObjectType armDiscriminatedObject)
 {
     return(new BicepDiscriminatedObjectKeywordSchema(armDiscriminatedObject));
 }
コード例 #15
0
 protected override IReadOnlyDictionary <string, DslParameterInfo> VisitDiscriminatedObject(DiscriminatedObjectType armDiscriminatedObject)
 {
     return(s_bodyParameters);
 }