private static void CheckType(Type specType, string expectedType, JsonSchema schema, JsonSchema property, ImplementationProperty implementationProperty, TypeValidationResult result, Func <Type, bool> typeCheck ) { var propertyType = implementationProperty.PropertyType; var nullable = !propertyType.IsValueType; if (IsNullableType(propertyType)) { nullable = true; propertyType = Nullable.GetUnderlyingType(implementationProperty.PropertyType); } if (!typeCheck(propertyType)) { result.AddError(TypeValidationError.ExpectedType(specType, propertyType, expectedType, schema.GetNameOrSpecificationId(), property.GetNameOrSpecificationId())); } if (result.Validation == Validation.SpecToType && property.Type.HasFlag(JsonObjectType.Null) && !nullable) { result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), property.GetNameOrSpecificationId(), "expected type to be nullable")); } }
private static void ValidateEnum(Type specType, JsonSchema schema, JsonSchema schemaProperty, ImplementationProperty specTypeProperty, TypeValidationResult result ) { var enumValues = GetEnumValues(specTypeProperty.PropertyType); foreach (var enumValue in enumValues) { if (!schemaProperty.Enumeration.Cast <string>().Contains(enumValue)) { result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), schemaProperty.GetNameOrSpecificationId(), $"enum did not contain value {enumValue}")); } } }
/// <summary> /// Get the properties for the given specification type, using the APM agent's serialization components. /// </summary> /// <remarks> /// Encapsulates the APM agent implementation for how specification constraints are enforced. This currently /// uses Json.NET for serialized property names and constraining max length. /// </remarks> /// <param name="specType">the specification type</param> /// <returns></returns> private static ImplementationProperty[] GetProperties(Type specType) { var resolver = new ElasticApmContractResolver(); JsonObjectContract contract; try { // the json schema may indicate a type is an "object", but the agent may model it in some other way // e.g. samples on metricset is modelled as a collection. In these scenarios, we won't be dealing with // an object contract and won't be able to statically determine validity of the type to the schema through reflection. // The only way to validate these against the schema is to serialize the types. contract = (JsonObjectContract)resolver.ResolveContract(specType); } catch (InvalidCastException e) { throw new ContractResolveException(e.Message); } var specProperties = new List <ImplementationProperty>(contract.Properties.Count); foreach (var jsonProperty in contract.Properties) { if (jsonProperty.Ignored) { continue; } var implementationProperty = new ImplementationProperty(jsonProperty.PropertyName, jsonProperty.PropertyType, specType); var maxLength = (MaxLengthAttribute)(jsonProperty.AttributeProvider.GetAttributes(typeof(MaxLengthAttribute), true).FirstOrDefault()); if (maxLength != null) { implementationProperty.MaxLength = maxLength.Length; } specProperties.Add(implementationProperty); } return(specProperties.ToArray()); }
private static void CheckMaxLength(Type specType, JsonSchema schema, JsonSchema schemaProperty, ImplementationProperty implementationProperty, TypeValidationResult result ) { if (schemaProperty.MaxLength.HasValue) { var maxLength = schemaProperty.MaxLength.Value; if (!implementationProperty.MaxLength.HasValue) { result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), implementationProperty.Name, $"expected property to enforce maxLength of {maxLength} but does not")); } else if (implementationProperty.MaxLength != maxLength) { result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), implementationProperty.Name, $"property max length {implementationProperty.MaxLength} not equal to spec maxLength {maxLength}")); } } }
private static void ValidateNumber(Type specType, JsonSchema schema, JsonSchema property, ImplementationProperty specTypeProperty, TypeValidationResult result ) => CheckType(specType, "number", schema, property, specTypeProperty, result, t => NumericTypeNames.Contains(t.FullName));
private static void ValidateInteger(Type specType, JsonSchema schema, JsonSchema property, ImplementationProperty specTypeProperty, TypeValidationResult result ) => CheckType(specType, "integer", schema, property, specTypeProperty, result, t => t == typeof(int) || t == typeof(long));
private static void ValidateBoolean(Type specType, JsonSchema schema, JsonSchema property, ImplementationProperty specTypeProperty, TypeValidationResult result ) => CheckType(specType, "boolean", schema, property, specTypeProperty, result, t => t == typeof(bool));
private static void ValidateString(Type specType, JsonSchema schema, JsonSchema property, ImplementationProperty specTypeProperty, TypeValidationResult result ) { CheckType(specType, "string", schema, property, specTypeProperty, result, t => t == typeof(string)); CheckMaxLength(specType, schema, property, specTypeProperty, result); }