private static ViewCheckResult BuildCheckResult(object view, List <ApiErrorView> errors, List <ApiErrorView> innerErrors) { var hasErrors = errors.Any(); var hasInnerErrors = innerErrors.Any(); if (!hasInnerErrors && !hasErrors) { return(ViewCheckResult.Ok()); } var allErrors = new List <ApiErrorView>(errors); if (hasInnerErrors) { allErrors.Add(ApiErrorViewFactory.InnerErrors(view, innerErrors)); } return(ViewCheckResult.Error(ApiErrorViewFactory.InnerErrors(view, allErrors))); }
public static ViewCheckResult CheckAsView(this object view) { var type = view.GetType(); var list = view as IList; if (list != null && type.IsConstructedGenericType) { var checkResults = (from object item in list select CheckAsView(item)).ToList(); if (!checkResults.Any()) { return(ViewCheckResult.Ok()); } var errorViews = checkResults .Where(result => !result.IsCorrect) .Select(result => result.ApiErrorView) .ToList(); return(ViewCheckResult.Error(ApiErrorViewFactory.InnerErrors(view, errorViews))); } return(ViewChecker.CheckContract(view)); }
/// <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}"))); } }