/// <summary> /// Checks if view complies with it's contract /// </summary> /// <param name="view">Object to check</param> /// <returns>Simply ok or verbose contract errors description (for reading, not serializing)</returns> /// <exception cref="InvalidOperationException"> /// Will be thrown if <see cref="view"/> does not have <see cref="ApiViewAttribute"/> attribute /// </exception> public static ViewCheckResult CheckContract(object view) { if (view == null) { return(ViewCheckResult.Ok()); } if (!view.IsApiView()) { throw new InvalidOperationException("No classes except API views can have contracts"); } try { var properties = view.GetType().GetRuntimeProperties(); var checkResults = new List <ApiErrorView>(); var innerCheckResults = new List <ApiErrorView>(); checkResults.AddRange(PerformSpecificChecks(view)); foreach (var propertyInfo in properties) { var propertyValue = propertyInfo.GetValue(view); if (propertyValue != null) { if (propertyValue.IsApiView()) { var checkResult = CheckContract(propertyValue); if (!checkResult.IsCorrect) { innerCheckResults.Add(checkResult.ApiErrorView); } } if (propertyValue.IsApiViewList()) { var list = propertyValue as IList; if (list == null) { continue; } innerCheckResults.AddRange(from object item in list select CheckContract(item) into checkResult where !checkResult.IsCorrect select checkResult.ApiErrorView); } } var contractAttributes = propertyInfo.GetCustomAttributes <ApiContractAttribute>().ToList(); if (!contractAttributes.Any()) { continue; } if (contractAttributes.Count != 1) { return(ViewCheckResult.Error(ApiErrorViewFactory.Unspecified())); } var contractAttribute = contractAttributes[0]; var contractType = contractAttribute.Type; var failedChecks = CheckerDictionary .Where(kv => contractType.HasFlag(kv.Key) && !kv.Value.Invoke(propertyValue)) .Select(kv => kv.Key) .ToList(); if (failedChecks.Any()) { var propertyName = propertyInfo.Name; checkResults.AddRange( failedChecks.Select(fc => ApiErrorViewFactory.ViewPropertyContractViolation(view, propertyName, fc))); } } return(BuildCheckResult(view, checkResults, innerCheckResults)); } catch (Exception ex) { return(ViewCheckResult.Error(ApiErrorViewFactory.Unspecified($"Failed to check view: {ex.Message}"))); } }