Exemplo n.º 1
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            info = null;
            var    svcStatus     = ServiceStatus.GetInstance();
            string navigPropName = string.Empty;
            string entityTypeShortName;
            var    entityTypeShortNames            = new List <string>();
            Tuple <string, string>    keyProp      = null;
            ExtensionRuleResultDetail detailglobal = new ExtensionRuleResultDetail(this.Name);

            info         = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detailglobal);
            detailglobal = info.Details[0];

            do
            {
                var navigPropNames = MetadataHelper.GetNavigationPropertyNames(out entityTypeShortName, entityTypeShortNames);
                if (null == navigPropNames || !navigPropNames.Any())
                {
                    detailglobal.ErrorMessage = "No navigation properties available to test";
                    return(passed);
                }

                navigPropName = navigPropNames[0];
                entityTypeShortNames.Add(entityTypeShortName);
                keyProp = MetadataHelper.GetKeyProperty(entityTypeShortName);
            }while (null == keyProp);

            string keyPropName  = keyProp.Item1;
            string keyPropType  = keyProp.Item2;
            var    entitySetUrl = entityTypeShortName.GetAccessEntitySetURL();

            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detailglobal.ErrorMessage = "Unable to get a proper Entity URL";
                return(passed);
            }

            string url  = svcStatus.RootURL.TrimEnd('/') + "/" + entitySetUrl;
            var    resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detailglobal.ErrorMessage = resp.StatusCode + "status code returned for " + url;
                return(passed);
            }

            var entities = JsonParserHelper.GetEntities(resp.ResponsePayload);

            if (!entities.Any())
            {
                detailglobal.ErrorMessage = "No entities returned:  " + resp.ResponsePayload;
                return(passed);
            }

            var entity = entities.First();

            if (!entity[keyPropName].HasValues)
            {
                detailglobal.ErrorMessage = keyPropName + " have no values to test";
                return(passed);
            }
            string keyPropVal = entity[keyPropName].ToString();
            string pattern    = "Edm.String" == keyPropType ? "{0}('{1}')/{2}/$ref" : "{0}({1})/{2}/$ref";

            url  = string.Format(pattern, url, keyPropVal, navigPropName);
            resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
            var detail = new ExtensionRuleResultDetail("ServiceImpl_SystemQueryOptionRef", url, HttpMethod.Get, string.Empty);

            detail.URI                = url;
            detail.ResponsePayload    = resp.ResponsePayload;
            detail.ResponseHeaders    = resp.ResponseHeaders;
            detail.HTTPMethod         = "GET";
            detail.ResponseStatusCode = resp.StatusCode.ToString();


            info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detailglobal);
            info.AddDetail(detail);
            if (null != resp && HttpStatusCode.OK == resp.StatusCode)
            {
                entities = JsonParserHelper.GetEntities(resp.ResponsePayload);
                if (!entities.Any())
                {
                    detail.ErrorMessage = "No entities available to test";
                    return(false);
                }

                entity = entities.First();
                var odataId = entity[Constants.V4OdataId].ToString();
                resp   = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
                passed = null != resp && HttpStatusCode.OK == resp.StatusCode;
                if (passed == false)
                {
                    detail.URI                = url;
                    detail.ResponsePayload    = resp.ResponsePayload;
                    detail.ResponseHeaders    = resp.ResponseHeaders;
                    detail.HTTPMethod         = "GET";
                    detail.ResponseStatusCode = resp.StatusCode.ToString();
                    detail.ErrorMessage       = "Response return an error message:  " + resp.StatusCode.ToString();
                }
            }
            else
            {
                detail.URI                = url;
                detail.ResponsePayload    = resp.ResponsePayload;
                detail.ResponseHeaders    = resp.ResponseHeaders;
                detail.HTTPMethod         = "GET";
                detail.ResponseStatusCode = resp.StatusCode.ToString();

                detail.ErrorMessage = "Response return an error message:  " + resp.StatusCode.ToString();
                passed = false;
            }

            return(passed);
        }
        /// <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 countRestrictions            = AnnotationsHelper.GetCountRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(countRestrictions.Item1))
            {
                detail.ErrorMessage = "Cannot find an entity-set which supports $count system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string   entitySet           = countRestrictions.Item1;
            bool     isNextLinkPropExist = false;
            Int64    totalCount          = 0;
            string   url  = string.Format("{0}/{1}?$count=true", context.ServiceBaseUri.AbsoluteUri.TrimEnd('/'), entitySet);
            Response resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, context.RequestHeaders), resp);

            if (resp.StatusCode.HasValue && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);
                var entries = JsonParserHelper.GetEntries(feed);

                if (feed[Constants.V4OdataCount] != null)
                {
                    totalCount = Convert.ToInt64(feed[Constants.V4OdataCount].Value <string>().StripOffDoubleQuotes());

                    if (entries.Count == totalCount)
                    {
                        passed = true;
                    }
                    else if (entries.Count < totalCount)
                    {
                        var jProps = feed.Children();

                        foreach (JProperty jProp in jProps)
                        {
                            if (jProp.Name == Constants.V4OdataNextLink)
                            {
                                isNextLinkPropExist = true;
                                break;
                            }
                        }

                        if (isNextLinkPropExist)
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            detail.ErrorMessage = string.Format("The feed has {0} entities totally, but it only display {1} entities and there is no NextLink annotation.", totalCount, entries.Count);
                        }
                    }
                    else
                    {
                        passed = null;
                        detail.ErrorMessage = string.Format("The response of feed {0} has only one page, so can not test partial results.", entitySet);
                    }
                }
                else
                {
                    passed = null;
                    detail.ErrorMessage = string.Format("The response of feed {0} does not have \"@Odata.count\" annotation so it cannot get the total count of entities.", entitySet);
                }
            }
            else
            {
                passed = null;
                detail.ErrorMessage = "Cannot get response from above URI.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <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 = AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(restrictions.Item1) ||
                null == restrictions.Item3 || !restrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            // Set a expected level number as 2, and store it in the parameter expectedLevel.
            int    expectedLevels      = 2;
            string entitySetName       = restrictions.Item1;
            string entityTypeShortName = entitySetName.MapEntitySetNameToEntityTypeShortName();

            string[] navigPropNames = MetadataHelper.GetNavigPropNamesRecurseByLevels(entityTypeShortName, context.MetadataDocument, expectedLevels);
            string   url            = string.Format("{0}/{1}?$expand=*($levels={2})", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySetName, expectedLevels);
            var      resp           = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

            if (resp != null && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);
                var entities = JsonParserHelper.GetEntries(feed);

                if (null == entities || !entities.Any())
                {
                    detail.ErrorMessage = string.Format("Cannot find any entities from the entity-set '{0}'", entitySetName);
                    info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                    return(passed);
                }

                bool    isFirstLevelEmpty = false;
                int     levelsCounter     = 0;
                JObject entry             = null;

                foreach (var en in entities)
                {
                    levelsCounter     = 0;
                    entry             = en as JObject;
                    isFirstLevelEmpty = false;
                    for (int i = 0; i < expectedLevels; i++)
                    {
                        if (entry != null && JTokenType.Object == entry.Type)
                        {
                            var navigPropVal = entry[navigPropNames[i]];

                            if (navigPropVal != null)
                            {
                                levelsCounter++;
                                entry = navigPropVal.Type == JTokenType.Array ? navigPropVal.First as JObject : navigPropVal as JObject;
                                if (entry == null)
                                {
                                    isFirstLevelEmpty = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (expectedLevels == levelsCounter)
                    {
                        passed = true;
                    }
                    else if (!(levelsCounter < expectedLevels && isFirstLevelEmpty)) // If not no data
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$levels' for expanded properties.";

                        break;
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$levels' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
Exemplo n.º 4
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;

            info = null;

            if (context.PayloadType == RuleEngine.PayloadType.Feed)
            {
                JObject feed;
                context.ResponsePayload.TryToJObject(out feed);
                var entries = JsonParserHelper.GetEntries(feed);

                foreach (JObject entry in entries)
                {
                    if (entry != null && entry.Type == JTokenType.Object)
                    {
                        var jProps = entry.Children();

                        // New a string to record the annotation name.
                        string record = string.Empty;

                        foreach (JProperty jProp in jProps)
                        {
                            if (JsonSchemaHelper.IsJSONArrayOrPrimitiveAnnotation(jProp.Name))
                            {
                                record = jProp.Name;
                            }
                            else
                            {
                                if (record == string.Empty)
                                {
                                    continue;
                                }
                                else
                                {
                                    // Prase the annotation name to two parts.
                                    string[] splitedStr = record.Split('@');

                                    // If the string before the sign "@" are the same with property name, this rule will passed.
                                    if (jProp.Name == splitedStr[0])
                                    {
                                        passed = true;
                                        record = string.Empty;
                                        continue;
                                    }
                                    else
                                    {
                                        passed = false;
                                        info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else if (context.PayloadType == RuleEngine.PayloadType.Entry)
            {
                JObject entry;
                context.ResponsePayload.TryToJObject(out entry);

                if (entry != null && entry.Type == JTokenType.Object)
                {
                    var jProps = entry.Children();

                    // New a string to record the annotation name.
                    string record = string.Empty;

                    foreach (JProperty jProp in jProps)
                    {
                        if (JsonSchemaHelper.IsJSONArrayOrPrimitiveAnnotation(jProp.Name))
                        {
                            record = jProp.Name;
                        }
                        else
                        {
                            if (record == string.Empty)
                            {
                                continue;
                            }
                            else
                            {
                                // Prase the annotation name to two parts.
                                string[] splitedStr = record.Split('@');

                                // If the string before the sign "@" are the same with property name, this rule will passed.
                                if (jProp.Name == splitedStr[0])
                                {
                                    passed = true;
                                    record = string.Empty;
                                    continue;
                                }
                                else
                                {
                                    passed = false;
                                    info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return(passed);
        }
        /// <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;

            info = null;
            bool isDefinedInMetadata     = false;
            bool isAnnotatingProNext     = false;
            bool isAnnotatingProPrevious = false;

            JObject allobject;

            context.ResponsePayload.TryToJObject(out allobject);

            // Access the metadata document and get the node which will be used.
            string   xpath         = string.Format(@"//*[local-name()='EntityType']/*[local-name()='Property']", context.EntityTypeShortName);
            XElement metadata      = XElement.Parse(context.MetadataDocument);
            var      propNodesList = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance);

            // If PayloadType is Feed, verify as below, else is Entry.
            if (context.PayloadType.Equals(RuleEngine.PayloadType.Feed))
            {
                var entries = JsonParserHelper.GetEntries(allobject);
                foreach (JObject entry in entries)
                {
                    var jProps = entry.Children();

                    foreach (JProperty jProp in jProps)
                    {
                        // Whether json property is annotation.
                        if (JsonSchemaHelper.IsAnnotation(jProp.Name) && !jProp.Name.StartsWith("@"))
                        {
                            if (jProp.Name.Contains("@"))
                            {
                                // Parse the annotation name to two parts.
                                string annotatedPro = jProp.Name.Remove(jProp.Name.LastIndexOf("@"), (jProp.Name.Length - jProp.Name.LastIndexOf("@")));

                                JProperty nextJProperty     = ((JProperty)jProp.Next);
                                JProperty previousJProperty = ((JProperty)jProp.Previous);

                                // Compare the navigation properties of navigation links,
                                foreach (var propNode in propNodesList)
                                {
                                    if (propNode.Attribute("Name").ToString().Contains(annotatedPro))
                                    {
                                        isDefinedInMetadata = true;
                                        break;
                                    }
                                    else
                                    {
                                        isDefinedInMetadata = false;
                                    }
                                }

                                // Whether the property defined in metadata.
                                if (isDefinedInMetadata && nextJProperty != null)
                                {
                                    if (nextJProperty.Name.ToString().Equals(annotatedPro))
                                    {
                                        if ((nextJProperty.Value.Type == JTokenType.Array) || (nextJProperty.Value.Type != JTokenType.Object))
                                        {
                                            isAnnotatingProNext = true;
                                        }
                                        else if ((nextJProperty.Value.Type == JTokenType.Object))
                                        {
                                            if ((context.Version == ODataVersion.V3) && (jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geography") || jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else if ((context.Version == ODataVersion.V4) && (jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geography") || jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else
                                            {
                                                isAnnotatingProNext = false;
                                            }
                                        }
                                    }
                                    else if (previousJProperty.Name.ToString().Equals(annotatedPro))
                                    {
                                        if ((previousJProperty.Value.Type == JTokenType.Array) || (nextJProperty.Value.Type != JTokenType.Object))
                                        {
                                            isAnnotatingProPrevious = true;
                                        }
                                        else if ((previousJProperty.Value.Type == JTokenType.Object))
                                        {
                                            if ((context.Version == ODataVersion.V3) && (jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geography") || jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else if ((context.Version == ODataVersion.V4) && (jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geography") || jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else
                                            {
                                                isAnnotatingProNext = false;
                                            }
                                        }
                                    }

                                    // If isAnnotatingProNext or isAnnotatingProPrevious is true, it means the name and value of annotation is not null.
                                    if (isAnnotatingProNext || isAnnotatingProPrevious)
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                if (allobject != null)
                {
                    var jProps = allobject.Children();

                    // New a string to record the annotation name.
                    string recordName  = string.Empty;
                    string recordValue = string.Empty;

                    foreach (JProperty jProp in jProps)
                    {
                        // Whether json property is annotation.
                        if (JsonSchemaHelper.IsAnnotation(jProp.Name) && !jProp.Name.StartsWith("@"))
                        {
                            if (jProp.Name.Contains("@"))
                            {
                                // Prase the annotation name to two parts.
                                string annotatedPro = jProp.Name.Remove(jProp.Name.LastIndexOf("@"), (jProp.Name.Length - jProp.Name.LastIndexOf("@")));

                                JProperty nextJProperty     = ((JProperty)jProp.Next);
                                JProperty previousJProperty = ((JProperty)jProp.Previous);

                                // Compare the navigation properties of navigation links,
                                foreach (var propNode in propNodesList)
                                {
                                    if (propNode.Attribute("Name").ToString().Contains(annotatedPro))
                                    {
                                        isDefinedInMetadata = true;
                                        break;
                                    }
                                    else
                                    {
                                        isDefinedInMetadata = false;
                                    }
                                }

                                // Whether the property defined in metadata.
                                if (isDefinedInMetadata && nextJProperty != null)
                                {
                                    if (nextJProperty.Name.ToString().Equals(annotatedPro))
                                    {
                                        if ((nextJProperty.Value.Type == JTokenType.Array) || (nextJProperty.Value.Type != JTokenType.Object))
                                        {
                                            isAnnotatingProNext = true;
                                        }
                                        else if ((nextJProperty.Value.Type == JTokenType.Object))
                                        {
                                            if ((context.Version == ODataVersion.V3) && (jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geography") || jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else if ((context.Version == ODataVersion.V4) && (jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geography") || jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else
                                            {
                                                isAnnotatingProNext = false;
                                            }
                                        }
                                    }
                                    else if (previousJProperty.Name.ToString().Equals(annotatedPro))
                                    {
                                        if ((previousJProperty.Value.Type == JTokenType.Array) || (nextJProperty.Value.Type != JTokenType.Object))
                                        {
                                            isAnnotatingProPrevious = true;
                                        }
                                        else if ((previousJProperty.Value.Type == JTokenType.Object))
                                        {
                                            if ((context.Version == ODataVersion.V3) && (jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geography") || jProp.Value.ToString().StripOffDoubleQuotes().StartsWith("Edm.Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else if ((context.Version == ODataVersion.V4) && (jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geography") || jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#').StartsWith("Geometry")))
                                            {
                                                isAnnotatingProNext = true;
                                            }
                                            else
                                            {
                                                isAnnotatingProNext = false;
                                            }
                                        }
                                    }

                                    // If isAnnotatingProNext or isAnnotatingProPrevious is true, it means the name and value of annotation is not null.
                                    if (isAnnotatingProNext || isAnnotatingProPrevious)
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (passed == false)
            {
                info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
            }

            return(passed);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            info = null;
            var svcStatus = ServiceStatus.GetInstance();

            string url    = svcStatus.RootURL.TrimEnd('/');
            var    detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);

            JObject serviceJO;

            svcStatus.ServiceDocument.TryToJObject(out serviceJO);

            JArray serviceOA = JsonParserHelper.GetEntries(serviceJO);

            string   xpath          = string.Format(@"//*[local-name()='FunctionImport']");
            XElement md             = XElement.Parse(svcStatus.MetadataDocument);
            XElement functionImport = md.XPathSelectElement(xpath, ODataNamespaceManager.Instance);

            if (functionImport == null)
            {
                detail.ErrorMessage = "The service has no function Import.";
                info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                return(passed);
            }

            string functionShortName = functionImport.Attribute("Function").Value.GetLastSegment();

            XElement function = md.XPathSelectElement(string.Format(@"//*[local-name()='Function' and @Name='{0}' and not (@IsBound='true')]", functionShortName), ODataNamespaceManager.Instance);

            string parameterString = string.Empty;
            List <KeyValuePair <string, string> > paralist = new List <KeyValuePair <string, string> >();

            if (function != null)
            {
                IEnumerable <XElement> parameters = function.XPathSelectElements(@"./*[local-name()='Parameter' and @Nullable='false']");
                foreach (XElement pa in parameters)
                {
                    string value = string.Empty;

                    if (EdmTypeManager.IsEdmSimpleType(pa.Attribute("Type").Value))
                    {
                        IEdmType typeInterface = EdmTypeManager.GetEdmType(pa.Attribute("Type").Value);
                        if (typeInterface != null)
                        {
                            value = typeInterface.GetXmlValueTemplate();
                        }
                    }

                    if (!string.IsNullOrEmpty(value))
                    {
                        KeyValuePair <string, string> kv = new KeyValuePair <string, string>(pa.Attribute("Name").Value, value);
                        paralist.Add(kv);
                    }
                    else
                    {
                        detail.ErrorMessage = "Parameter type is not supported by this test.";
                        info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                        return(passed);
                    }
                }
            }

            for (int i = 0; i < paralist.Count; i++)
            {
                if (i == 0)
                {
                    parameterString = "(" + paralist[i].Key + "=" + paralist[i].Value;
                }
                else if (i < paralist.Count - 1)
                {
                    parameterString += "," + paralist[i].Key + "=" + paralist[i].Value;
                }
                else if (i == paralist.Count - 1)
                {
                    parameterString += "," + paralist[i].Key + "=" + paralist[i].Value + ")";
                }
            }

            url += @"/" + functionShortName + parameterString;

            Response response = WebHelper.GetEntity(url);

            if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.NoContent)
            {
                detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);
                passed = true;
            }
            else
            {
                passed = false;
            }

            info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
            return(passed);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature 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 = null;

            List <string> entitySetURLs = MetadataHelper.GetEntitySetURLs();

            foreach (string entitySetUrl in entitySetURLs)
            {
                string entityTypeShortName = entitySetUrl.MapEntitySetNameToEntityTypeShortName();
                Tuple <string, string> key = MetadataHelper.GetKeyProperty(entityTypeShortName);
                Response resp = WebHelper.Get(new Uri(context.ServiceBaseUri + "/" + entitySetUrl), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                if (null == resp || HttpStatusCode.OK != resp.StatusCode)
                {
                    continue;
                }

                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);

                if (feed == null || JTokenType.Object != feed.Type)
                {
                    continue;
                }

                JArray entities = JsonParserHelper.GetEntries(feed);
                string identity = entities[0][key.Item1].ToString();

                string url = context.ServiceBaseUri + "/" + entitySetUrl + "(" + (key.Item2.Equals("Edm.String") ? "\'" + identity + "\'" : identity) + ")";

                resp   = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, "");

                if (null == resp || HttpStatusCode.OK != resp.StatusCode)
                {
                    passed = false; break;
                }

                resp.ResponsePayload.TryToJObject(out feed);

                if (feed == null || JTokenType.Object != feed.Type)
                {
                    passed = false; break;
                }

                JArray entity = JsonParserHelper.GetEntries(feed);

                if (feed[key.Item1] != null && feed[key.Item1].ToString().Equals(identity))
                {
                    passed = true; break;
                }
                passed = false; break;
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
Exemplo n.º 8
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;

            info = null;

            JObject entry;

            context.ResponsePayload.TryToJObject(out entry);

            if (entry != null && entry.Type == JTokenType.Object && context.OdataMetadataType == ODataMetadataType.MinOnly)
            {
                JsonParserHelper.ClearProperyValsList();
                List <JToken> odataMetadataVals = JsonParserHelper.GetSpecifiedPropertyValsFromEntryPayload(entry, Constants.OdataV4JsonIdentity, MatchType.Equal);

                JsonParserHelper.ClearProperyValsList();
                List <JToken> associationLinkVals = JsonParserHelper.GetSpecifiedPropertyValsFromEntryPayload(entry, Constants.OdataAssociationLinkPropertyNameSuffix, MatchType.Contained);

                string url = string.Empty;

                if (odataMetadataVals.Count == 1)
                {
                    string odataMetadataValStr = odataMetadataVals[0].ToString().Trim('"');
                    int    index = odataMetadataValStr.IndexOf(Constants.Metadata);
                    url = odataMetadataValStr.Remove(index, odataMetadataValStr.Length - index);

                    foreach (var associationLinkVal in associationLinkVals)
                    {
                        if (Uri.IsWellFormedUriString(associationLinkVal.ToString().Trim('"'), UriKind.Relative))
                        {
                            url += associationLinkVal.ToString().Trim('"') + @"/$ref";
                        }
                        else
                        {
                            url = associationLinkVal.ToString().Trim('"');
                        }

                        Uri uri = new Uri(url, UriKind.RelativeOrAbsolute);

                        // Send a request with "application/json;odata=minimalmetadata" in Accept header.
                        string   acceptHeader = Constants.V4AcceptHeaderJsonMinimalMetadata;
                        Response response     = WebHelper.Get(uri, acceptHeader, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                        // If response's status code is not equal to 200, it will indicate the url cannot be computed.
                        if (response.StatusCode != HttpStatusCode.OK)
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                            break;
                        }
                    }
                }
            }

            return(passed);
        }
        /// <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;

            info = null;
            var serviceStatus = ServiceStatus.GetInstance();
            var detail        = new ExtensionRuleResultDetail(this.Name);

            string[] entitySetUrls       = ContextHelper.GetFeeds(serviceStatus.ServiceDocument, this.PayloadFormat.Value).ToArray();
            string   entitySetName       = entitySetUrls.First().MapEntitySetURLToEntitySetName();
            string   entityTypeShortName = entitySetName.MapEntitySetNameToEntityTypeShortName();
            string   url      = string.Format("{0}/{1}", serviceStatus.RootURL.TrimEnd('/'), entitySetName);
            Response response = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, null);

            detail                    = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty, response);
            detail.URI                = url;
            detail.ResponsePayload    = response.ResponsePayload;
            detail.ResponseHeaders    = response.ResponseHeaders;
            detail.ResponseStatusCode = response.StatusCode.ToString();
            detail.HTTPMethod         = "GET";

            if (response.StatusCode != HttpStatusCode.OK)
            {
                detail.ErrorMessage = "HTTP Error was returned:  " + response.StatusCode.ToString() + " with Reponse Body:  " + response.ResponsePayload;
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }



            JObject feed;

            response.ResponsePayload.TryToJObject(out feed);
            var entries  = JsonParserHelper.GetEntries(feed);
            var entry    = entries.First();
            var entryUrl = string.Empty;

            if (null != entry[Constants.V4OdataId])
            {
                entryUrl = entry[Constants.V4OdataId].ToString().TrimEnd('\\');
            }
            else
            {
                detail.ErrorMessage = "Cannot get the entity-id from the current entity.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }

            try
            {
                Uri testuri = new Uri(entryUrl);
            }
            catch
            {
                detail.ErrorMessage = "The entity URL provided by @odata.id is not a valid URL.  This is what was returned:  " + entryUrl;
                info   = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);
                passed = false;
                return(passed);
            }


            // Use the XPath query language to access the metadata document and get all NavigationProperty.
            XElement metadata                = XElement.Parse(serviceStatus.MetadataDocument);
            string   xpath                   = string.Format(@"//*[local-name()='EntityType' and @Name='{0}']/*[local-name()='Property']", entityTypeShortName);
            IEnumerable <XElement> props     = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance);
            List <string>          propNames = new List <string>();

            foreach (var prop in props)
            {
                propNames.Add(prop.Attribute("Name").Value);
            }

            url                       = string.Format("{0}/{1}/$value", entryUrl.TrimEnd('/'), propNames[0]);
            response                  = WebHelper.Get(url, null, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail                    = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty, response);
            detail.URI                = url;
            detail.ResponsePayload    = response.ResponsePayload;
            detail.ResponseHeaders    = response.ResponseHeaders;
            detail.HTTPMethod         = "GET";
            detail.ResponseStatusCode = response.StatusCode.ToString();
            // Get the value of propNames[0] property in entry payload and verify whether this value is equal to /$value payload value.
            if (response.StatusCode == HttpStatusCode.OK && entry[propNames[0]].ToString().Equals(response.ResponsePayload))
            {
                passed = true;
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support '/$value' segment.";
            }

            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);
            return(passed);
        }
        /// <summary>
        /// Verify Entry.Core.4063
        /// </summary>
        /// <param name="context">Service context</param>
        /// <param name="info">out parameter to return violation information when rule fail</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;

            info = null;

            JObject entry;

            context.ResponsePayload.TryToJObject(out entry);

            if (entry != null && entry.Type == JTokenType.Object)
            {
                if (ODataVersion.V3 == context.Version)
                {
                    JsonParserHelper.ClearPropertyNamesList();
                    List <string> odataMediaAnnotations = JsonParserHelper.GetSpecifiedPropertyNamesFromEntryPayload(entry, Constants.OdataMedia, MatchType.Contained);

                    if (0 < odataMediaAnnotations.Count)
                    {
                        var temp = from o in odataMediaAnnotations where o.Contains(Constants.OdataMediaEtag) select o;

                        if (0 < temp.Count())
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                        }
                    }
                }
                else if (ODataVersion.V4 == context.Version)
                {
                    JsonParserHelper.ClearPropertyNamesList();
                    List <string> odataMediaAnnotations = JsonParserHelper.GetSpecifiedPropertyNamesFromEntryPayload(entry, Constants.V4OdataMedia, MatchType.Contained);

                    if (0 < odataMediaAnnotations.Count)
                    {
                        var temp = from o in odataMediaAnnotations where o.Contains(Constants.V4OdataMediaEtag) select o;

                        if (0 < temp.Count())
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                        }
                    }
                }
            }

            return(passed);
        }
        /// <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;
            info = null;
            JObject allobject;
            context.ResponsePayload.TryToJObject(out allobject);

            // If PayloadType is Feed, verify as below, eles is Entry.
            if (context.PayloadType.Equals(RuleEngine.PayloadType.Feed))
            {
                var entries = JsonParserHelper.GetEntries(allobject);

                foreach (JObject entry in entries)
                {
                    var o = (JObject)entry;
                    var jProps = o.Children();

                    foreach (JProperty jProp in jProps)
                    {
                        if (JsonSchemaHelper.IsAnnotation(jProp.Name))
                        {
                            if (Regex.IsMatch(jProp.Name, @"[a-zA-Z0-9]+\.[a-zA-Z0-9]+"))
                            {
                                passed = true;
                                continue;
                            }
                            else
                            {
                                passed = false;
                                info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                return passed;
                            }
                        }
                    }
                }
            }
            else
            {
                var o = (JObject)allobject;
                var jProps = o.Children();

                foreach (JProperty jProp in jProps)
                {
                    if (JsonSchemaHelper.IsAnnotation(jProp.Name))
                    {
                        if (Regex.IsMatch(jProp.Name, @"[a-zA-Z0-9]+\.[a-zA-Z0-9]+"))
                        {
                            passed = true;
                            continue;
                        }
                        else
                        {
                            passed = false;
                            info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                            return passed;
                        }
                    }
                }
            }

            return passed;
        }
Exemplo n.º 12
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            Response resp = WebHelper.Get(new Uri(context.ServiceBaseUri + "/$all"), Constants.AcceptHeaderJson,
                                          RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, context.ServiceBaseUri + "/$all",
                                                                             HttpMethod.Get, context.RequestHeaders.ToString());

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                return(passed = false);
            }

            JObject allFeed;

            resp.ResponsePayload.TryToJObject(out allFeed);

            if (allFeed == null || JTokenType.Object != allFeed.Type)
            {
                return(passed = false);
            }

            JArray allEntities = JsonParserHelper.GetEntries(allFeed);

            passed = true;

            List <string> entitySetURLs = MetadataHelper.GetEntitySetURLs();

            foreach (string entitySetUrl in entitySetURLs)
            {
                string entityTypeShortName = entitySetUrl.MapEntitySetNameToEntityTypeShortName();
                Tuple <string, string> key = MetadataHelper.GetKeyProperty(entityTypeShortName);

                resp = WebHelper.Get(new Uri(context.ServiceBaseUri + "/" + entitySetUrl),
                                     Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                if (null == resp || HttpStatusCode.OK != resp.StatusCode)
                {
                    continue;
                }

                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);

                if (feed == null || JTokenType.Object != feed.Type)
                {
                    continue;
                }

                JArray entities = JsonParserHelper.GetEntries(feed);
                foreach (JToken entity in entities)
                {
                    if (!Find(allEntities, key.Item1, entity[key].ToString()))
                    {
                        return(passed = false);
                    }
                }
            }

            return(passed);
        }
        /// <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;

            info = null;

            JObject entry;

            context.ResponsePayload.TryToJObject(out entry);

            List <string> expandProps = ODataUriAnalyzer.GetQueryOptionValsFromUrl(context.Destination.ToString(), @"expand");

            if (entry != null && entry.Type == JTokenType.Object && expandProps.Count > 0)
            {
                var props = entry.Children();

                foreach (JProperty prop in props)
                {
                    if (expandProps.Contains(prop.Name))
                    {
                        passed = null;

                        if (prop.Value.Type == JTokenType.Object)
                        {
                            JObject jObj = prop.Value as JObject;
                            JsonParserHelper.ClearPropertiesList();

                            if (JsonParserHelper.GetSpecifiedPropertiesFromEntryPayload(jObj, Constants.OdataV4JsonIdentity).Count > 0)
                            {
                                passed = true;
                                break;
                            }
                            else
                            {
                                passed = false;
                            }
                        }
                        else if (prop.Value.Type == JTokenType.Array)
                        {
                            JArray jArr = prop.Value as JArray;

                            if (JsonParserHelper.GetSpecifiedPropertiesFromFeedPayload(jArr, Constants.OdataV4JsonIdentity).Count > 0)
                            {
                                passed = true;
                                break;
                            }
                            else
                            {
                                passed = false;
                            }
                        }
                    }
                }

                if (passed == false)
                {
                    info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                }
            }

            return(passed);
        }
        /// <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 = true;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);

            var payloadFormat = context.ServiceDocument.GetFormatFromPayload();

            string[] feeds            = ContextHelper.GetFeeds(context.ServiceDocument, payloadFormat).ToArray();
            string   entitySetName    = feeds.First().MapEntitySetURLToEntitySetName();
            Uri      firstFeedFullUrl = new Uri(string.Format("{0}/{1}", context.DestinationBasePath.TrimEnd('/'), entitySetName));
            Response response         = WebHelper.Get(firstFeedFullUrl, Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail = new ExtensionRuleResultDetail(this.Name, firstFeedFullUrl.AbsoluteUri, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                JObject feed;
                response.ResponsePayload.TryToJObject(out feed);
                var entries = JsonParserHelper.GetEntries(feed);

                foreach (JObject entry in entries)
                {
                    var jProps = entry.Children();

                    foreach (JProperty jProp in jProps)
                    {
                        if (JsonSchemaHelper.IsAnnotation(jProp.Name) && jProp.Name.EndsWith("@" + Constants.OdataType) && !jProp.Name.StartsWith("@" + Constants.OdataType))
                        {
                            string typeValue = jProp.Value.ToString().TrimStart('#');

                            if (typeValue.Contains("Collection("))
                            {
                                typeValue = typeValue.Substring(typeValue.IndexOf("(") + 1, typeValue.LastIndexOf(")") - typeValue.IndexOf("(") - 1);
                            }

                            // Don't contains dot means it is primitive value or collection.
                            if (!typeValue.Contains("."))
                            {
                                if (PrimitiveDataTypes.NonQualifiedTypes.Contains(typeValue))
                                {
                                    passed = true;
                                }
                                else
                                {
                                    passed = false;
                                    detail.ErrorMessage += string.Format("The type {0} is not defined in OData-CSDL.", typeValue);
                                    break;
                                }
                            }
                        }
                    }

                    if (passed == false)  // Break to foreach all if anyone of them cannot match the rule
                    {
                        break;
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = string.Format(Constants.ErrorURI, "GET", firstFeedFullUrl.AbsoluteUri, "failed");
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <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);
        }
Exemplo n.º 16
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;

            info = null;
            var serviceStatus = ServiceStatus.GetInstance();
            var detail        = new ExtensionRuleResultDetail(this.Name);
            List <EntityTypeElement> entityTypes = MetadataHelper.GetEntityTypeInheritance(serviceStatus.MetadataDocument, serviceStatus.ServiceDocument);

            if (!entityTypes.Any())
            {
                detail.ErrorMessage = "Cannot find a derived entity type from metadata.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }

            string entityTypeShortName = string.Empty;
            string entitySetUrl        = string.Empty;
            string derivedTypeFullName = string.Empty;

            foreach (var entityType in entityTypes)
            {
                entityTypeShortName = entityType.EntityTypeShortName;
                derivedTypeFullName = string.Format("{0}.{1}", entityType.EntityTypeNamespace, entityTypeShortName);
                if (!string.IsNullOrEmpty(entityType.EntitySetName))
                {
                    entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
                }
                else
                {
                    continue;
                }

                if (string.IsNullOrEmpty(entityTypeShortName) ||
                    string.IsNullOrEmpty(entitySetUrl) ||
                    string.IsNullOrEmpty(derivedTypeFullName))
                {
                    continue;
                }
            }

            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detail.ErrorMessage = string.Format("Cannot find the entity-set URL which is matched with {0}", entityTypeShortName);
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }

            string url  = string.Format("{0}/{1}/{2}", serviceStatus.RootURL, entitySetUrl, derivedTypeFullName);
            var    resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders);

            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp);
            if (resp != null && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);

                if (feed != null && JTokenType.Object == feed.Type)
                {
                    var entities            = JsonParserHelper.GetEntries(feed).ToList();
                    var appropriateEntities = entities.FindAll(e => e[Constants.V4OdataType].ToString().Contains(derivedTypeFullName)).Select(e => e);

                    if (entities.Count == appropriateEntities.Count())
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;

                        detail.ErrorMessage = "The service does not execute an acculate result on casting to a derived type.";
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
            }

            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);
            return(passed);
        }
Exemplo n.º 17
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;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var filterRestrictions           = AnnotationsHelper.GetFilterRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(filterRestrictions.Item1) ||
                null == filterRestrictions.Item2 || !filterRestrictions.Item2.Any())
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $filter system query options in the service.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet         = filterRestrictions.Item1;
            string primitivePropName = filterRestrictions.Item2.First().PropertyName;
            string primitivePropType = filterRestrictions.Item2.First().PropertyType;

            string url  = string.Format("{0}/{1}", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet);
            var    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);

            if (feed != null && JTokenType.Object == feed.Type)
            {
                var    entities = JsonParserHelper.GetEntries(feed);
                string propVal  = "Edm.String" == primitivePropType?string.Format("'{0}'", entities[0][primitivePropName].ToString()) : entities[0][primitivePropName].ToString();

                //var req = WebRequest.Create(string.Format("{0}?$filter={1} eq @test1&@test1={2}", url, primitivePropName, propVal)) as HttpWebRequest;;
                //resp = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize); //REPLACE HEADER

                string requri = string.Format("{0}?$filter={1} eq @test1&@test1={2}", url, primitivePropName, propVal);
                resp   = WebHelper.Get(requri, null, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                detail = new ExtensionRuleResultDetail(this.Name, requri, "GET", string.Empty, resp);

                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    JObject feed1;
                    resp.ResponsePayload.TryToJObject(out feed1);

                    if (feed1 != null && JTokenType.Object == feed1.Type)
                    {
                        var entities1 = JsonParserHelper.GetEntries(feed1).ToList();
                        var temp      = entities1.FindAll(en => propVal == en[primitivePropName].ToString()).Select(en => en);

                        if (entities1.Count() == temp.Count())
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            detail.ErrorMessage = "The service does not execute an accurate result on aliases in $filter expressions.";
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                }
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature 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>();

            XmlDocument metadata = new XmlDocument();

            metadata.LoadXml(ServiceStatus.GetInstance().MetadataDocument);

            string      xPath     = "//*[local-name()='EnumType']";
            XmlNodeList enumTypes = metadata.SelectNodes(xPath, ODataNamespaceManager.Instance);

            foreach (XmlNode enumType in enumTypes)
            {
                xPath = "//*[local-name()='EntityType']";
                XmlNodeList entityTypes = metadata.SelectNodes(xPath, ODataNamespaceManager.Instance);

                List <string> typeNames = new List <string> {
                    enumType.ParentNode.Attributes["Namespace"].Value + "." + enumType.Attributes["Name"].Value
                };

                if (enumType.ParentNode.Attributes["Alias"] != null)
                {
                    typeNames.Add(enumType.ParentNode.Attributes["Alias"].Value + "." + enumType.Attributes["Name"].Value);
                }
                List <string> properties = null;
                foreach (XmlNode entityType in entityTypes)
                {
                    properties = MetadataHelper.GetPropertiesWithSpecifiedTypeFromEntityType(entityType.Attributes["Name"].Value,
                                                                                             ServiceStatus.GetInstance().MetadataDocument, typeNames);
                    if (properties == null || properties.Count < 1)
                    {
                        continue;
                    }

                    string entitySetUrl = entityType.Attributes["Name"].Value.MapEntityTypeShortNameToEntitySetName();

                    Response resp = WebHelper.Get(new Uri(context.ServiceBaseUri + "/" + entitySetUrl), Constants.AcceptHeaderJson,
                                                  RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                    if (null == resp || HttpStatusCode.OK != resp.StatusCode)
                    {
                        continue;
                    }

                    JObject feed;
                    resp.ResponsePayload.TryToJObject(out feed);

                    if (feed == null || JTokenType.Object != feed.Type)
                    {
                        continue;
                    }

                    JArray entities  = JsonParserHelper.GetEntries(feed);
                    string enumValue = entities[0][properties[0].Split(',')[0]].ToString();

                    string url = context.ServiceBaseUri + "/" + entitySetUrl + "?$filter=" + properties[0].Split(',')[0] + " has " +
                                 typeNames[0] + "\'" + enumValue + "\'";

                    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                    details.Add(new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, ""));

                    if (null == resp || HttpStatusCode.OK != resp.StatusCode)
                    {
                        passed = false; break;
                    }

                    resp.ResponsePayload.TryToJObject(out feed);

                    if (feed == null || JTokenType.Object != feed.Type)
                    {
                        passed = false; break;
                    }

                    entities = JsonParserHelper.GetEntries(feed);

                    foreach (JToken entity in entities)
                    {
                        Dictionary <string, ushort> map = buildEnumMapping(enumType);
                        ushort expect, actual;
                        if (!mappingToUshort(map, enumValue, out expect) || !mappingToUshort(map, entity[properties[0].Split(',')[0]].ToString(), out actual))
                        {
                            passed = false; break;
                        }

                        if (enumType.Attributes["IsFlags"] != null && enumType.Attributes["IsFlags"].Value.Equals("true", StringComparison.OrdinalIgnoreCase))
                        {
                            if ((expect & actual) != expect)
                            {
                                passed = false; break;
                            }
                        }
                        else
                        {
                            if (expect != actual)
                            {
                                passed = false; break;
                            }
                        }

                        //if (!entity[properties[0].Split(',')[0]].ToString().Equals(enumValue))
                        //{ passed = false; break; }
                    }

                    if (passed.HasValue && !passed.Value)
                    {
                        break;
                    }
                    else
                    {
                        passed = true; break;
                    }
                }

                if (passed != null)
                {
                    break;
                }
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);
            return(passed);
        }
Exemplo n.º 19
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;

            info = null;

            string odataTypeAnnotation = Constants.V4OdataType;

            if (context.Version == ODataVersion.V3)
            {
                odataTypeAnnotation = Constants.OdataType;
            }

            if (context.PayloadType == RuleEngine.PayloadType.Entry)
            {
                JObject entry;
                context.ResponsePayload.TryToJObject(out entry);

                if (entry != null && entry.Type == JTokenType.Object)
                {
                    var jProps = entry.Children();

                    foreach (JProperty jProp in jProps)
                    {
                        if (jProp.Name.Equals(odataTypeAnnotation))
                        {
                            passed = null;

                            if (Uri.IsWellFormedUriString(jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#'), UriKind.Relative))
                            {
                                passed = true;
                            }
                            else
                            {
                                passed = false;
                                info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                break;
                            }
                        }
                    }
                }
            }
            else if (context.PayloadType == RuleEngine.PayloadType.Feed)
            {
                JObject feed;
                context.ResponsePayload.TryToJObject(out feed);
                var entries = JsonParserHelper.GetEntries(feed);

                foreach (JObject entry in entries)
                {
                    if (entry != null && entry.Type == JTokenType.Object)
                    {
                        var jProps = entry.Children();

                        foreach (JProperty jProp in jProps)
                        {
                            if (jProp.Name.Equals(odataTypeAnnotation))
                            {
                                passed = null;

                                if (Uri.IsWellFormedUriString(jProp.Value.ToString().StripOffDoubleQuotes().TrimStart('#'), UriKind.Relative))
                                {
                                    passed = true;
                                }
                                else
                                {
                                    passed = false;
                                    info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return(passed);
        }
        /// <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;

            info = null;
            ServiceStatus serviceStatus    = ServiceStatus.GetInstance();
            TermDocuments termDocs         = TermDocuments.GetInstance();
            DataFactory   dFactory         = DataFactory.Instance();
            var           detail1          = new ExtensionRuleResultDetail(this.Name);
            var           detail2          = new ExtensionRuleResultDetail(this.Name);
            var           detail3          = new ExtensionRuleResultDetail(this.Name);
            List <string> keyPropertyTypes = new List <string>()
            {
                "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String"
            };
            List <EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.SingleValued).ToList();

            if (null == entityTypeElements || 0 == entityTypeElements.Count())
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with Int32/Int64/Int16/Guid/String key property, but there is no this entity type in metadata so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return(passed);
            }

            string entityTypeShortName = string.Empty;
            string entitySetName       = string.Empty;
            string entitySetUrl        = string.Empty;
            string navigPropName       = string.Empty;

            foreach (var en in entityTypeElements)
            {
                var matchEntity = en.EntitySetName.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc,
                                                                   new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                {
                    AnnotationsHelper.GetExpandRestrictions, AnnotationsHelper.GetInsertRestrictions
                    , AnnotationsHelper.GetDeleteRestrictions, AnnotationsHelper.GetUpdateRestrictions
                });

                if (!string.IsNullOrEmpty(matchEntity.Item1) &&
                    matchEntity.Item2 != null && matchEntity.Item2.Any() &&
                    matchEntity.Item3 != null && matchEntity.Item3.Any())
                {
                    entityTypeShortName = en.EntityTypeShortName;
                    entitySetName       = matchEntity.Item1;
                    entitySetUrl        = entitySetName.MapEntitySetNameToEntitySetURL();
                    foreach (NavigProperty np in matchEntity.Item3)
                    {
                        string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                        if (np.NavigationRoughType == NavigationRoughType.SingleValued && !nEntityTypeShortName.IsMediaType())
                        {
                            navigPropName = np.NavigationPropertyName;
                            break;
                        }
                    }
                }
                if (!string.IsNullOrEmpty(entitySetName) && !string.IsNullOrEmpty(navigPropName))
                {
                    break;
                }
            }

            if (string.IsNullOrEmpty(entitySetName) || string.IsNullOrEmpty(navigPropName))
            {
                detail1.ErrorMessage = "Cannot find the appropriate entity-set URL to verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return(passed);
            }

            string navigPropEntitySet = navigPropName.MapNavigationPropertyNameToEntitySetURL(entityTypeShortName);
            string url             = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var    additionalInfos = new List <AdditionalInfo>();
            var    reqData         = dFactory.ConstructInsertedEntityData(entitySetName, entityTypeShortName, new List <string>()
            {
                navigPropName
            }, out additionalInfos);
            string reqDataStr  = reqData.ToString();
            bool   isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var    resp        = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);

            detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode)
            {
                string entityId = additionalInfos.Last().EntityId;
                url     = serviceStatus.RootURL.TrimEnd('/') + @"/" + navigPropEntitySet;
                resp    = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders);
                detail2 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp);
                if (resp.StatusCode.HasValue && HttpStatusCode.OK == resp.StatusCode)
                {
                    JObject feed;
                    resp.ResponsePayload.TryToJObject(out feed);
                    var entities = JsonParserHelper.GetEntries(feed);
                    if (entities.Any())
                    {
                        string v4OdataIdVal = string.Empty;
                        if (entities.First[Constants.V4OdataId] != null)
                        {
                            v4OdataIdVal = entities.First[Constants.V4OdataId].ToString();
                        }

                        url        = string.Format("{0}/{1}/$ref", entityId, navigPropName);
                        reqDataStr =
                            @"{
                                """ + Constants.V4OdataId + @""" : """ + v4OdataIdVal + @"""
                        }";
                        bool hasEtag = additionalInfos.Last().HasEtag;
                        resp    = WebHelper.UpdateEntity(url, context.RequestHeaders, reqDataStr, HttpMethod.Put, hasEtag);
                        detail3 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Put, string.Empty, resp, string.Empty, reqDataStr);

                        if (null != resp && HttpStatusCode.NoContent == resp.StatusCode)
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            detail3.ErrorMessage = "PUT to $ref failed by above URI";
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Get entity failed from above URI.";
                }

                // Restore the service.
                var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos);
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "Created the new entity failed for above URI.";
            }

            var details = new List <ExtensionRuleResultDetail>()
            {
                detail1, detail2, detail3
            }.RemoveNullableDetails();

            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return(passed);
        }
Exemplo n.º 21
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;

            info = null;
            ServiceStatus             serviceStatus    = ServiceStatus.GetInstance();
            TermDocuments             termDocs         = TermDocuments.GetInstance();
            DataFactory               dFactory         = DataFactory.Instance();
            ExtensionRuleResultDetail detail1          = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail detail2          = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail detail3          = new ExtensionRuleResultDetail(this.Name);
            List <string>             keyPropertyTypes = new List <string>()
            {
                "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String"
            };
            List <EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.CollectionValued).ToList();

            if (entityTypeElements == null || entityTypeElements.Count == 0)
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with Int32/Int64/Int16/Guid/String key property, but there is no this entity type in metadata so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return(passed);
            }

            foreach (var et in entityTypeElements)
            {
                string navigPropName = null;
                string navigPropRelatedEntitySetUrl      = null;
                string navigPropRelatedEntityTypeKeyName = null;

                var matchEntity = et.EntitySetName.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc,
                                                                   new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                {
                    AnnotationsHelper.GetDeleteRestrictions, AnnotationsHelper.GetInsertRestrictions
                });

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

                foreach (var np in matchEntity.Item3)
                {
                    navigPropName = np.NavigationPropertyName;
                    string navigEntityTypeShortName     = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    List <NormalProperty> navigKeyProps = MetadataHelper.GetKeyProperties(serviceStatus.MetadataDocument, navigEntityTypeShortName).ToList();

                    if (navigKeyProps.Count == 1 && keyPropertyTypes.Contains(navigKeyProps[0].PropertyType))
                    {
                        navigPropRelatedEntitySetUrl      = navigEntityTypeShortName.MapEntityTypeShortNameToEntitySetURL();
                        navigPropRelatedEntityTypeKeyName = navigKeyProps[0].PropertyName;
                        break;
                    }
                }

                if (!string.IsNullOrEmpty(navigPropRelatedEntityTypeKeyName) && !string.IsNullOrEmpty(navigPropRelatedEntitySetUrl))
                {
                    string entitySetUrl    = et.EntitySetName.MapEntitySetNameToEntitySetURL();
                    string url             = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
                    var    additionalInfos = new List <AdditionalInfo>();
                    var    reqData         = dFactory.ConstructInsertedEntityData(et.EntitySetName, et.EntityTypeShortName, null, out additionalInfos);
                    string reqDataStr      = reqData.ToString();
                    bool   isMediaType     = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
                    var    resp            = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
                    detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

                    if (resp.StatusCode.HasValue && HttpStatusCode.Created == resp.StatusCode)
                    {
                        string entityId = additionalInfos.Last().EntityId;
                        url                        = serviceStatus.RootURL.TrimEnd('/') + @"/" + navigPropRelatedEntitySetUrl;
                        resp                       = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders);
                        detail2                    = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp, string.Empty, reqDataStr);
                        detail2.URI                = url;
                        detail2.ResponsePayload    = resp.ResponsePayload;
                        detail2.ResponseHeaders    = resp.ResponseHeaders;
                        detail2.HTTPMethod         = "GET";
                        detail2.ResponseStatusCode = resp.StatusCode.ToString();

                        if (null != resp && resp.StatusCode == HttpStatusCode.OK)
                        {
                            JObject feed;
                            resp.ResponsePayload.TryToJObject(out feed);
                            var entities = JsonParserHelper.GetEntries(feed);
                            if (entities.Count > 0)
                            {
                                // Use the HTTP method POST to link the navigation property for an entity.
                                reqDataStr = @"{""" + Constants.V4OdataId + @""" : """ + entities[0][Constants.V4OdataId].ToString() + @"""}";
                                url        = string.Format("{0}/{1}/$ref", entityId.TrimEnd('/'), navigPropName.TrimEnd('/'));
                                resp       = WebHelper.CreateEntity(url, reqDataStr);
                                detail3    = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
                                if (null != resp && resp.StatusCode == HttpStatusCode.NoContent)
                                {
                                    passed = true;
                                }
                                else
                                {
                                    passed = false;
                                    detail3.ErrorMessage = "Created the new entity failed for above URI.";
                                }
                            }
                        }
                        else
                        {
                            passed = false;
                            detail2.ErrorMessage = "Get entity failed from above URI.";
                        }

                        // Restore the service.
                        var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos);
                    }
                    else
                    {
                        passed = false;
                        detail1.ErrorMessage = "Created the new entity failed for above URI.";
                    }
                }

                if (false == passed)
                {
                    break;
                }
            }

            var details = new List <ExtensionRuleResultDetail>()
            {
                detail1, detail2, detail3
            }.RemoveNullableDetails();

            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return(passed);
        }
Exemplo n.º 22
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            info = null;
            ServiceStatus serviceStatus    = ServiceStatus.GetInstance();
            TermDocuments termDocs         = TermDocuments.GetInstance();
            DataFactory   dFactory         = DataFactory.Instance();
            var           detail           = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL, HttpMethod.Post, string.Empty);
            List <string> keyPropertyTypes = new List <string>()
            {
                "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String"
            };
            var entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None);

            if (null == entityTypeElements || 0 == entityTypeElements.Count())
            {
                detail.ErrorMessage = "To verify this rule it expects an entity type with Int32/Int64/Int16/Guid/String key property, but there is no this entity type in metadata so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }

            string entitySet = string.Empty;
            string navigProp = string.Empty;
            NavigationRoughType navigRoughType = NavigationRoughType.None;
            EntityTypeElement   entityType     = new EntityTypeElement();

            foreach (var en in entityTypeElements)
            {
                if (!string.IsNullOrEmpty(en.EntitySetName) && en.NavigationProperties.Count > 0)
                {
                    foreach (NavigProperty np in en.NavigationProperties)
                    {
                        string npTypeFullName = np.NavigationPropertyType.RemoveCollectionFlag();

                        XmlDocument metadata = new XmlDocument();
                        metadata.LoadXml(context.MetadataDocument);
                        if (context.ContainsExternalSchema)
                        {
                            metadata.LoadXml(context.MergedMetadataDocument);
                        }
                        XmlNode npXmlNode;
                        MetadataHelper.GetTypeNode(npTypeFullName, metadata, out npXmlNode);
                        if (!(npXmlNode.Attributes["HasStream"] != null ? npXmlNode.Attributes["HasStream"].Value.Equals("true") : false))
                        {
                            navigProp      = np.NavigationPropertyName;
                            navigRoughType = np.NavigationRoughType;
                            entityType     = en;
                            entitySet      = en.EntitySetName;
                            break;
                        }
                    }

                    if (!string.IsNullOrEmpty(entitySet))
                    {
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(entitySet) || string.IsNullOrEmpty(navigProp))
            {
                detail.ErrorMessage = "Cannot find an entity-set URL which can be execute the deep insert operation on it.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return(passed);
            }

            string url             = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySet;
            var    additionalInfos = new List <AdditionalInfo>();
            var    reqData         = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, new List <string>()
            {
                navigProp
            }, out additionalInfos);
            string reqDataStr  = reqData.ToString();
            bool   isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var    resp        = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);

            detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode)
            {
                var    mainEntityID = additionalInfos.Last().EntityId;
                string entityId     = additionalInfos.First().EntityId;

                string refUrl = mainEntityID.TrimEnd('/') + "/" + navigProp + "/$ref";
                string refEntitySetNameWithEnityID = entityId.Split('/').Last();
                string refEntityValue = serviceStatus.RootURL.TrimEnd('/') + @"/" + refEntitySetNameWithEnityID;
                string refEntityID    = refEntitySetNameWithEnityID.Contains("'") ? refEntitySetNameWithEnityID.Split('\'')[1] : refEntitySetNameWithEnityID.Split('(')[1].TrimEnd(')');

                resp   = WebHelper.GetEntity(mainEntityID);
                detail = new ExtensionRuleResultDetail(this.Name, mainEntityID, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                if (HttpStatusCode.OK == resp.StatusCode && additionalInfos.Count > 1)
                {
                    resp   = WebHelper.GetEntity(refUrl);
                    detail = new ExtensionRuleResultDetail(this.Name, refUrl, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                    if (HttpStatusCode.OK == resp.StatusCode)
                    {
                        JObject refPayload;
                        resp.ResponsePayload.TryToJObject(out refPayload);
                        bool created = false;

                        if (navigRoughType == NavigationRoughType.CollectionValued)
                        {
                            var entries = JsonParserHelper.GetEntries(refPayload);

                            if (entries != null)
                            {
                                foreach (JObject entry in entries)
                                {
                                    foreach (JProperty prop in entry.Children())
                                    {
                                        if (prop.Name.Equals(Constants.V4OdataId))
                                        {
                                            created = prop.Value.ToString().Contains(refEntityID);
                                            if (created)
                                            {
                                                passed = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            foreach (JProperty prop in refPayload.Children())
                            {
                                if (prop.Name.Equals(Constants.V4OdataId))
                                {
                                    created = prop.Value.ToString().Contains(refEntityID);
                                    if (created)
                                    {
                                        passed = true;
                                        break;
                                    }
                                }
                            }
                        }

                        if (!created)
                        {
                            passed = false;
                            detail.ErrorMessage = string.Format("The HTTP deep insert failed to add a reference to a collection-valued navigation property failed.");
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = "Can not get the created entity. ";
                }

                // Restore the service.
                var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos);
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "Created the new entity failed for above URI. ";
            }

            var details = new List <ExtensionRuleResultDetail>()
            {
                detail
            };

            info = new ExtensionRuleViolationInfo(new Uri(url), serviceStatus.ServiceDocument, details);

            return(passed);
        }
        /// <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;

            info = null;
            var    serviceStatus = ServiceStatus.GetInstance();
            var    detail1       = new ExtensionRuleResultDetail(this.Name);
            var    detail2       = new ExtensionRuleResultDetail(this.Name);
            var    detail3       = new ExtensionRuleResultDetail(this.Name);
            string feedUrl       = string.Empty;
            string entityUrl     = string.Empty;

            KeyValuePair <string, IEnumerable <string> > entityUrls;

            if (JsonParserHelper.GetBatchSupportedEntityUrls(out entityUrls))
            {
                feedUrl   = string.Format("{0}/{1}", serviceStatus.RootURL, entityUrls.Key);
                entityUrl = entityUrls.Value.First();
            }

            string relativeUrl = new Uri(entityUrl).LocalPath;
            string host        = entityUrl.Remove(entityUrl.IndexOf(relativeUrl));

            string format1Request = string.Format(@"
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http 
Content-Transfer-Encoding:binary


GET {0} HTTP/1.1

--batch_36522ad7-fc75-4b56-8c71-56071383e77b--", entityUrl);

            string format2Request = string.Format(@"
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http 
Content-Transfer-Encoding:binary


GET {0} HTTP/1.1
Host: {1}

--batch_36522ad7-fc75-4b56-8c71-56071383e77b--", relativeUrl, host);

            string format3Reuqest = string.Format(@"
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http 
Content-Transfer-Encoding:binary


GET {0} HTTP/1.1

--batch_36522ad7-fc75-4b56-8c71-56071383e77b--", relativeUrl);

            string   boundary        = @"batch_36522ad7-fc75-4b56-8c71-56071383e77b";
            Response format1Response = WebHelper.BatchOperation(serviceStatus.RootURL.TrimEnd('/') + @"/", format1Request, boundary);

            detail1 = new ExtensionRuleResultDetail(this.Name, feedUrl + "/$batch", HttpMethod.Post, string.Empty, format1Response, string.Empty, format1Request);

            Response format2Response = WebHelper.BatchOperation(serviceStatus.RootURL.TrimEnd('/') + @"/", format2Request, boundary);

            detail2 = new ExtensionRuleResultDetail(this.Name, feedUrl + "/$batch", HttpMethod.Post, string.Empty, format2Response, string.Empty, format2Request);

            Response format3Response = WebHelper.BatchOperation(serviceStatus.RootURL.TrimEnd('/') + @"/", format3Reuqest, boundary);

            detail3 = new ExtensionRuleResultDetail(this.Name, feedUrl + "/$batch", HttpMethod.Post, string.Empty, format3Response, string.Empty, format3Reuqest);

            if (format1Response != null && !string.IsNullOrEmpty(format1Response.ResponsePayload))
            {
                if (!format1Response.ResponsePayload.Contains(@"HTTP/1.1 200 OK"))
                {
                    passed = false;
                    detail1.ErrorMessage = string.Format("Batch request failed by above URI.");
                }
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "No response returned from above URI.";
            }

            if (format2Response != null && !string.IsNullOrEmpty(format2Response.ResponsePayload))
            {
                if (!format2Response.ResponsePayload.Contains(@"HTTP/1.1 200 OK"))
                {
                    passed = false;
                    detail2.ErrorMessage = string.Format("Batch request failed by above URI and host : {0}. ", host);
                }
            }
            else
            {
                passed = false;
                detail2.ErrorMessage = string.Format("No response returned from above URI and host : {0}. ", host);
            }

            if (format3Response != null && !string.IsNullOrEmpty(format3Response.ResponsePayload))
            {
                if (!format3Response.ResponsePayload.Contains(@"HTTP/1.1 200 OK"))
                {
                    passed = false;
                    detail3.ErrorMessage = string.Format("Batch request failed by above URI and Batch request host : {0}.", host);
                }
            }
            else
            {
                passed = false;
                detail3.ErrorMessage = string.Format("No response returned from above URI and Batch request host : {0}. ", host);
            }

            if (passed == null)
            {
                passed = true;
            }

            List <ExtensionRuleResultDetail> details = new List <ExtensionRuleResultDetail>()
            {
                detail1, detail2, detail3
            };

            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return(passed);
        }
        /// <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 <string> vocDocs = new List <string>()
            {
                context.VocCapabilities, context.VocCore, context.VocMeasures
            };
            List <string> expectedTypes = new List <string>()
            {
                "Edm.String", "Edm.Int32", "Edm.Int16", "Edm.Single", "Edm.Double", "Edm.Boolean", "Edm.DateTimeOffset", "Edm.Guid"
            };
            List <ExtensionRuleResultDetail> details = new List <ExtensionRuleResultDetail>();
            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 navigPropName         = string.Empty;
            string primitivePropertyName = string.Empty;
            string primitivePropertyType = 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;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();
                    var    funcs = new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                    {
                        AnnotationsHelper.GetFilterRestrictions
                    };
                    var rest = nEntitySetName.GetRestrictions(context.MetadataDocument, context.VocCapabilities, funcs, expectedTypes);

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

                    navigPropName         = np.NavigationPropertyName;
                    primitivePropertyName = rest.Item2.First().PropertyName;
                    primitivePropertyType = rest.Item2.First().PropertyType;

                    break;
                }

                entitySet = r.Key;

                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(navigPropName))
            {
                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);
            }

            if (string.IsNullOrEmpty(primitivePropertyName) || string.IsNullOrEmpty(primitivePropertyType))
            {
                detail.ErrorMessage = "Cannot get an appropriate primitive property from navigation properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }
            string struri   = string.Format("{0}/{1}?$expand={2}", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigPropName);
            Uri    uri      = new Uri(struri);
            var    response = WebHelper.Get(uri, Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (HttpStatusCode.OK != response.StatusCode)
            {
                passed = false;
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(response.ResponsePayload);
                info = new ExtensionRuleViolationInfo(uri, response.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            response.ResponsePayload.TryToJObject(out feed);

            if (feed != null && JTokenType.Object == feed.Type)
            {
                var entities  = JsonParserHelper.GetEntries(feed);
                var navigProp = entities[0][navigPropName] as JArray;

                if (navigProp != null && navigProp.Count != 0)
                {
                    string propVal    = navigProp[0][primitivePropertyName].ToString();
                    string compareVal = propVal;

                    if (primitivePropertyType.Equals("Edm.String"))
                    {
                        compareVal = "'" + propVal + "'";
                    }

                    string url  = string.Format("{0}/{1}?$expand={2}($filter={3} eq {4})", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigPropName, primitivePropertyName, compareVal);
                    var    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                    detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

                    if (resp.StatusCode == HttpStatusCode.OK)
                    {
                        JObject jObj;
                        resp.ResponsePayload.TryToJObject(out jObj);

                        if (jObj != null && JTokenType.Object == jObj.Type)
                        {
                            var entries = JsonParserHelper.GetEntries(jObj).ToList();

                            foreach (var entry in entries)
                            {
                                if (entry[navigPropName] != null && ((JArray)entry[navigPropName]).Count == 0)
                                {
                                    continue;
                                }
                                else if (entry[navigPropName] != null && ((JArray)entry[navigPropName]).Count > 0)
                                {
                                    var temp = entry[navigPropName].ToList()
                                               .FindAll(en => propVal == en[primitivePropertyName].ToString())
                                               .Select(en => en);

                                    if (entry[navigPropName].ToList().Count == temp.Count())
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                        detail.ErrorMessage = string.Format("The service does not execute an accurate result on system query option '$filter' (Actual Value: {0}, Expected Value: {1}).", entry[navigPropName].ToList().Count, temp.Count());
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format("The service does not support system query option '$filter' on expanded entities.");
                    }
                }
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <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 expandRestrictions           = AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(expandRestrictions.Item1) ||
                null == expandRestrictions.Item3 || !expandRestrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet = expandRestrictions.Item1;
            string navigProp = expandRestrictions.Item3.First().NavigationPropertyName;
            string url       = string.Format("{0}/{1}", context.ServiceBaseUri, entitySet);
            var    resp      = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);
            var entities = JsonParserHelper.GetEntries(feed);

            if (null == entities || 0 == entities.Count)
            {
                detail.ErrorMessage = string.Format("The entity-set {0} has no entity.", entitySet);
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            var    entity    = entities.First;
            string entityURL = null != entity[Constants.V4OdataId] ? entity[Constants.V4OdataId].ToString() : string.Empty;

            if (string.IsNullOrEmpty(entityURL))
            {
                detail.ErrorMessage = "Cannot find the annotation @odata.id in the current entity.";
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            url    = string.Format("{0}?$expand={1}/$ref", entityURL, navigProp);
            resp   = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

            if (resp.StatusCode == HttpStatusCode.OK)
            {
                JObject entry;
                resp.ResponsePayload.TryToJObject(out entry);

                if (entry != null && JTokenType.Object == entry.Type)
                {
                    entity = entry[navigProp].First;
                    url    = entity[Constants.V4OdataId].ToString();
                    resp   = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                    if (resp.StatusCode == HttpStatusCode.OK)
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result, because the value of the annotation '@odata.id' is a bad link.";
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the '$ref' segment for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <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);
            Tuple <string, List <NormalProperty>, List <NavigProperty> > filterRestrictions = null;

            try
            {
                filterRestrictions = AnnotationsHelper.GetFilterRestrictions(context.MetadataDocument, context.VocCapabilities);
            }
            catch
            {
                detail1.ErrorMessage = "The Metadata is malformed.  Cannot determine the entity-set that supports $filter";
                info   = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);
                passed = false;
                return(passed);
            }


            if (string.IsNullOrEmpty(filterRestrictions.Item1) ||
                null == filterRestrictions.Item2 || !filterRestrictions.Item2.Any())
            {
                detail1.ErrorMessage = "Cannot find an appropriate entity-set which supports $filter system query options in the service.  Check for Malformed Metadata";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            string entitySet         = filterRestrictions.Item1;
            string primitivePropName = filterRestrictions.Item2.First().PropertyName;
            string primitivePropType = filterRestrictions.Item2.First().PropertyType;

            string url  = string.Format("{0}/{1}", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet);
            var    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail1.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);

            if (feed != null && JTokenType.Object == feed.Type)
            {
                var    entities = JsonParserHelper.GetEntries(feed);
                string propVal  = entities[0][primitivePropName].ToString();

                #region Equal operation on filter.
                bool?  isEqualOpValidation = null;
                string pattern             = "Edm.String" == primitivePropType ? "{0}/{1}?$filter={2} eq '{3}'" : "{0}/{1}?$filter={2} eq {3}";
                url     = string.Format(pattern, context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, primitivePropName, propVal);
                resp    = 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), resp);

                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    JObject feed1;
                    resp.ResponsePayload.TryToJObject(out feed1);

                    if (feed1 != null && JTokenType.Object == feed1.Type)
                    {
                        var entities1 = JsonParserHelper.GetEntries(feed1).ToList();
                        var temp      = entities1.FindAll(en => propVal == en[primitivePropName].ToString()).Select(en => en);

                        if (entities1.Count() == temp.Count())
                        {
                            isEqualOpValidation = true;
                        }
                        else
                        {
                            isEqualOpValidation  = false;
                            detail1.ErrorMessage = "The service does not execute an accurate result with system query option $filter eq.";
                            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                            return(isEqualOpValidation);
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail1.ErrorMessage = "Request failed with system query option $filter eq.";
                    info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                    return(passed);
                }
                #endregion

                #region NotEqual operation on filter.
                bool?isNotEqualOpValidation = null;
                pattern = "Edm.String" == primitivePropType ? "{0}/{1}?$filter={2} ne '{3}'" : "{0}/{1}?$filter={2} ne {3}";
                url     = string.Format(pattern, context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, primitivePropName, propVal);
                resp    = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                ExtensionRuleResultDetail detail2 = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    JObject feed2;
                    resp.ResponsePayload.TryToJObject(out feed2);

                    if (feed2 != null && JTokenType.Object == feed2.Type)
                    {
                        var entities2 = JsonParserHelper.GetEntries(feed2).ToList();
                        var temp      = entities2.FindAll(en => propVal != en[primitivePropName].ToString()).Select(en => en);

                        if (entities2.Count() == temp.Count())
                        {
                            isNotEqualOpValidation = true;
                        }
                        else
                        {
                            isNotEqualOpValidation = false;
                            detail2.ErrorMessage   = "The service does not execute an accurate result with system query option $filter ne.";
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Request failed with system query option $filter ne.";
                }
                #endregion

                if (true == isEqualOpValidation && true == isNotEqualOpValidation)
                {
                    passed = true;
                }

                details.Add(detail2);
            }

            details.Insert(0, detail1);
            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);

            return(passed);
        }
        /// <summary>
        /// Verify Common.Core.4719
        /// </summary>
        /// <param name="context">Service context</param>
        /// <param name="info">out parameter to return violation information when rule fail</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");
            }

            info = null;
            bool?  passed           = null;
            bool   isActionProperty = false;
            string actionName       = string.Empty;

            XElement metadata = XElement.Parse(context.MetadataDocument);

            JObject allobject;

            context.ResponsePayload.TryToJObject(out allobject);
            var jProps = allobject.Children();

            // Use the XPath query language to access the metadata document to get all bound Action properties.
            // The bound action has IsBound as true and the first parameter with Type attribute equal to the entity type full name.
            string        xpath            = string.Format(@"//*[local-name()='Action' and @IsBound='true']/*[local-name()='Parameter' and position()=1 and @Type='{0}']/parent::*", context.EntityTypeFullName);
            List <string> boundActionNames = MetadataHelper.GetPropertyValues(context, xpath, "Name");

            xpath = @"//*[local-name() = 'Schema'][1]";
            XElement metadataSchema = metadata.XPathSelectElement(xpath, ODataNamespaceManager.Instance);

            if (boundActionNames.Count > 0)
            {
                foreach (JProperty jProp in jProps)
                {
                    isActionProperty = JsonParserHelper.isFunctionOrActionProperty(boundActionNames, jProp, metadataSchema);

                    // Whether the property is bound action property exist.
                    if (isActionProperty)
                    {
                        if (jProp.Value["title"] != null && jProp.Value["target"] != null)
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                            break;
                        }
                    }
                }

                if (passed.HasValue && passed.Value == false)
                {
                    return(false);
                }

                if (context.PayloadType == RuleEngine.PayloadType.Feed && allobject[Constants.Value] != null)
                {
                    foreach (JObject ob in (JArray)allobject[Constants.Value])
                    {
                        jProps = ob.Children();

                        foreach (JProperty jProp in jProps)
                        {
                            isActionProperty = JsonParserHelper.isFunctionOrActionProperty(boundActionNames, jProp, metadataSchema);

                            // Whether the property is bound action property exist.
                            if (isActionProperty)
                            {
                                if (jProp.Value["title"] != null && jProp.Value["target"] != null)
                                {
                                    passed = true;
                                }
                                else
                                {
                                    passed = false;
                                    info   = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                                    break;
                                }
                            }
                        }

                        if (passed.HasValue && passed.Value == false)
                        {
                            break;
                        }
                    }
                }
            }
            return(passed);
        }
        /// <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;

            info = null;
            var    serviceStatus = ServiceStatus.GetInstance();
            string feedUrl       = string.Empty;
            string entityUrl     = string.Empty;
            KeyValuePair <string, IEnumerable <string> > entityUrls;

            if (JsonParserHelper.GetBatchSupportedEntityUrls(out entityUrls))
            {
                feedUrl   = string.Format("{0}/{1}", context.ServiceBaseUri, entityUrls.Key);
                entityUrl = entityUrls.Value.First();
            }

            string relativeUrl = new Uri(entityUrl).LocalPath;
            string host        = entityUrl.Remove(entityUrl.IndexOf(relativeUrl));

            string batchRequest = string.Format(@"
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http 
Content-Transfer-Encoding:binary

GET {0} HTTP/1.1

--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http 
Content-Transfer-Encoding:binary

GET {0} HTTP/1.1

--batch_36522ad7-fc75-4b56-8c71-56071383e77b--", entityUrl);

            string        boundary           = @"batch_36522ad7-fc75-4b56-8c71-56071383e77b";
            List <string> batchResponseSigns = new List <string>();
            Response      batchResponse      = WebHelper.BatchOperation(context.ServiceBaseUri.OriginalString, batchRequest, boundary);
            var           detail             = new ExtensionRuleResultDetail(this.Name, context.ServiceBaseUri.OriginalString + "/$batch", HttpMethod.Post, string.Empty, batchResponse, string.Empty, batchRequest);

            if (batchResponse != null && !string.IsNullOrEmpty(batchResponse.ResponsePayload))
            {
                string[] responseSegments = batchResponse.ResponsePayload.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string seg in responseSegments)
                {
                    if (seg.StartsWith(@"--batchresponse"))
                    {
                        batchResponseSigns.Add(seg);
                    }
                }

                // The request has 3 batch separators
                if (batchResponseSigns.Count == 3)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = string.Format("The batch response body does not match one-to-one with the batch request body, the batch request has 3 batch separators, but response has {0} batch separators. ", batchResponseSigns.Count);
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = string.Format("Batch request failed: {0}.", batchRequest);
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
Exemplo n.º 29
0
        /// <summary>
        /// Verifies the service implementation feature.
        /// </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 the service implementation feature 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);

            info   = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            detail = info.Details[0];

            List <string> supportedPropertyTypes = new List <string> {
                PrimitiveDataTypes.Int16, PrimitiveDataTypes.Int32, PrimitiveDataTypes.Int64,
                PrimitiveDataTypes.Decimal, PrimitiveDataTypes.Double
            };

            var filterRestrictions = AnnotationsHelper.GetFilterRestrictionsWithoutNavi(context.MetadataDocument, context.VocCapabilities, supportedPropertyTypes);

            if (string.IsNullOrEmpty(filterRestrictions.Item1) ||
                null == filterRestrictions.Item2 || !filterRestrictions.Item2.Any())
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports Greater Than system query options in the service.";

                return(passed);
            }

            string entitySet         = filterRestrictions.Item1;
            string primitivePropName = filterRestrictions.Item2.First().PropertyName;

            string url  = string.Format("{0}/{1}", context.ServiceBaseUri, entitySet);
            var    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);

            if (feed == null || JTokenType.Object != feed.Type)
            {
                detail.ErrorMessage = "The service does not return a valid response for system query option";
                return(passed);
            }

            var   entities = JsonParserHelper.GetEntries(feed);
            Int64 propVal  = entities[0].Value <Int64>(primitivePropName) - 1;

            string pattern = "{0}/{1}?$filter={2} gt {3}";

            url  = string.Format(pattern, context.ServiceBaseUri, entitySet, primitivePropName, propVal);
            resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail.URI                = url;
            detail.HTTPMethod         = "GET";
            detail.RequestHeaders     = StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders);
            detail.ResponseStatusCode = resp != null && resp.StatusCode.HasValue ? resp.StatusCode.Value.ToString() : "";
            detail.ResponseHeaders    = string.IsNullOrEmpty(resp.ResponseHeaders) ? "" : resp.ResponseHeaders;
            detail.ResponsePayload    = string.IsNullOrEmpty(resp.ResponsePayload) ? "" : resp.ResponsePayload;


            if (resp.StatusCode != HttpStatusCode.OK)
            {
                passed = false;
                detail.ErrorMessage = "Request failed with system query option $filter gt.";
                return(passed);
            }

            JObject feed2;

            resp.ResponsePayload.TryToJObject(out feed2);

            if (feed2 == null || JTokenType.Object != feed2.Type)
            {
                passed = false;
                detail.ErrorMessage = "The service does not return a valid response for system query option $filter gt.";
                return(passed);
            }

            var entities2 = JsonParserHelper.GetEntries(feed2).ToList();
            var temp      = entities2.FindAll(en => en.Value <Int64>(primitivePropName) > propVal).Select(en => en);

            if (entities2.Count() == temp.Count())
            {
                passed = true;
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not execute an accurate result with system query option $filter gt.";
            }

            return(passed);
        }
        /// <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 navigPropName         = string.Empty;
            string primitivePropertyName = 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;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityType = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    var    funcs       = new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                    {
                        AnnotationsHelper.GetFilterRestrictions
                    };
                    var expectedTypes = new List <string>()
                    {
                        "Edm.String"
                    };
                    var props = MetadataHelper.GetNormalProperties(context.MetadataDocument, nEntityType);

                    if (null == props || !props.Any())
                    {
                        continue;
                    }

                    var targetProps = props.Where(p => expectedTypes.Contains(p.PropertyType)).Select(p => p);

                    if (!targetProps.Any())
                    {
                        continue;
                    }

                    navigPropName         = np.NavigationPropertyName;
                    primitivePropertyName = targetProps.First().PropertyName;

                    break;
                }

                entitySet = r.Key;

                if (!string.IsNullOrEmpty(navigPropName) && !string.IsNullOrEmpty(primitivePropertyName))
                {
                    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(navigPropName))
            {
                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);
            }

            if (string.IsNullOrEmpty(primitivePropertyName))
            {
                detail.ErrorMessage = "Cannot get an appropriate primitive property from navigation properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string url  = string.Format("{0}/{1}?$expand={2}", context.ServiceBaseUri, entitySet, navigPropName);
            var    resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);
            var entities = JsonParserHelper.GetEntries(feed);

            if (null == entities || !entities.Any())
            {
                detail.ErrorMessage = string.Format("Cannot find any entities from the entity-set '{0}'", entitySet);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string searchVal = string.Empty;

            foreach (var en in entities)
            {
                if (null != en[navigPropName] || JTokenType.Array == en[navigPropName].Type || en[navigPropName].Any())
                {
                    if (JTokenType.Object != en[navigPropName].First.Type)
                    {
                        break;
                    }

                    var nEntity = en[navigPropName].First as JObject;
                    searchVal = nEntity[primitivePropertyName].ToString().Contains(" ") ?
                                string.Format("\"{0}\"", nEntity[primitivePropertyName].ToString()) :
                                nEntity[primitivePropertyName].ToString();

                    if (!string.IsNullOrEmpty(searchVal))
                    {
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(searchVal))
            {
                detail.ErrorMessage = "Cannot find any appropriate search values in the expanded entities.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            url    = string.Format("{0}/{1}?$expand={2}($search={3})", context.ServiceBaseUri, entitySet, navigPropName, searchVal);
            resp   = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", String.Empty, resp);

            if (null != resp && resp.StatusCode == HttpStatusCode.OK)
            {
                resp.ResponsePayload.TryToJObject(out feed);

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

                    foreach (var e in entities)
                    {
                        var navigEntities = e[navigPropName].ToList();

                        if (null == navigEntities || !navigEntities.Any())
                        {
                            continue;
                        }

                        foreach (var ne in navigEntities)
                        {
                            passed = null;

                            if (searchVal.StripOffDoubleQuotes() == ne[primitivePropertyName].ToString())
                            {
                                passed = true;
                            }

                            if (passed == null)
                            {
                                passed = false;
                                detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$search' for expanded properties.";
                            }
                        }

                        if (passed == false)
                        {
                            break;
                        }
                    }

                    if (null == passed)
                    {
                        detail.ErrorMessage = "Cannot find any appropriate data to verify this rule.";
                    }
                }
                else
                {
                    detail.ErrorMessage = "The service does not return an correct response payload.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$search' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }