Пример #1
0
 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);
 }
Пример #2
0
        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"));
            }
        }
Пример #3
0
        private async Task <TypeValidationResult> ValidateAsync(Type type, string specificationId, Validation validation)
        {
            var schema = await LoadSchemaAsync(specificationId).ConfigureAwait(false);

            var result = new TypeValidationResult(type, specificationId, validation);

            ValidateProperties(type, schema, result);

            foreach (var inheritedSchema in schema.AllInheritedSchemas)
            {
                ValidateProperties(type, inheritedSchema, result);
            }

            return(result);
        }
Пример #4
0
 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}"));
         }
     }
 }
Пример #5
0
 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));
Пример #6
0
 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));
Пример #7
0
 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));
Пример #8
0
        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}"));
                }
            }
        }
Пример #9
0
        private static void ValidateProperties(Type specType, JsonSchema schema, ImplementationProperty[] properties, TypeValidationResult result)
        {
            IReadOnlyDictionary <string, JsonSchemaProperty> schemaProperties;

            try
            {
                schemaProperties = schema.ActualProperties;
            }
            catch (InvalidOperationException e)
            {
                throw new JsonSchemaException($"Cannot get schema properties for {schema.GetNameOrSpecificationId()}. {e.Message}", e);
            }

            foreach (var kv in schemaProperties)
            {
                var schemaProperty   = kv.Value.ActualSchema;
                var name             = kv.Value.Name;
                var specTypeProperty = properties.SingleOrDefault(p => p.Name == name);
                if (specTypeProperty == null)
                {
                    // No "type" means any type, which maps to None
                    if (schemaProperty.Type.HasFlag(JsonObjectType.None) || schemaProperty.Type.HasFlag(JsonObjectType.Null))
                    {
                        if (result.Validation == Validation.SpecToType)
                        {
                            result.AddError(TypeValidationError.NotFound(specType, schema.GetNameOrSpecificationId(), name));
                        }
                    }
                    else
                    {
                        result.AddError(TypeValidationError.NotFound(specType, schema.GetNameOrSpecificationId(), name));
                    }

                    continue;
                }

                // check certain .NET types first before defaulting to the JSON schema flags.
                // A property might be represented as more than one type e.g. ["integer", "string", "null],
                // so it's better to look at the .NET type first and try to look for the associated JSON schema flag.
                switch (specTypeProperty.PropertyType.FullName)
                {
                case "System.UInt16":
                case "System.Int16":
                case "System.Byte":
                case "System.SByte":
                case "System.UInt32":
                case "System.Single":
                case "System.Decimal":
                case "System.Double":
                case "System.UInt64":
                    if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
                    {
                        ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else
                    {
                        result.AddError(
                            TypeValidationError.ExpectedType(
                                specType,
                                specTypeProperty.PropertyType,
                                "number",
                                schema.GetNameOrSpecificationId(),
                                name));
                    }
                    break;

                case "System.Int64":
                case "System.Int32":
                    if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
                    {
                        ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Integer))
                    {
                        ValidateInteger(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else
                    {
                        result.AddError(
                            TypeValidationError.ExpectedType(
                                specType,
                                specTypeProperty.PropertyType,
                                "integer",
                                schema.GetNameOrSpecificationId(),
                                name));
                    }
                    break;

                case "System.String":
                    if (schemaProperty.Type.HasFlag(JsonObjectType.String))
                    {
                        ValidateString(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else
                    {
                        result.AddError(
                            TypeValidationError.ExpectedType(
                                specType,
                                specTypeProperty.PropertyType,
                                "string",
                                schema.GetNameOrSpecificationId(),
                                name));
                    }
                    break;

                case "System.Boolean":
                    if (schemaProperty.Type.HasFlag(JsonObjectType.Boolean))
                    {
                        ValidateBoolean(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else
                    {
                        result.AddError(
                            TypeValidationError.ExpectedType(
                                specType,
                                specTypeProperty.PropertyType,
                                "boolean",
                                schema.GetNameOrSpecificationId(),
                                name));
                    }
                    break;

                default:
                    // Are there multiple types? If so, based on the .NET type not being a primitive type, we would expect the presence of the
                    // schema "object" or "array" type in the majority of cases, so check these first.
                    // For types with custom serialization, the schema type may be a primitive type, so we can't easily statically validate it.
                    if (HasMultipleNonNullTypes(schemaProperty.Type))
                    {
                        if (schemaProperty.Type.HasFlag(JsonObjectType.Object))
                        {
                            ValidateProperties(specTypeProperty.PropertyType, schemaProperty, result);
                        }
                        else if (schemaProperty.Type.HasFlag(JsonObjectType.Array) &&
                                 IsEnumerableType(specTypeProperty.PropertyType, out var elementType))
                        {
                            ValidateProperties(elementType, schemaProperty.Item.ActualSchema, result);
                        }
                        else
                        {
                            result.AddIgnore(new TypeValidationIgnore(schema.GetNameOrSpecificationId(), name,
                                                                      $"Cannot statically check type. .NET type '{specType}', schema type '{schemaProperty.Type}'"));
                        }
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Boolean))
                    {
                        ValidateBoolean(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Integer))
                    {
                        ValidateInteger(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
                    {
                        ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.String))
                    {
                        if (schemaProperty.IsEnumeration && specTypeProperty.PropertyType.IsEnum)
                        {
                            ValidateEnum(specType, schema, schemaProperty, specTypeProperty, result);
                        }
                        else
                        {
                            ValidateString(specType, schema, schemaProperty, specTypeProperty, result);
                        }
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Object))
                    {
                        ValidateProperties(specTypeProperty.PropertyType, schemaProperty, result);
                    }
                    else if (schemaProperty.Type.HasFlag(JsonObjectType.Array))
                    {
                        if (IsEnumerableType(specTypeProperty.PropertyType, out var elementType))
                        {
                            ValidateProperties(elementType, schemaProperty.Item.ActualSchema, result);
                        }
                    }
                    break;
                }
            }
        }
Пример #10
0
        private static void ValidateProperties(Type specType, JsonSchema schema, TypeValidationResult result)
        {
            ImplementationProperty[] properties;

            try
            {
                properties = GetProperties(specType);
            }
            catch (ContractResolveException e)
            {
                result.AddIgnore(new TypeValidationIgnore(schema.GetNameOrSpecificationId(), specType.Name, e.Message));
                return;
            }

            ValidateProperties(specType, schema, properties, result);

            foreach (var inheritedSchema in schema.AllInheritedSchemas)
            {
                ValidateProperties(specType, inheritedSchema, properties, result);
            }

            if (schema.AnyOf.Count > 0)
            {
                var anyOfResults = Enumerable
                                   .Range(1, schema.AnyOf.Count)
                                   .Select(_ => new TypeValidationResult(specType, result.SpecificationId, result.Validation))
                                   .ToList();

                var index = 0;
                foreach (var anyOfSchema in schema.AnyOf)
                {
                    ValidateProperties(specType, anyOfSchema, properties, anyOfResults[index]);
                    ++index;
                }

                // at least one must be successful
                if (!anyOfResults.Any(r => r.Success))
                {
                    var errors = anyOfResults.Select(r => r.ToString());
                    result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), specType.Name,
                                                            $"anyOf failure: {string.Join(",", errors)}"));
                }
            }

            if (schema.OneOf.Count > 0)
            {
                var oneOfResults = Enumerable
                                   .Range(1, schema.OneOf.Count)
                                   .Select(_ => new TypeValidationResult(specType, result.SpecificationId, result.Validation))
                                   .ToList();

                var index = 0;
                foreach (var oneOfSchema in schema.OneOf)
                {
                    ValidateProperties(specType, oneOfSchema, properties, oneOfResults[index]);
                    ++index;
                }

                // only one must be successful
                if (!oneOfResults.Any(r => r.Success))
                {
                    var errors = oneOfResults.Select(r => r.ToString());
                    result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), specType.Name,
                                                            $"oneOf all failure: {string.Join(",", errors)}"));
                }
                else if (oneOfResults.Count(r => r.Success) > 0)
                {
                    var errors = oneOfResults.Where(r => r.Success).Select(r => r.ToString());
                    result.AddError(new TypeValidationError(specType, schema.GetNameOrSpecificationId(), specType.Name,
                                                            $"oneOf more than one successful failure: {string.Join(",", errors)}"));
                }
            }
        }