/// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet = string.Empty;
            string navigProp = string.Empty;

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                entitySet = r.Key;

                foreach (var np in r.Value.Item2)
                {
                    navigProp = np.NavigationPropertyName;
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    var    rest = AnnotationsHelper.GetCountRestrictions(context.MetadataDocument, context.VocCapabilities);

                    if (string.IsNullOrEmpty(rest.Item1) ||
                        null == rest.Item2 || !rest.Item2.Any() ||
                        null == rest.Item3 || !rest.Item3.Any())
                    {
                        continue;
                    }

                    break;
                }

                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigProp))
            {
                detail.ErrorMessage = "Cannot get expanded entities because cannot get collection type of navigation property from metadata";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string   url  = string.Format("{0}/{1}?$expand={2}($count=true)", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigProp);
            Uri      uri  = new Uri(url);
            Response resp = WebHelper.Get(uri, Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail                    = new ExtensionRuleResultDetail(this.Name, uri.AbsoluteUri, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);
            detail.URI                = url;
            detail.ResponsePayload    = resp.ResponsePayload;
            detail.ResponseHeaders    = resp.ResponseHeaders;
            detail.HTTPMethod         = "GET";
            detail.ResponseStatusCode = resp.StatusCode.ToString();
            if (resp != null && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);

                if (null == feed)
                {
                    detail.ErrorMessage = "Cannot find any entity-sets in the service.\r\n";
                    info = new ExtensionRuleViolationInfo(uri, resp.ResponsePayload, detail);

                    return(passed);
                }

                JObject entry    = null;
                var     entities = JsonParserHelper.GetEntries(feed);

                foreach (var e in entities)
                {
                    if (null != e[navigProp] && JTokenType.Array == e[navigProp].Type)
                    {
                        entry = e as JObject;
                        break;
                    }
                }

                if (null == entry)
                {
                    detail.ErrorMessage = "Cannot find an appropriate entity which contains at least one collection-valued navigation property.\r\n";
                    info = new ExtensionRuleViolationInfo(uri, resp.ResponsePayload, detail);

                    return(passed);
                }


                if (null != entry && JTokenType.Object == entry.Type)
                {
                    JArray jArr         = entry[navigProp] as JArray;
                    int    actualAmount = 0;
                    JsonParserHelper.GetEntitiesNumFromCollectionValuedNavigProp(entry[Constants.V4OdataId].ToString(), entry, navigProp, context.RequestHeaders, ref actualAmount);

                    if (null != entry[navigProp + Constants.V4OdataCount] && actualAmount == (int)entry[navigProp + Constants.V4OdataCount])
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$count' for expanded properties.";
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$count' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
コード例 #2
0
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            List <ExtensionRuleResultDetail> details = new List <ExtensionRuleResultDetail>();
            ExtensionRuleResultDetail        detail1 = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail        detail2 = new ExtensionRuleResultDetail(this.Name);
            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail1.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail1.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            string entitySet     = string.Empty;
            string navigPropName = string.Empty;

            #region Verify the system query $top.

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();

                    if (false == nEntitySetName.IsEntitySetSupportTopQuery(context.MetadataDocument, new List <string>()
                    {
                        context.VocCapabilities
                    }))
                    {
                        continue;
                    }

                    navigPropName = np.NavigationPropertyName;
                    break;
                }

                entitySet = r.Key;
                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail1.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail1.ErrorMessage = "Cannot find any collection-valued navigation properties which support system query options $top.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            bool?  isTopQueryValidation = null;
            string url      = string.Format("{0}/{1}?$expand={2}($top=1)", context.ServiceBaseUri, entitySet, navigPropName);
            var    response = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                response.ResponsePayload.TryToJObject(out feed);

                if (feed != null && JTokenType.Object == feed.Type)
                {
                    var entities = JsonParserHelper.GetEntries(feed);

                    foreach (var entity in entities)
                    {
                        if (entity[navigPropName] != null)
                        {
                            if (JTokenType.Array == entity[navigPropName].Type && ((JArray)entity[navigPropName]).Count <= 1)
                            {
                                isTopQueryValidation = true;
                            }
                            else
                            {
                                passed = false;
                                detail1.ErrorMessage = "The service does not execute an accurate result on the system query option '$top' for expanded properties.";
                                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                                return(passed);
                            }
                        }
                    }
                }
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "The service does not support the system query option '$top' for expanded properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            #endregion

            #region Verify the system query $skip.

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();

                    if (false == nEntitySetName.IsEntitySetSupportSkipQuery(context.MetadataDocument, new List <string>()
                    {
                        context.VocCapabilities
                    }))
                    {
                        continue;
                    }

                    navigPropName = np.NavigationPropertyName;
                    break;
                }

                entitySet = r.Key;
                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail2.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail2.ErrorMessage = "Cannot find any collection-valued navigation properties which support system query options $skip.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            bool?isSkipQueryValidation = null;
            url      = string.Format("{0}/{1}?$expand={2}($skip=1)", context.ServiceBaseUri, entitySet, navigPropName);
            response = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail2  = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                response.ResponsePayload.TryToJObject(out feed);

                if (feed != null && JTokenType.Object == feed.Type)
                {
                    var entities = JsonParserHelper.GetEntries(feed);

                    foreach (var entity in entities)
                    {
                        if (entity[navigPropName] != null)
                        {
                            int actualAmount = 0;
                            JsonParserHelper.GetEntitiesNumFromCollectionValuedNavigProp(context.DestinationBasePath, (JObject)entity, navigPropName, context.RequestHeaders, ref actualAmount);

                            if (JTokenType.Array == entity[navigPropName].Type && ((JArray)entity[navigPropName]).Count <= actualAmount)// TODO: This should be justified by total count
                            {
                                isSkipQueryValidation = true;
                            }
                            else
                            {
                                isSkipQueryValidation = false;
                                detail2.ErrorMessage  = "The service does not execute an accurate result on the system query option '$skip' for expanded properties.";
                                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                                return(isSkipQueryValidation);
                            }
                        }
                    }
                }
            }
            else
            {
                passed = false;
                detail2.ErrorMessage = "The service does not support the system query option '$skip' for expanded properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            #endregion

            if (isTopQueryValidation == true && isSkipQueryValidation == true)
            {
                passed = true;
            }

            details.Add(detail1);
            details.Add(detail2);
            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);

            return(passed);
        }