public void CheckJsonProperty(JsonProperty prop, JsonDataType? expectedType = null, string odataTypeName = null, bool? isArray = null, bool? customMembersIsNull = null) { if (expectedType != null) Assert.AreEqual(expectedType.Value, prop.Type); if (odataTypeName != null) Assert.AreEqual(odataTypeName, prop.ODataTypeName); if (isArray != null) Assert.AreEqual(isArray.Value, prop.IsArray); if (customMembersIsNull != null && customMembersIsNull.Value) Assert.IsNull(prop.CustomMembers); else if (customMembersIsNull != null && !customMembersIsNull.Value) Assert.IsNotNull(prop.CustomMembers); }
private PropertyValidationOutcome ValidateSimpleArrayProperty(JsonProperty actualProperty, JsonProperty expectedProperty, List<ValidationError> detectedErrors) { if (actualProperty.IsArray != true || expectedProperty.IsArray != true) { throw new SchemaBuildException("Cannot use simple array valiation without array types", null); } if (actualProperty.Type == expectedProperty.Type && expectedProperty.Type != JsonDataType.Object && expectedProperty.Type != JsonDataType.ODataType) { return PropertyValidationOutcome.Ok; } else if (expectedProperty.IsArray && actualProperty.IsArray && actualProperty.OriginalValue == "[]") { return PropertyValidationOutcome.Ok; } else { detectedErrors.Add(new ValidationError(ValidationErrorCode.ArrayTypeMismatch, null, "Array expected members to be of type {0} but found: {1}", expectedProperty.Type, actualProperty.Type)); return PropertyValidationOutcome.InvalidType; } }
/// <summary> /// Verify that a property from the json-to-validate matches something in our schema /// </summary> /// <param name="inputProperty"></param> /// <param name="schemas"></param> /// <param name="detectedErrors"></param> /// <param name="options"></param> /// <returns></returns> private PropertyValidationOutcome ValidateProperty(JsonProperty inputProperty, Dictionary<string, JsonSchema> schemas, List<ValidationError> detectedErrors, ValidationOptions options) { if (this.ExpectedProperties.ContainsKey(inputProperty.Name)) { // The property was expected to be found in this schema! Yay. var schemaPropertyDef = this.ExpectedProperties[inputProperty.Name]; // Check for simple value types first if (this.SimpleValueTypes(schemaPropertyDef.Type, inputProperty.Type) && this.AllFalse(schemaPropertyDef.IsArray, inputProperty.IsArray)) { if (schemaPropertyDef.Type == inputProperty.Type && inputProperty.Type != JsonDataType.String) { return PropertyValidationOutcome.Ok; } else if (schemaPropertyDef.Type == inputProperty.Type && inputProperty.Type == JsonDataType.String) { // Perform extra validation to see if the string is the right format (iso date, enum value, url, or just a string) if (null == options || options.RelaxedStringValidation) return PropertyValidationOutcome.Ok; return ValidateStringFormat(schemaPropertyDef, inputProperty, detectedErrors); } else { // Type of the inputProperty is mismatched from the expected value. detectedErrors.Add(new ValidationError(ValidationErrorCode.ExpectedTypeDifferent, null, "Expected type {0} but was instead {1}: {2}", schemaPropertyDef.Type, inputProperty.Type, inputProperty.Name)); return PropertyValidationOutcome.InvalidType; } } else if (null == inputProperty.OriginalValue) { if (null != this.NullableProperties && !this.NullableProperties.Contains(schemaPropertyDef.Name)) { detectedErrors.Add(new ValidationWarning(ValidationErrorCode.NullPropertyValue, null, "Non-nullable property {0} had a null value in the response. Expected {1}.", schemaPropertyDef.Name, schemaPropertyDef.Type)); } return PropertyValidationOutcome.Ok; } else if (schemaPropertyDef.IsArray || inputProperty.IsArray) { // Check for an array if (schemaPropertyDef.IsArray && !inputProperty.IsArray) { // Expected an array, but didn't get one detectedErrors.Add(new ValidationError(ValidationErrorCode.ExpectedArrayValue, null, "Expected an array but property was not an array: {0}", inputProperty.Name)); return PropertyValidationOutcome.InvalidType; } else if (!schemaPropertyDef.IsArray && inputProperty.IsArray) { detectedErrors.Add(new ValidationError(ValidationErrorCode.ExpectedNonArrayValue, null, "Expected a value of type {0} but property was an array: {1}", schemaPropertyDef.Type, inputProperty.Name)); return PropertyValidationOutcome.InvalidType; } return this.ValidateArrayProperty(inputProperty, schemas, detectedErrors, options); } else if (schemaPropertyDef.Type == JsonDataType.ODataType && (inputProperty.Type == JsonDataType.Object || inputProperty.Type == JsonDataType.ODataType)) { // Compare the ODataType schema to the custom schema if (!schemas.ContainsKey(schemaPropertyDef.ODataTypeName)) { detectedErrors.Add(new ValidationError(ValidationErrorCode.ResourceTypeNotFound, null, "Missing resource: resource {0} was not found (property name '{1}').", schemaPropertyDef.ODataTypeName, inputProperty.Name)); return PropertyValidationOutcome.MissingResourceType; } else if (inputProperty.Type == JsonDataType.Object) { var odataSchema = schemas[schemaPropertyDef.ODataTypeName]; ValidationError[] odataErrors; if (null != inputProperty.CustomMembers && !odataSchema.ValidateCustomObject(inputProperty.CustomMembers.Values.ToArray(), out odataErrors, schemas, options)) { var propertyError = ValidationError.NewConsolidatedError(ValidationErrorCode.ConsolidatedError, odataErrors, "Schema validation failed on property '{0}' ['{1}']", inputProperty.Name, odataSchema.ResourceName); detectedErrors.Add(propertyError); return PropertyValidationOutcome.InvalidType; } else if (null == inputProperty.CustomMembers) { detectedErrors.Add(new ValidationError(ValidationErrorCode.NoCustomMembersFound, null, "Property '{0}' is of type Custom but has no custom members.", inputProperty.Name)); } return PropertyValidationOutcome.Ok; } else { var odataSchema = schemas[schemaPropertyDef.ODataTypeName]; if (inputProperty.CustomMembers == null) { detectedErrors.Add(new ValidationError(ValidationErrorCode.MissingCustomMembers, null, "Property {0} is missing custom members and cannot be validated.", inputProperty.Name)); return PropertyValidationOutcome.InvalidType; } else { odataSchema.ValidateObjectProperties(inputProperty.CustomMembers.Values, options, schemas, detectedErrors); return PropertyValidationOutcome.Ok; } } } else if (schemaPropertyDef.Type == JsonDataType.Object) { detectedErrors.Add(new ValidationWarning(ValidationErrorCode.CustomValidationNotSupported, null, "Schema type was 'Custom' which is not supported. Add a resource type to the definition of property: {0}", inputProperty.Name)); return PropertyValidationOutcome.MissingResourceType; } else { detectedErrors.Add(new ValidationError(ValidationErrorCode.ExpectedTypeDifferent, null, "Type mismatch: property '{0}' [{1}] doesn't match expected type [{2}].", inputProperty.Name, inputProperty.Type, schemaPropertyDef.Type)); return PropertyValidationOutcome.InvalidType; } } else { detectedErrors.Add(new ValidationWarning(ValidationErrorCode.AdditionalPropertyDetected, null, "Extra property: property '{0}' [{1}] was not expected.", inputProperty.Name, inputProperty.Type)); return PropertyValidationOutcome.MissingFromSchema; } }
private bool ValidateCustomObject(JsonProperty[] properties, out ValidationError[] errors, Dictionary<string, JsonSchema> otherSchemas, ValidationOptions options) { List<string> missingProperties = new List<string>(this.ExpectedProperties.Keys); List<ValidationError> detectedErrors = new List<ValidationError>(); foreach (var inputProperty in properties) { missingProperties.Remove(inputProperty.Name); this.ValidateProperty(inputProperty, otherSchemas, detectedErrors, new ValidationOptions()); } this.CleanMissingProperties(options, missingProperties); errors = detectedErrors.ToArray(); return detectedErrors.Count == 0; }
/// <summary> /// Check each member of the actualProperty's array to make sure it matches the resource type specified for the property. /// </summary> /// <param name="actualProperty"></param> /// <param name="schemas"></param> /// <param name="detectedErrors"></param> /// <param name="options"></param> private PropertyValidationOutcome ValidateArrayProperty(JsonProperty actualProperty, Dictionary<string, JsonSchema> schemas, List<ValidationError> detectedErrors, ValidationOptions options) { JArray actualArray = (JArray)JsonConvert.DeserializeObject(actualProperty.OriginalValue); JsonSchema memberSchema; if (string.IsNullOrEmpty(actualProperty.ODataTypeName)) { return this.ValidateSimpleArrayProperty(actualProperty, this.ExpectedProperties[actualProperty.Name], detectedErrors); } else if (!schemas.TryGetValue(actualProperty.ODataTypeName, out memberSchema)) { detectedErrors.Add(new ValidationError(ValidationErrorCode.ResourceTypeNotFound, null, "Failed to locate resource definition for: {0}", actualProperty.ODataTypeName)); return PropertyValidationOutcome.MissingResourceType; } bool hadErrors = false; for(int i=0; i<actualArray.Count; i++) { JContainer member = actualArray[i] as JContainer; if (member != null) { List<ValidationError> memberErrors = new List<ValidationError>(); memberSchema.ValidateContainerObject(member, options, schemas, memberErrors); hadErrors |= memberErrors.Count > 0; foreach (var error in memberErrors) { error.Source = string.Format("{0} [{1}]", actualProperty.Name, i); detectedErrors.Add(error); } } } return hadErrors ? PropertyValidationOutcome.GenericError : PropertyValidationOutcome.Ok; }
private static PropertyValidationOutcome ValidateStringFormat(JsonProperty schemaProperty, JsonProperty inputProperty, List<ValidationError> detectedErrorsCollection) { switch (schemaProperty.StringFormat) { case ExpectedStringFormat.Iso8601Date: { DateTime output; bool result = (DateTime.TryParseExact(inputProperty.OriginalValue, Iso8601Formats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out output)); if (!result) { detectedErrorsCollection.Add(new ValidationError(ValidationErrorCode.InvalidDateTimeString, null, "Invalid ISO 8601 date-time string in property: {1}: {0}", inputProperty.OriginalValue, schemaProperty.Name)); return PropertyValidationOutcome.BadStringValue; } return PropertyValidationOutcome.Ok; } case ExpectedStringFormat.AbsoluteUrl: { try { Uri parsedUri = new Uri(inputProperty.OriginalValue, UriKind.Absolute); return PropertyValidationOutcome.Ok; } catch (FormatException) { detectedErrorsCollection.Add(new ValidationError(ValidationErrorCode.InvalidUrlString, null, "Invalid absolute URL value in property {1}: {0}", inputProperty.OriginalValue, schemaProperty.Name)); return PropertyValidationOutcome.BadStringValue; } } case ExpectedStringFormat.EnumeratedValue: { if (!schemaProperty.IsValidEnumValue(inputProperty.OriginalValue)) { detectedErrorsCollection.Add(new ValidationError(ValidationErrorCode.InvalidEnumeratedValueString, null, "Invalid enumerated value in property {1}: {0}", inputProperty.OriginalValue, schemaProperty.Name)); return PropertyValidationOutcome.BadStringValue; } return PropertyValidationOutcome.Ok; } case ExpectedStringFormat.Generic: return PropertyValidationOutcome.Ok; default: throw new NotImplementedException(); } }
private static void AddParameterDataToProperty(IEnumerable<ParameterDefinition> parameters, JsonProperty propertyInfo) { if (null != propertyInfo && null != parameters) { // See if we can look up more data for this new property var findParameterQuery = from p in parameters where p.Name == propertyInfo.Name select p; var parameterData = findParameterQuery.FirstOrDefault(); if (null != parameterData) { propertyInfo.Description = parameterData.Description; } } }
//private static object SwaggerProperty(JsonDataType type, string odataTypeName) private static object SwaggerProperty(JsonProperty property) { JsonDataType type = property.Type; string customDataType = property.ODataTypeName; string description = property.Description; return MakeSwaggerProperty(type, customDataType, description); }