/// <summary> /// Returns list or errors /// </summary> /// <param name="view">View object</param> /// <returns>List of error descriptions (empty list if no contracts are violated)</returns> private static IEnumerable <ApiErrorView> PerformSpecificChecks(object view) { var result = new List <ApiErrorView>(); var methodInfos = view .GetType() .GetRuntimeMethods() .Where( mi => mi.ReturnType == typeof(bool) && mi.GetParameters().Length == 0 && mi.GetCustomAttributes <ApiViewCheckAttribute>().Any()) .ToList(); foreach (var methodInfo in methodInfos) { var deleg = methodInfo.CreateDelegate(typeof(Func <bool>), view); var checkResult = (bool)deleg.DynamicInvoke(); if (!checkResult) { var apiErrorView = ApiErrorViewFactory.ViewContractViolation(view, methodInfo.GetCustomAttribute <ApiViewCheckAttribute>().CheckDescription); result.Add(apiErrorView); } } return(result); }
private static void PerformTest() { var viewTree = new NodeView { Name = "Root", Children = new List <NodeView> { new NodeView { Children = new List <NodeView> { new NodeView() } } }, IsLeaf = true }; var checkResult = viewTree.CheckAsView(); var logBuilder = new StringBuilder(); logBuilder.AppendLine(); logBuilder.AppendLine($"IsCorrect: {checkResult.IsCorrect}"); logBuilder.AppendLine(Separator); logBuilder.AppendLine("Message:"); logBuilder.AppendLine(checkResult.ErrorDescription); var level = checkResult.IsCorrect ? LogLevel.Info : LogLevel.Warning; LogMessage(level, logBuilder.ToString()); var apiErrorView = ApiErrorViewFactory.ForeignKeyError(typeof(List <string>), "jljfslkdf"); using (var stringWriter = new StringWriter()) { var settings = new XmlWriterSettings { Indent = true, NewLineOnAttributes = true }; using (var xmlWriter = XmlWriter.Create(stringWriter, settings)) { var xmlSerializer = new XmlSerializer(apiErrorView.GetType()); xmlSerializer.Serialize(xmlWriter, apiErrorView); } LogMessage(LogLevel.Info, stringWriter.ToString()); LogMessage(LogLevel.Info, EnumInfoProvider.GetEnumElementByName("1", typeof(SimpleEnum)).ToString()); var str = EnumInfoProvider.GetElementName(FlagsEnum.Second | FlagsEnum.Third); LogMessage(LogLevel.Info, EnumInfoProvider.GetEnumElementByName <FlagsEnum>(str).ToString()); } }
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}"))); } }