public void ResourceStringValidationBadTimestamp()
        {
            var testFile = GetDocFile();
            var resource = testFile.Resources.Single(x => x.Name == "example.resource");
            var schema = new JsonSchema(resource);

            var method = testFile.Requests.Single(x => x.Identifier == "bad-timestamp");

            ValidationError[] detectedErrors;
            bool result = schema.ValidateExpectedResponse(method, out detectedErrors);

            Assert.IsFalse(result);
            Assert.IsTrue(detectedErrors.WereErrors());

            Assert.IsNotNull(detectedErrors.SingleOrDefault(x => x.Code == ValidationErrorCode.ExpectedTypeDifferent));
        }
        /// <summary>
        /// Examines input json string to ensure that it compiles with the JsonSchema definition. Any errors in the
        /// validation of the schema are returned via the errors out parameter.
        /// </summary>
        /// <param name="schema">Schemas definition used as a reference.</param>
        /// <param name="inputJson">Input json example to be validated</param>
        /// <param name="errors">Out parameter that provides any errors, warnings, or messages that were generated</param>
        /// <param name="expectedJson"></param>
        /// <returns></returns>
        public bool ValidateJsonCompilesWithSchema(JsonSchema schema, JsonExample inputJson, out ValidationError[] errors, JsonExample expectedJson = null, ValidationOptions options = null)
        {
            if (null == schema)
                throw new ArgumentNullException("schema");
            if (null == inputJson)
                throw new ArgumentNullException("inputJson");

            string collectionPropertyName = "value";
            if (null != inputJson.Annotation && null != inputJson.Annotation.CollectionPropertyName)
            {
                collectionPropertyName = inputJson.Annotation.CollectionPropertyName;
            }

            // If we didn't get an options, create a new one with some defaults provided by the annotation
            options = options ?? new ValidationOptions();
            options.AllowTruncatedResponses = (inputJson.Annotation ?? new CodeBlockAnnotation()).TruncatedResult;
            options.CollectionPropertyName = collectionPropertyName;

            return schema.ValidateJson(inputJson, out errors, this.registeredSchema, options, expectedJson);
        }
        /// <summary>
        /// Validates the value of json according to an implicit schmea defined by expectedJson
        /// </summary>
        /// <param name="expectedResponseAnnotation"></param>
        /// <param name="actualResponseBodyJson"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        public bool ValidateJsonExample(CodeBlockAnnotation expectedResponseAnnotation, string actualResponseBodyJson, out ValidationError[] errors, ValidationOptions options = null)
        {
            List<ValidationError> newErrors = new List<ValidationError>();

            var resourceType = expectedResponseAnnotation.ResourceType;
            if (resourceType == "stream")
            {
                // No validation since we're streaming data
                errors = null;
                return true;
            }
            else
            {
                JsonSchema schema;
                if (string.IsNullOrEmpty(resourceType))
                {
                    schema = JsonSchema.EmptyResponseSchema;
                }
                else if (!this.registeredSchema.TryGetValue(resourceType, out schema))
                {
                    newErrors.Add(new ValidationWarning(ValidationErrorCode.ResponseResourceTypeMissing, null, "Missing required resource: {0}. Validation limited to basics only.", resourceType));
                    // Create a new schema based on what's avaiable in the json
                    schema = new JsonSchema(actualResponseBodyJson, new CodeBlockAnnotation { ResourceType = expectedResponseAnnotation.ResourceType });
                }

                ValidationError[] validationJsonOutput;
                this.ValidateJsonCompilesWithSchema(schema, new JsonExample(actualResponseBodyJson, expectedResponseAnnotation), out validationJsonOutput, options: options);

                newErrors.AddRange(validationJsonOutput);
                errors = newErrors.ToArray();
                return errors.Length == 0;
            }
        }
 public void RegisterJsonResource(ResourceDefinition resource)
 {
     var schema = new JsonSchema(resource);
     this.registeredSchema[resource.Metadata.ResourceType] = schema;
 }
        /// <summary>
        /// Returns a JSON schema reference, either by finding an example in the registered schema or by 
        /// creating a new temporary schema from the fallback.
        /// </summary>
        /// <param name="resourceType"></param>
        /// <param name="errors"></param>
        /// <param name="jsonStringForFallbackIfMissingResource"></param>
        /// <returns></returns>
        protected JsonSchema GetJsonSchema(string resourceType, IList<ValidationError> errors, string jsonStringForFallbackIfMissingResource)
        {
            JsonSchema schema;
            if (string.IsNullOrEmpty(resourceType))
            {
                errors.Add(new ValidationMessage(null, "Resource type was null or missing, so we assume there is no response to validate."));
                schema = JsonSchema.EmptyResponseSchema;
            }
            else if (!this.registeredSchema.TryGetValue(resourceType, out schema) && !string.IsNullOrEmpty(jsonStringForFallbackIfMissingResource))
            {
                errors.Add(new ValidationWarning(ValidationErrorCode.ResponseResourceTypeMissing, null, "Missing required resource: {0}. Validation based on fallback example.", resourceType));
                // Create a new schema based on what's avaiable in the expected response JSON
                schema = new JsonSchema(jsonStringForFallbackIfMissingResource, new CodeBlockAnnotation { ResourceType = resourceType });
            }

            return schema;
        }
Ejemplo n.º 6
0
 private static JsonProperty ParseProperty(JToken token, JsonSchema containerSchema, List<ValidationError> detectedErrors = null)
 {
     JsonProperty propertyInfo = null;
     if (token.Type == JTokenType.Property)
     {
         JProperty tokenProperty = (JProperty)token;
         propertyInfo = ParseProperty(tokenProperty.Name, tokenProperty.Value, containerSchema);
     }
     else
     {
         if (detectedErrors != null)
         {
             detectedErrors.Add(
                 new ValidationWarning(
                     ValidationErrorCode.JsonParserException,
                     token.Path,
                     "Unhandled token type: " + token.Type));
         }
         else
         {
             Console.WriteLine("Unhandled token type: " + token.Type);
         }
     }
     return propertyInfo;
 }
Ejemplo n.º 7
0
        private static JsonProperty ParseProperty(string name, JToken value, JsonSchema containerSchema)
        {
            switch (value.Type)
            {
                case JTokenType.Boolean:
                    return new JsonProperty { Name = name, Type = JsonDataType.Boolean, OriginalValue = value.ToString() };

                case JTokenType.Float:
                case JTokenType.Integer:
                    return new JsonProperty { Name = name, Type = JsonDataType.Number, OriginalValue = value.ToString() };

                case JTokenType.String:
                    return new JsonProperty { Name = name, Type = JsonDataType.String, OriginalValue = value.ToString() };

                case JTokenType.Date:
                    return new JsonProperty { Name = name, Type = JsonDataType.String, OriginalValue = value.ToString() };

                case JTokenType.Object:
                    {
                        var objectSchema = ObjectToSchema((JObject)value);
                        if (objectSchema.ContainsKey("@odata.type"))
                        {
                            return new JsonProperty { Name = name, Type = JsonDataType.ODataType, ODataTypeName = objectSchema["@odata.type"].OriginalValue };
                        }
                        else
                        {
                            // See if we can infer type from the parent scehma
                            JsonProperty schemaProperty;
                            JsonDataType propertyType = JsonDataType.Object;
                            string odataTypeName = null;
                            if (null != containerSchema && containerSchema.ExpectedProperties.TryGetValue(name, out schemaProperty))
                            {
                                odataTypeName = schemaProperty.ODataTypeName;
                                propertyType = schemaProperty.Type;
                            }
                            return new JsonProperty { Name = name, Type = propertyType, ODataTypeName = odataTypeName, CustomMembers = ObjectToSchema((JObject)value) };
                        }
                    }

                case JTokenType.Array:
                    {
                        // Array
                        JsonDataType propertyType = JsonDataType.Array;
                        string odataTypeName = null;

                        // Infer type from the items in the array
                        var firstChild = value.First;
                        if (null != firstChild)
                        {
                            var objectType = ParseProperty("array[0]", firstChild, null);
                            if (null != objectType)
                            {
                                odataTypeName = objectType.ODataTypeName;
                                propertyType = objectType.Type;
                            }
                        }
                        else
                        {
                            propertyType = JsonDataType.Array;
                        }

                        // See if we can do better than just Custom
                        if (propertyType == JsonDataType.Object)
                        {
                            JsonProperty schemaProperty;
                            if (null != containerSchema && containerSchema.ExpectedProperties.TryGetValue(name, out schemaProperty))
                            {
                                // Use the parent schema's type indication
                                odataTypeName = schemaProperty.ODataTypeName;
                                propertyType = schemaProperty.Type;
                            }
                        }

                        Dictionary<string, JsonProperty> members = null;
                        if (propertyType == JsonDataType.Object || propertyType == JsonDataType.Array)
                        {
                            var firstValue = (JObject)value.First;
                            members = firstValue != null ? ObjectToSchema(firstValue) : new Dictionary<string, JsonProperty>();
                        }

                        return new JsonProperty { Name = name, Type = propertyType, ODataTypeName = odataTypeName, IsArray = true,
                            OriginalValue = value.ToString(), CustomMembers = members };
                    }
                case JTokenType.Null:
                    return new JsonProperty { Name = name, Type = JsonDataType.Object, IsArray = false, OriginalValue = null };
                default:
                    Console.WriteLine("Unsupported: Property {0} is of type {1} which is not currently supported.", name, value.Type);
                    throw new NotSupportedException(string.Format("Unsupported: Property {0} is of type {1} which is not currently supported.", name, value.Type));
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Validate the input json against the defined scehma when the instance was created.
        /// </summary>
        /// <param name="jsonInput">Input json to validate against schema</param>
        /// <param name="errors">Array of errors if the validation fails</param>
        /// <param name="otherSchemas"></param>
        /// <param name="options"></param>
        /// <param name="expectedJson"></param>
        /// <returns>True if validation was successful, otherwise false.</returns>
        public bool ValidateJson(JsonExample jsonInput, out ValidationError[] errors, Dictionary<string, JsonSchema> otherSchemas, ValidationOptions options, JsonExample expectedJson = null)
        {
            JContainer obj;
            try
            {
                var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None, NullValueHandling = NullValueHandling.Include, DefaultValueHandling = DefaultValueHandling.Include };
                obj = (JContainer)JsonConvert.DeserializeObject(jsonInput.JsonData, settings);
            }
            catch (Exception ex)
            {
                errors = new ValidationError[] { new ValidationError(ValidationErrorCode.JsonParserException, null, "Failed to parse json string: {0}. Json: {1}", ex.Message, jsonInput.JsonData) };
                return false;
            }

            var annotation = jsonInput.Annotation ?? new CodeBlockAnnotation();

            List<ValidationError> detectedErrors = new List<ValidationError>();

            bool expectErrorObject = (jsonInput.Annotation != null) && jsonInput.Annotation.ExpectError;

            // Check for an error response
            dynamic errorObject = obj["error"];
            if (null != errorObject && !expectErrorObject)
            {
                string code = errorObject.code;
                string message = errorObject.message;

                detectedErrors.Clear();
                detectedErrors.Add(new ValidationError(ValidationErrorCode.JsonErrorObject, null, "Error response received. Code: {0}, Message: {1}", code, message));
                errors = detectedErrors.ToArray();
                return false;
            }
            else if (expectErrorObject && null == errorObject)
            {
                detectedErrors.Clear();
                detectedErrors.Add(new ValidationError(ValidationErrorCode.JsonErrorObjectExpected, null, "Expected an error object response, but didn't receive one."));
                errors = detectedErrors.ToArray();
                return false;
            }

            // Check to see if this is a "collection" instance
            if (null != annotation && annotation.IsCollection)
            {
                this.ValidateCollectionObject(obj, annotation, otherSchemas, options.CollectionPropertyName, detectedErrors);
            }
            // otherwise verify the object matches this schema
            else
            {
                options = options ?? new ValidationOptions(annotation);
                if (null != expectedJson)
                {
                    var expectedJsonSchema = new JsonSchema(expectedJson.JsonData, expectedJson.Annotation);
                    options.ExpectedJsonSchema = expectedJsonSchema;
                    options.RequiredPropertyNames = expectedJsonSchema.ExpectedProperties.Keys.ToArray();
                }
                this.ValidateContainerObject(obj, options, otherSchemas, detectedErrors);
            }

            errors = detectedErrors.ToArray();
            return detectedErrors.Count == 0;
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Build a ParameterDefinition instance based on a property name and provide value and schema.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="containerSchema"></param>
        /// <returns></returns>
        internal static ParameterDefinition ParseProperty(string name, JToken value, JsonSchema containerSchema)
        {
            ParameterDefinition param = new ParameterDefinition();
            param.Name = name;
            param.OriginalValue = value.ToString();

            switch (value.Type)
            {
                case JTokenType.Boolean:
                    param.Type = ParameterDataType.Boolean;
                    break;
                case JTokenType.Float:
                    param.Type = ParameterDataType.Float;
                    break;
                case JTokenType.Integer:
                    param.Type = ParameterDataType.Int64;
                    break;
                case JTokenType.String:
                    var propValue = value.Value<string>();
                    SimpleDataType customType = ExtensionMethods.ParseSimpleTypeString(propValue.ToLowerInvariant());
                    ParameterDataType paramType = (customType != SimpleDataType.None) ? new ParameterDataType(customType) : ParameterDataType.String;
                    param.Type = paramType;
                    break;
                case JTokenType.Date:
                    param.Type = ParameterDataType.DateTimeOffset;
                    break;
                case JTokenType.Object:
                    {
                        var objectSchema = GeneratePropertyCollection((JObject)value);
                        if (objectSchema.ContainsKey("@odata.type"))
                        {
                            param.Type = new ParameterDataType(objectSchema["@odata.type"].OriginalValue);
                        }
                        else if (objectSchema.ContainsKey("@type"))
                        {
                            param.Type = new ParameterDataType(objectSchema["@type"].OriginalValue);
                        }
                        else
                        {
                            // See if we can infer type from the parent scehma
                            var propertyCollection = GeneratePropertyCollection((JObject)value);
                            ParameterDefinition[] customMembers = null;
                            if (propertyCollection != null)
                            {
                                customMembers = propertyCollection.Values.ToArray();
                            }

                            ParameterDefinition schemaProperty;
                            if (null != containerSchema && containerSchema.ExpectedProperties.TryGetValue(name, out schemaProperty))
                            {
                                param.Type = new ParameterDataType(schemaProperty.Type, customMembers);
                            }
                            else
                            {
                                param.Type = new ParameterDataType(customMembers);
                            }
                        }
                        break;
                    }
                case JTokenType.Array:
                {
                    ParameterDataType propertyType = ParameterDataType.GenericCollection;

                    // Try to infer type from the items in the array
                    var firstChild = value.First;
                    if (null != firstChild)
                    {
                        var objectType = ParseProperty("array[0]", firstChild, null);
                        if (null != objectType)
                        {
                            propertyType = ParameterDataType.CollectionOfType(objectType.Type);
                        }
                    }

                    // See if we can do better than GenericCollection if that's the situation we're in.
                    if (propertyType == ParameterDataType.GenericCollection)
                    {
                        ParameterDefinition schemaProperty;
                        if (null != containerSchema && containerSchema.ExpectedProperties.TryGetValue(name, out schemaProperty))
                        {
                            // Use the parent schema's type indication
                            //propertyType = ParameterDataType.CollectionOfType(schemaProperty.Type);
                            propertyType = schemaProperty.Type;
                        }
                    }

                    Dictionary<string, ParameterDefinition> members = null;
                    if ((propertyType.IsObject || (propertyType.IsCollection && propertyType.CollectionResourceType == SimpleDataType.Object)) &&
                        string.IsNullOrEmpty(propertyType.CustomTypeName))
                    {
                        // If we don't know what kind of object is here, let's record what we see as custom members
                        var firstValue = (JObject)value.First;
                        members = firstValue != null ? GeneratePropertyCollection(firstValue) : new Dictionary<string, ParameterDefinition>();
                    }
                    ParameterDefinition[] customMembers = null;
                    if (members != null) customMembers = members.Values.ToArray();
                    param.Type = new ParameterDataType(propertyType, customMembers);
                    break;
                }
                case JTokenType.Null:
                    param.Type = ParameterDataType.GenericObject;
                    param.OriginalValue = null;
                    break;
                default:
                    Console.WriteLine("Unsupported: Property {0} is of type {1} which is not currently supported.", name, value.Type);
                    throw new NotSupportedException(string.Format("Unsupported: Property {0} is of type {1} which is not currently supported.", name, value.Type));
            }

            return param;
        }
        public void ResourceStringValidationValidExampleTest()
        {
            var testFile = GetDocFile();

            var resource = testFile.Resources.Single(x => x.Name == "example.resource");
            var schema = new JsonSchema(resource);

            var method = testFile.Requests.Single(x => x.Identifier == "valid-response");

            ValidationError[] detectedErrors;
            bool result = schema.ValidateExpectedResponse(method, out detectedErrors);
            Assert.IsTrue(result);
            Assert.IsEmpty(detectedErrors, "Validation errors were detected");
        }
Ejemplo n.º 11
0
        public void RegisterJsonResource(ResourceDefinition resource)
        {
            var schema = new JsonSchema(resource);

            this.registeredSchema[resource.Name] = schema;
        }