예제 #1
0
        /// <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}")));
            }
        }