/// <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;

            Uri metadataServiceUrl = new Uri(context.Destination.AbsoluteUri.TrimEnd('/') + @"/$metadata" + @"/");
            var req = WebRequest.Create(metadataServiceUrl) as HttpWebRequest;
            Response response = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, metadataServiceUrl.AbsoluteUri, "GET", string.Empty, response);

            if (response != null && response.StatusCode == HttpStatusCode.OK && response.ResponsePayload.IsMetadata())
            {
                passed = true;
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The response is not the metadata service document. Please refer to section 15 of [OData-CSDL].";
            }

            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;
            var response = WebHelper.Get(context.Destination, string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, context.Destination.AbsoluteUri, "GET", StringHelper.MergeHeaders(string.Empty, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                if (context.PayloadFormat == RuleEngine.PayloadFormat.JsonLight)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = "Get above URI with accept header 'application/json', the response is not JSON format.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = String.Format("No response returned from above URI with accept header 'application/json'.");
            }

            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.GetChangeTracking(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(restrictions.Item1) ||
                null == restrictions.Item2 || !restrictions.Item2.Any() ||
                null == restrictions.Item3 || !restrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find an entity-container or any entity-sets which support the change tracking function.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return passed;
            }

            string url = string.Format("{0}/{1}", context.ServiceBaseUri, restrictions.Item1);
            List<KeyValuePair<string, string>> requestHeaders = new List<KeyValuePair<string,string>>();

            foreach (KeyValuePair<string, string> kvp in context.RequestHeaders)
            {
                requestHeaders.Add(new KeyValuePair<string, string>(kvp.Key, kvp.Value));
            }

            // Add odata.track-changes preference in request header.
            KeyValuePair<string, string> prefer = new KeyValuePair<string, string>("Prefer", "odata.track-changes");
            requestHeaders.Add(prefer);
            Response response = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, requestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, requestHeaders), response);

            if (response != null && !string.IsNullOrEmpty(response.ResponseHeaders))
            {
                string preferHeader = response.ResponseHeaders.GetHeaderValue("Preference-Applied");

                if (string.IsNullOrEmpty(preferHeader))
                {
                    passed = false;
                    detail.ErrorMessage = "The response header returned by the service does not contain 'odata.track-changes', when request with the header 'Prefer:odata.track-changes'.";
                }
                else if (preferHeader.Contains("odata.track-changes"))
                {
                    passed = true;
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support Delta change tracking.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
示例#4
0
 public ExtensionRuleViolationInfo(Uri uri, string content, ExtensionRuleResultDetail detail)
 {
     this.Endpoint = uri;
     this.Content  = content;
     this.Details  = new List <ExtensionRuleResultDetail>();
     if (detail != null)
     {
         this.Details.Add(detail.Clone());
     }
 }
 public ExtensionRuleViolationInfo(Uri uri, string content, ExtensionRuleResultDetail detail)
 {
     this.Endpoint = uri;
     this.Content = content;
     this.Details = new List<ExtensionRuleResultDetail>();
     if (detail != null)
     {
         this.Details.Add(detail.Clone());
     }
 }
        /// <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);
            detail.ErrorMessage = @"This negative rule is not verified.";
            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

            return passed;
        }
        public ExtensionRuleResultDetail Clone()
        {
            ExtensionRuleResultDetail desDetail = new ExtensionRuleResultDetail();

            desDetail.RuleName           = this.RuleName;
            desDetail.URI                = this.URI;
            desDetail.HTTPMethod         = this.HTTPMethod;
            desDetail.RequestHeaders     = this.RequestHeaders;
            desDetail.RequestData        = this.RequestData;
            desDetail.ResponseStatusCode = this.ResponseStatusCode;
            desDetail.ResponseHeaders    = this.ResponseHeaders;
            desDetail.ResponsePayload    = this.ResponsePayload;
            desDetail.ErrorMessage       = this.ErrorMessage;

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

            var response = WebHelper.Get(context.Destination, Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, context.Destination.AbsoluteUri, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                // Extracts OData-Version value from ResponseHttpHeaders.
                string oDataVersion = context.ResponseHttpHeaders.GetHeaderValue(Constants.ODataVersion).TrimEnd(';');

                if (!string.IsNullOrEmpty(oDataVersion))
                {
                    float version = float.Parse(oDataVersion);

                    // The minimal OData version of conformance validation is 4.0.
                    if (version >= 4.0)
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format(@"The OData version is {0} which should be not less than minimal version 4.0.", version);
                    }
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = @"Can not get the OData-Version header from response headers.";
                }
            }
            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;

            string url = context.Destination.AbsoluteUri + @"/$metadata";
            Response response = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(string.Empty, context.RequestHeaders), response);

            // Get EntityContainer from metadata response payload.
            string xpath = @"//*[local-name()='EntityContainer']";
            XElement metadata = XElement.Parse(response.ResponsePayload);
            IEnumerable<XElement> entityContainer = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance);
            List<string> propNames = new List<string>();

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

            if (propNames.Count > 0)
            {
                passed = true;
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "There is no EntityContainer in metadat document.";
            }

            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;

            string url = string.Format("{0}/?$find=*", context.Destination);
            var req = WebRequest.Create(url) as HttpWebRequest;
            Response response = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            ExtensionRuleResultDetail detail1 = new ExtensionRuleResultDetail(this.Name, url, "GET", string.Empty, response);

            if (response != null && response.StatusCode != null)
            {

                if (response.StatusCode != HttpStatusCode.OK)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail1.ErrorMessage = "Services SHOULD fail the above URI because it contains query options 'find' which services does not understand.";
                }
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "No response returned from above URI.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);
            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();
            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);
                        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, navigPropName);
                                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;
        }
        /// <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 entityTypeShortName;
            var propTypes = new string[2] { "Edm.Date", "Edm.DateTimeOffset" };
            var propNames = MetadataHelper.GetPropertyNames(propTypes, out entityTypeShortName);
            if (null == propNames || !propNames.Any())
            {
                return passed;
            }

            string propName = propNames[0];
            var entitySetUrl = entityTypeShortName.GetAccessEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                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)
            {
                var settings = new JsonSerializerSettings();
                settings.DateParseHandling = DateParseHandling.None;
                JObject jObj = JsonConvert.DeserializeObject(resp.ResponsePayload, settings) as JObject;
                JArray jArr = jObj.GetValue(Constants.Value) as JArray;
                var entity = jArr.First as JObject;
                var propVal = entity[propName].ToString();
                int index = propVal.IndexOf('T');
                propVal = propVal.Substring(index + 1, 2);
                url = string.Format("{0}?$filter=hour({1}) eq {2}", url, propName, propVal);
                resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
                var detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);
                info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                if (null != resp && HttpStatusCode.OK == resp.StatusCode)
                {
                    jObj = JsonConvert.DeserializeObject(resp.ResponsePayload, settings) as JObject;
                    jArr = jObj.GetValue(Constants.Value) as JArray;
                    if (null == jArr || !jArr.Any())
                    {
                        return false;
                    }

                    foreach (JObject et in jArr)
                    {
                        passed = et[propName].ToString().Substring(index + 1, 2) == propVal;
                    }
                }
                else
                {
                    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 entitySets = MetadataHelper.GetFeeds(context.MetadataDocument);

            if (null == entitySets || !entitySets.Any())
            {
                detail.ErrorMessage = "Cannot find any entity-sets in metadata document.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return passed;
            }

            string entitySet = string.Empty;

            foreach (var set in entitySets)
            {
                if (true == set.IsSupportAsynchronousOperation(context.MetadataDocument, new List<string>() { context.VocCapabilities }))
                {
                    entitySet = set;
                    break;
                }
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports asynchronous operation in metadata document.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return passed;
            }

            string url = string.Format("{0}/{1}", context.ServiceBaseUri, entitySet);
            List<KeyValuePair<string, string>> requestHeaders = new List<KeyValuePair<string,string>>();

            foreach (KeyValuePair<string, string> kvp in context.RequestHeaders)
            {
                requestHeaders.Add(new KeyValuePair<string, string>(kvp.Key, kvp.Value));
            }

            // Add respond-async preference in request header.
            KeyValuePair<string, string> prefer = new KeyValuePair<string, string>("Prefer", "respond-async");
            requestHeaders.Add(prefer);
            Response response = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, requestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, requestHeaders), response);

            if (response != null )
            {
                if(response.StatusCode == HttpStatusCode.Accepted)
                {
                    passed = true;
                }
                else if (!string.IsNullOrEmpty(response.ResponseHeaders))
                {
                    string preferHeader = response.ResponseHeaders.GetHeaderValue("Preference-Applied");

                    if (!string.IsNullOrEmpty(preferHeader) && preferHeader.Contains("respond-async"))
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not support Asynchronous operations.";
                    }
                }
            }
            else
            {
                detail.ErrorMessage = "The service does not support Asynchronous operations.";
            }

            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;
            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, 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, 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(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;
        }
        /// <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 entityTypeShortName;
            var navigPropNames = MetadataHelper.GetNavigationPropertyNames(out entityTypeShortName);
            if (null == navigPropNames || !navigPropNames.Any())
            {
                return passed;
            }

            string navigPropName = navigPropNames[0];
            navigPropNames.RemoveAt(0);
            var entitySetUrl = entityTypeShortName.MapEntityTypeShortNameToEntitySetURL();
            string url = string.Format("{0}/{1}?$expand={2}", svcStatus.RootURL.TrimEnd('/'), entitySetUrl, navigPropName);
            var resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
            var detail = new ExtensionRuleResultDetail("ServiceImpl_SystemQueryOptionExpand", url, HttpMethod.Get, string.Empty);
            info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
            if (null != resp && HttpStatusCode.OK == resp.StatusCode)
            {
                var jObj = JObject.Parse(resp.ResponsePayload);
                var jArr = jObj.GetValue("value") as JArray;
                foreach (JObject entity in jArr)
                {
                    passed = false;
                    foreach (JProperty prop in entity.Children())
                    {
                        if (prop.Name == navigPropName)
                        {
                            passed = true;
                            break;
                        }
                    }
                }
            }

            return passed;
        }
 /// <summary>
 /// Add detail information to extension info.
 /// </summary>
 /// <param name="detail"></param>
 public void AddDetail(ExtensionRuleResultDetail detail)
 {
     this.Details.Add(detail);
 }
        /// <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, serviceStatus.RootURL, HttpMethod.Post, string.Empty);
            var detail2 = 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.None).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;
            }

            // To get test entity which is of media type
            EntityTypeElement entityType = null;
            foreach (var entityEle in entityTypeElements)
            {
                if (MetadataHelper.IsMediaEntity(serviceStatus.MetadataDocument, entityEle.EntityTypeShortName, context))
                {
                    entityType = entityEle;
                    break;
                }
            }

            if (null == entityType)
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with deleteable and insertable restrictions, but there is no entity type in metadata which is insertable and deleteable.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            // Map 'entity-set name' to 'entity-set URL'.
            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detail1.ErrorMessage = string.Format("Cannot find the entity-set URL which is matched with {0}", entityType.EntityTypeShortName);
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
            string reqDataStr = reqData.ToString();
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, true, ref additionalInfos);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (resp.StatusCode.HasValue && (HttpStatusCode.Created == resp.StatusCode || HttpStatusCode.NoContent == resp.StatusCode))
            {
                string entityId = additionalInfos.Last().EntityId;
                resp = WebHelper.GetEntity(entityId);
                detail2 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, string.Empty, resp);
                if (resp.StatusCode.HasValue && HttpStatusCode.OK == resp.StatusCode)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Can not get created entity 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 }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return passed;
        }
示例#18
0
 /// <summary>
 /// Add detail information to extension info.
 /// </summary>
 /// <param name="detail"></param>
 public void AddDetail(ExtensionRuleResultDetail detail)
 {
     this.Details.Add(detail);
 }
        /// <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 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" };
            List<EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None).ToList();

            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;
            }

            EntityTypeElement eTypeElement = new EntityTypeElement();

            foreach (var en in entityTypeElements)
            {

                if (MetadataHelper.IsMediaEntity(serviceStatus.MetadataDocument, en.EntityTypeShortName, context) && !string.IsNullOrEmpty(en.EntitySetName))
                {
                    eTypeElement = en;
                    break;
                }
            }

            if (eTypeElement.EntityTypeShortName == null)
            {
                detail.ErrorMessage = "Cannot find the appropriate media entity type to verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + eTypeElement.EntitySetName;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(eTypeElement.EntitySetName, eTypeElement.EntityTypeShortName, null, out additionalInfos);
            string reqDataStr = reqData.ToString();
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, true, ref additionalInfos);
            detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode || HttpStatusCode.NoContent == resp.StatusCode)
            {
                url = additionalInfos.Last().EntityId.TrimEnd('/') + "/$value";
                if(url.Equals("/$value"))
                {
                    detail.ErrorMessage = string.Format("Fail to find the image read and edit URL.", resp.StatusCode);
                    return passed;
                }

                resp = WebHelper.Get(new Uri(url), null, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, null);
                detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp);
                if (resp.StatusCode.HasValue && HttpStatusCode.OK == resp.StatusCode)
                {
                    resp = WebHelper.UpdateMediaTypeEntity(url, context.RequestHeaders, additionalInfos.Last().HasEtag);
                    detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Put, string.Empty, resp, string.Empty, "Successfully updated the stream of the image.");

                    if (null != resp && (HttpStatusCode.NoContent == resp.StatusCode || resp.StatusCode == HttpStatusCode.OK))
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format("HTTP PUT to update the image failed with the error {0}.", resp.StatusCode);
                    }
                }
                else
                {
                    detail.ErrorMessage = "Get entity image failed from above URI.";
                }

                // Restore the service.
                var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos);
            }
            else
            {
                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 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 entityTypeShortName;
            var propTypes = new string[1] { "Edm.DateTimeOffset" };
            var propNames = MetadataHelper.GetPropertyNames(propTypes, out entityTypeShortName);
            if (null == propNames || !propNames.Any())
            {
                return passed;
            }

            string propName = propNames[0];
            var entitySetUrl = entityTypeShortName.GetAccessEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                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)
            {
                JObject jObj = JObject.Parse(resp.ResponsePayload);
                JArray jArr = jObj.GetValue(Constants.Value) as JArray;
                var entity = jArr.First as JObject;
                var propVal = Convert.ToDateTime(entity[propName]).TimeOfDay.ToString();
                url = string.Format("{0}?$filter=time({1}) eq time('{1}')", url, propName);
                resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
                var detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);
                info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                if (null != resp && HttpStatusCode.OK == resp.StatusCode)
                {
                    jObj = JObject.Parse(resp.ResponsePayload);
                    jArr = jObj.GetValue(Constants.Value) as JArray;
                    foreach (JObject et in jArr)
                    {
                        passed = Convert.ToDateTime(et[propName]).TimeOfDay.ToString() == propVal;
                    }
                }
                else
                {
                    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;
            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            TermDocuments termDocs = TermDocuments.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            ExtensionRuleResultDetail detail1 = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail detail2 = 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.None).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;
            }

            // To get test entity which is deleteable and insertable
            EntityTypeElement entityType = null;
            foreach (var entityEle in entityTypeElements)
            {
                var matchEntity = entityEle.EntitySetName.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc,
                    new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                    {
                        AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetDeleteRestrictions
                    });

                if (!string.IsNullOrEmpty(matchEntity.Item1)
                     && matchEntity.Item2 != null && matchEntity.Item2.Any()
                     && matchEntity.Item3 != null && matchEntity.Item3.Any())
                {
                    entityType = entityEle;
                    break;
                }
            }

            if (null == entityType)
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with deleteable and insertable restrictions, but there is no entity type in metadata which is insertable and deleteable.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

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

                return passed;
            }

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

            // If create successfully, the resource is updatable and deletable.
            if (resp.StatusCode.HasValue && resp.StatusCode == HttpStatusCode.Created)
            {
                // Get feed response.
                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 && resp.StatusCode == HttpStatusCode.OK)
                {
                    JObject jo;
                    resp.ResponsePayload.TryToJObject(out jo);
                    var entries = JsonParserHelper.GetEntries(jo);
                    foreach (JObject entry in entries)
                    {
                        if (entry[Constants.V4OdataEditLink] == null)
                        {
                            passed = false;
                            detail2.ErrorMessage = "Not all entities from above URI contain edit links.";
                            break;
                        }
                    }

                    if (passed == null)
                    {
                        passed = true;
                    }
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Get feed resource failed from above URI.";
                }

                // Restore the service.
                var resps = WebHelper.DeleteEntities(additionalInfos);
            }
            else
            {
                passed = null;
                detail1.ErrorMessage = "Created new entity failed, it is not an updatable resource and can not verify this rule.";
            }

            var details = new List<ExtensionRuleResultDetail>() { detail1, detail2 }.RemoveNullableDetails();
            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;
            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);
            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)
            {
                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;
                }

                string navigPropName = null;
                string navigPropRelatedEntityTypeShortName = null;
                string navigPropRelatedEntitySetName = null;
                string navigPropRelatedEntitySetUrl = null;
                NormalProperty navigPropRelatedEntityTypeKey = null;

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

                    if (navigKeyProps.Count == 1 && keyPropertyTypes.Contains(navigKeyProps[0].PropertyType))
                    {
                        navigPropRelatedEntitySetName = navigPropRelatedEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();
                        navigPropRelatedEntitySetUrl = navigPropRelatedEntityTypeShortName.MapEntityTypeShortNameToEntitySetURL();
                        navigPropRelatedEntityTypeKey = navigKeyProps[0];
                        break;
                    }
                }

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

                    if (resp.StatusCode.HasValue && HttpStatusCode.Created == resp.StatusCode)
                    {
                        string entityId = additionalInfos1.Last().EntityId;
                        url = serviceStatus.RootURL.TrimEnd('/') + @"/" + navigPropRelatedEntitySetUrl;
                        if (string.IsNullOrEmpty(navigPropRelatedEntityTypeShortName))
                        {
                            continue;
                        }

                        var navigPropEntityType = navigPropRelatedEntityTypeShortName.GetSpecifiedEntityType(serviceStatus.MetadataDocument);
                        if (null == navigPropEntityType)
                        {
                            continue;
                        }

                        url = string.Format("{0}/{1}", entityId, navigPropName);
                        var additionalInfos2 = new List<AdditionalInfo>();
                        reqData = dFactory.ConstructInsertedEntityData(navigPropRelatedEntitySetName, navigPropRelatedEntityTypeShortName, null, out additionalInfos2);
                        reqDataStr = reqData.ToString();
                        isMediaType = !string.IsNullOrEmpty(additionalInfos2.Last().ODataMediaEtag);
                        resp = WebHelper.CreateEntity(url, reqData, isMediaType, ref additionalInfos2);
                        detail2 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

                        if (resp.StatusCode.HasValue && resp.StatusCode == HttpStatusCode.Created)
                        {
                            passed = true;

                            // Restore the service.
                            var resps2 = WebHelper.DeleteEntities(additionalInfos2);
                        }
                        else
                        {
                            passed = false;
                            detail2.ErrorMessage = "Create entity failed for above URI.";
                        }

                        // Restore the service.
                        var resps1 = WebHelper.DeleteEntities(additionalInfos1);
                    }
                    else
                    {
                        passed = false;
                        detail1.ErrorMessage = "Created the new entity failed for above URI.";
                    }
                }

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

            var details = new List<ExtensionRuleResultDetail>() { detail1, detail2 }.RemoveNullableDetails();
            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> expectedTypes = new List<string>() { "Edm.String", "Edm.Int32", "Edm.Int16", "Edm.Single", "Edm.Double", "Edm.Boolean", "Edm.DateTimeOffset", "Edm.Guid" };
            ExtensionRuleResultDetail detail1 = new ExtensionRuleResultDetail(this.Name);

            var restrictions = AnnotationsHelper.GetRestrictions(
                context.MetadataDocument,
                context.VocCapabilities,
                new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>() { AnnotationsHelper.GetExpandRestrictions, AnnotationsHelper.GetFilterRestrictions },
                null,
                NavigationRoughType.CollectionValued);

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

                return passed;
            }

            string entitySet = restrictions.Item1;
            string navigPropName = string.Empty;
            string navigPropType = string.Empty;

            foreach (var np in restrictions.Item3)
            {
                if (NavigationRoughType.CollectionValued == np.NavigationRoughType)
                {
                    navigPropName = np.NavigationPropertyName;
                    navigPropType = np.NavigationPropertyType;
                }
            }

            string entityType = navigPropType.RemoveCollectionFlag().GetLastSegment();
            var tmp = MetadataHelper.GetPropertiesWithSpecifiedTypeFromEntityType(entityType, context.MetadataDocument, expectedTypes);

            if (null == tmp && !tmp.Any())
            {
                detail1.ErrorMessage = "Cannot find an appropriate primitive properties of entity type in the service.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return passed;
            }

            var str = tmp.First().Split(',');
            string primitivePropertyName = str[0];
            string primitivePropertyType = str[1];

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

                return passed;
            }

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

                return passed;
            }

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

                return passed;
            }

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

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

                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, entitySet, navigPropName, primitivePropertyName, compareVal);
                    var 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 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;
                                        detail1.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;
                        detail1.ErrorMessage = "The service does not support system query option '$filter' on expanded entities.";
                    }
                }
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);
            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();
            DataFactory dFactory = DataFactory.Instance();
            var detail = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL, HttpMethod.Post, string.Empty);
            string updateUrl = serviceStatus.RootURL;
            List<string> keyPropertyTypes = new List<string>() { "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String" };
            List<string> norPropertyTypes = new List<string>() { "Edm.String" };
            List<EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None).ToList();
            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 and a string type normal 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;
            }

            EntityTypeElement entityType = null;

            List<string> navigationPropNames = new List<string>();

            foreach (var en in entityTypeElements)
            {

                if (MetadataHelper.HasEntityNavigationProp(serviceStatus.MetadataDocument, en.EntityTypeShortName, NavigationRoughType.CollectionValued, context, out navigationPropNames))
                {
                    entityType = en;
                    break;
                }
            }

            if (navigationPropNames!=null && !navigationPropNames.Any())
            {
                detail.ErrorMessage = "To verify this rule it is expected that an entity type has collection-valued navigation type property, but there is no such entity type in metadata so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string navigationPropName = navigationPropNames.First();

            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
            updateUrl = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detail.ErrorMessage = string.Format("Cannot find the entity-set URL which is matched with {0}", entityType.EntityTypeShortName);
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var additionalInfosAll = new List<AdditionalInfo>();
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
            additionalInfosAll = 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)
            {
                string entityId = additionalInfos.Last().EntityId;
                string nPropUrl = entityId.TrimEnd('/') + "/" + navigationPropName;
                updateUrl = entityId.TrimEnd('/') + "/" + navigationPropName + "/$ref";
                bool hasEtag = additionalInfos.Last().HasEtag;
                resp = WebHelper.GetEntity(updateUrl);
                detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Get, string.Empty, resp);
                if (HttpStatusCode.OK == resp.StatusCode)
                {
                    string navigPropType = MetadataHelper.GetNavigPropertyTypeFromMetadata(navigationPropName, entityType.EntityTypeShortName, serviceStatus.MetadataDocument);
                    string navigPropTypeShortName = navigPropType.Substring(navigPropType.IndexOf("(")).GetLastSegment().TrimEnd(')');
                    string nEntitySetName = navigationPropName.MapNavigationPropertyNameToEntitySetName(entityType.EntityTypeShortName);
                    string nEntitySetUrl = nEntitySetName.MapEntitySetNameToEntitySetURL();
                    nEntitySetUrl = serviceStatus.RootURL.TrimEnd('/') + @"/" + nEntitySetUrl;

                    reqData = dFactory.ConstructInsertedEntityData(nEntitySetName, navigPropTypeShortName, null, out additionalInfos);

                    foreach(AdditionalInfo a in additionalInfos)
                    {
                        additionalInfosAll.Add(a);
                    }

                    reqDataStr = reqData.ToString();
                    isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
                    resp = WebHelper.CreateEntity(nEntitySetUrl, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
                    detail = new ExtensionRuleResultDetail(this.Name, nEntitySetUrl, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

                    if (HttpStatusCode.Created == resp.StatusCode)
                    {
                        string refEntityId = additionalInfos.Last().EntityId;

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

                        if (HttpStatusCode.OK == resp.StatusCode)
                        {
                            string refEntitySetNameWithEnityID = refEntityId.Split('/').Last();
                            string refEntityValue = serviceStatus.RootURL.TrimEnd('/') + @"/" + refEntitySetNameWithEnityID;
                            string refEntityID = refEntitySetNameWithEnityID.Contains("'") ? refEntitySetNameWithEnityID.Split('\'')[1] : refEntitySetNameWithEnityID.Split('(')[1].TrimEnd(')');

                            JObject navigationRefSet = new JObject();
                            navigationRefSet.Add(Constants.V4OdataId, refEntityValue);

                            resp = WebHelper.CreateEntity(updateUrl, context.RequestHeaders, navigationRefSet, isMediaType, ref additionalInfosAll);
                            detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Patch, string.Empty, resp, string.Empty, reqDataStr);
                            if (HttpStatusCode.NoContent == resp.StatusCode)
                            {
                                resp = WebHelper.GetEntity(updateUrl);
                                detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

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

                                    bool created = false;

                                    var entries = JsonParserHelper.GetEntries(refEntitySet);

                                    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;
                                                    }
                                                }
                                            }
                                        }

                                        if (!created)
                                        {
                                            passed = false;
                                            detail.ErrorMessage = string.Format("The HTTP Post to add a reference to a collection-valued navigation property failed.");
                                        }
                                    }
                                }
                                else
                                {
                                    passed = false;
                                    detail.ErrorMessage = "HTTP Get to the navigation property reference failed.";
                                }
                            }
                            else
                            {
                                passed = false;
                                detail.ErrorMessage = "HTTP Post to add the navigation property reference failed.";
                            }
                        }
                        else
                        {
                            detail.ErrorMessage = "Can not get the created navigation entity set entity.";
                        }
                    }
                    else
                    {
                        detail.ErrorMessage = "Creating navigation entity failes from above URI.";
                    }
                }
                else
                {
                    detail.ErrorMessage = "Can not get the created entity's navigation property reference collection set from above URI.";
                }

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

            var details = new List<ExtensionRuleResultDetail>() { detail };
            info = new ExtensionRuleViolationInfo(new Uri(updateUrl), serviceStatus.ServiceDocument, details);

            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;

            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.GetFilterRestrictions(context.MetadataDocument, context.VocCapabilities, supportedPropertyTypes,NavigationRoughType.None);

            if (string.IsNullOrEmpty(filterRestrictions.Item1) ||
                null == filterRestrictions.Item2 || !filterRestrictions.Item2.Any())
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports Less 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=(1 add 0) mul {2} sub {3} lt 2";
            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 '()'.";
                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 '()'.";
                return passed;
            }

            var entities2 = JsonParserHelper.GetEntries(feed2).ToList();
            var temp = entities2.FindAll(en => en.Value<Int64>(primitivePropName) - propVal < 2).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 '()'.";
            }

            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 termDocs = TermDocuments.GetInstance();
            var serviceStatus = ServiceStatus.GetInstance();
            var dFactory = DataFactory.Instance();
            var detail1 = new ExtensionRuleResultDetail(this.Name);
            var detail2 = new ExtensionRuleResultDetail(this.Name);
            List<string> keyPropertyTypes = new List<string>() { "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String" };
            List<string> norPropertyTypes = new List<string>() { "Edm.String" };
            List<EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, norPropertyTypes, NavigationRoughType.None).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 and a normal property with string type, but there is not this entity type in metadata, so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            EntityTypeElement entityType = null;
            foreach (var ele in entityTypeElements)
            {
                if (ele.EntityTypeShortName.IsMediaType())
                {
                    continue;
                }

                var funcs = new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                {
                    AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetUpdateRestrictions, AnnotationsHelper.GetDeleteRestrictions
                };
                var methods = new List<Func<string, string, List<string>, bool?>>()
                {
                    SupportiveFeatureHelper.IsSupportBatchOperation
                };
                var restrictions = ele.EntitySetName.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc, funcs, null, NavigationRoughType.None, methods);

                if (!string.IsNullOrEmpty(restrictions.Item1) &&
                    null != restrictions.Item2 && restrictions.Item2.Any())
                {
                    entityType = ele;
                    break;
                }
            }

            if (null == entityType || string.IsNullOrEmpty(entityType.EntitySetName))
            {
                detail1.ErrorMessage = "The service does not support batch operation.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();

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

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
            string reqDataStr = reqData.ToString();
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, false, 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;
                bool hasEtag = additionalInfos.Last().HasEtag;
                resp = WebHelper.GetEntity(entityId);

                if (HttpStatusCode.OK == resp.StatusCode)
                {
                    JObject entity = JObject.Parse(resp.ResponsePayload);
                    List<string> norPropertyNames = entityType.NormalProperties.Where(norProp => norPropertyTypes.Contains(norProp.PropertyType)).Select(norProp => norProp.PropertyName).ToList();
                    reqDataStr = dFactory.ConstructUpdatedEntityData(entity, norPropertyNames).ToString();

                    string boundary = "batch_4e1a76dc-b738-4aa4-9f93-df661d0a4c9f";
                    string batchReqData = string.Format(
            @"
            --batch_4e1a76dc-b738-4aa4-9f93-df661d0a4c9f
            Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621

            --changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621
            Content-Type: application/http
            Content-Transfer-Encoding: binary
            Content-ID: 1

            PATCH {0} HTTP/1.1
            "
            + (hasEtag ? "If-Match: *" : string.Empty) +
            @"

            {1}

            --changeset_77162fcd-b8da-41ac-a9f8-9357efbbd621--

            --batch_4e1a76dc-b738-4aa4-9f93-df661d0a4c9f--
            ", entityId, reqDataStr);
                    resp = WebHelper.BatchOperation(serviceStatus.RootURL, batchReqData, boundary);
                    detail2 = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL + "/$batch", HttpMethod.Post, string.Empty, resp, string.Empty, batchReqData);

                    if (HttpStatusCode.OK == resp.StatusCode)
                    {
                        string expectedData =
            @"Content-Type: application/http
            Content-Transfer-Encoding: binary
            Content-ID: 1
            HTTP/1.1 204 No Content
            ";
                        int index = resp.ResponsePayload.IndexOf("--changesetresponse");
                        string payload = resp.ResponsePayload.Remove(0, index + 1);
                        if (expectedData == payload.Filtration(new List<string>() { "\nContent-Type:", "\nContent-Transfer-Encoding:", "\nContent-ID:", "\nHTTP/1.1 " }))
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            detail2.ErrorMessage = string.Format("The response payload does not accord with the expected pattern as follows:\r\n{0}", expectedData);
                        }
                    }
                    else
                    {
                        passed = false;
                        detail2.ErrorMessage = "The OData service does not return a 200 OK HTTP status code.";
                    }

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

            var details = new List<ExtensionRuleResultDetail>() { detail1, detail2 }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            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;
            info = null;
            var svcStatus = ServiceStatus.GetInstance();
            string entityTypeShortName;
            var props = MetadataHelper.GetProperties("Edm.GeographyPoint", 1, out entityTypeShortName);
            if (null == props || !props.Any())
            {
                return passed;
            }

            string propName = props[0].PropertyName;
            string srid = props[0].SRID;
            var entitySetUrl = entityTypeShortName.GetAccessEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                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)
            {
                JObject jObj = JObject.Parse(resp.ResponsePayload);
                JArray jArr = jObj.GetValue(Constants.Value) as JArray;
                var entity = jArr.First as JObject;
                var propVal = entity[propName]["coordinates"] as JArray;
                var pt1 = new Point(Convert.ToDouble(propVal[0]), Convert.ToDouble(propVal[1]));
                var pt2 = new Point(0.0, 0.0);
                var distance = Point.GetDistance(pt1, pt2);
                url = string.Format("{0}?$filter=geo.distance({1}, geography'POINT(0.0 0.0)') ge {2}", url, propName, distance);
                resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
                var detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);
                info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                if (null != resp && HttpStatusCode.OK == resp.StatusCode)
                {
                    jObj = JObject.Parse(resp.ResponsePayload);
                    jArr = jObj.GetValue(Constants.Value) as JArray;
                    foreach (JObject et in jArr)
                    {
                        propVal = et[propName]["coordinates"] as JArray;
                        pt1 = new Point(Convert.ToDouble(propVal[0]), Convert.ToDouble(propVal[1]));
                        pt2 = new Point(0.0, 0.0);
                        var dis = Point.GetDistance(pt1, pt2);
                        passed = dis >= distance;
                    }
                }
                else
                {
                    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;
            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            var detail = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL, HttpMethod.Post, string.Empty);
            string updateUrl = serviceStatus.RootURL;
            List<string> keyPropertyTypes = new List<string>() { "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String" };
            List<string> norPropertyTypes = new List<string>() { "Edm.String" };
            List<EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None).ToList();
            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 and a string type normal 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;
            }

            EntityTypeElement entityType = null;
            NormalProperty primitiveProp = null;
            foreach (var en in entityTypeElements)
            {
                foreach(NormalProperty np in en.NormalProperties)
                {
                    if (!np.IsKey && np.PropertyType.Equals("Edm.String"))
                    {
                        entityType = en;
                        primitiveProp = np;
                        break;
                    }
                }

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

            if (entityType == null)
            {
                detail.ErrorMessage = "To verify this rule it expects an Edm.String type property in an entity, but there is no such entity in metadata. So can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
            updateUrl = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detail.ErrorMessage = string.Format("Cannot find the entity-set URL which is matched with {0}", entityType.EntityTypeShortName);
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);

            if (!dFactory.CheckOrAddTheMissingPropertyData(entityType.EntitySetName, primitiveProp.PropertyName, ref reqData))
            {
                detail.ErrorMessage = "The property to update does not exist, and cannot be updated.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string reqDataStr = reqData.ToString();
            if (reqDataStr.Length > 2)
            {
                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)
                {
                    string entityId = additionalInfos.Last().EntityId;
                    updateUrl = entityId.TrimEnd('/') + "/" + primitiveProp.PropertyName + @"/$value";
                    bool hasEtag = additionalInfos.Last().HasEtag;
                    resp = WebHelper.GetPropertyValue(updateUrl);
                    detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Get, string.Empty, resp);
                    if (HttpStatusCode.OK == resp.StatusCode || HttpStatusCode.NoContent == resp.StatusCode)
                    {
                        resp = WebHelper.UpdateAStringProperty(updateUrl, context.RequestHeaders, hasEtag);
                        detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Put, string.Empty, resp, string.Empty, reqDataStr);
                        if (HttpStatusCode.NoContent == resp.StatusCode)
                        {
                            resp = WebHelper.GetPropertyValue(updateUrl);
                            detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                            if (HttpStatusCode.OK == resp.StatusCode)
                            {
                                if (resp.ResponsePayload == Constants.UpdateData)
                                {
                                    passed = true;
                                }
                                else
                                {
                                    passed = false;
                                    detail.ErrorMessage = string.Format("The primitive property in request is not updated.");
                                }
                            }
                            else
                            {
                                passed = false;
                                detail.ErrorMessage = "Can not get the updated entity.";
                            }
                        }
                        else
                        {
                            passed = false;
                            detail.ErrorMessage = "Put the primitive property failed.";
                        }
                    }
                    else
                    {
                        detail.ErrorMessage = "Can not get the created entity from above URI.";
                    }

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

            var details = new List<ExtensionRuleResultDetail>() { detail };
            info = new ExtensionRuleViolationInfo(new Uri(updateUrl), serviceStatus.ServiceDocument, details);

            return passed;
        }
        public void Execute(ServiceContext context, IEnumerable <Rule> rules, int rulecount, TestComplete ruleresult)
        {
            if (context == null)
            {
                this.resultProvider.JobCompleted(true);
                var e = new RuntimeException(new ArgumentNullException("context"), Resource.NullServiceContext);
                this.LogRuntimeError(e);
                return;
            }

            if (rules == null)
            {
                throw new ArgumentNullException("rules");
            }

            int count = 0;

            try
            {
                foreach (var rule in rules)
                {
                    count++;
                    TestResult result;
                    try
                    {
                        result = RuleExecuter.ExecuteRule(context, rule);
                    }
                    catch (RuntimeException e)
                    {
                        result = TestResult.CreateAbortedResult(rule, context.JobId);

                        e.JobId               = context.JobId;
                        e.RuleName            = rule.Name;
                        e.DestinationEndpoint = context.Destination.AbsolutePath;
                        this.LogRuntimeError(e);
                    }
                    catch (Exception ex)
                    {
                        if (!ExceptionHelper.IsCatchableExceptionType(ex))
                        {
                            throw;
                        }


                        result = TestResult.CreateAbortedResult(rule, context.JobId);

                        if (result.Details == null)
                        {
                            result.Details = new List <ExtensionRuleResultDetail>();
                        }
                        ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(result.RuleName, string.Empty, HttpMethod.Get, "Exception:  " + ex.Message);
                        result.Details.Add(detail);
                        detail = new ExtensionRuleResultDetail(result.RuleName, string.Empty, HttpMethod.Get, "StackTrace:  " + ex.StackTrace);
                        result.Details.Add(detail);



                        var e = new RuntimeException(ex, null);
                        e.JobId               = context.JobId;
                        e.RuleName            = rule.Name;
                        e.DestinationEndpoint = context.Destination.AbsolutePath;

                        this.LogRuntimeError(e);
                        bool tryagain = false;
                        if (tryagain)
                        {
                            result = RuleExecuter.ExecuteRule(context, rule);
                        }
                    }
                    if (ruleresult != null)
                    {
                        ruleresult(result, rulecount, ref count);
                    }
                    this.resultProvider.Accept(result);
                }

                //this.resultProvider.JobCompleted(errorOccurred);
            }
            catch (Exception ex)
            {
                if (!ExceptionHelper.IsCatchableExceptionType(ex))
                {
                    throw;
                }

                this.resultProvider.JobCompleted(true);
            }
        }
        /// <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);
            var detail4 = 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;
            }

            EntityTypeElement entityType = null;
            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.GetDeleteRestrictions, AnnotationsHelper.GetInsertRestrictions
                    });

                if (!string.IsNullOrEmpty(matchEntity.Item1)
                     && matchEntity.Item2 != null && matchEntity.Item2.Any()
                     && matchEntity.Item3 != null && matchEntity.Item3.Any())
                {
                    entityType = en;
                    break;
                }
            }

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

                return passed;
            }

            // Create a entity
            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var keyNames = entityType.KeyProperties;
            string keyName = keyNames.First().PropertyName;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.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 == HttpStatusCode.Created)
            {
                var entityId = additionalInfos.Last().EntityId;
                var hasEtag = additionalInfos.Last().HasEtag;

                // Get a individual property except key property
                string toDeletePropertyName = string.Empty;
                List<string> properties = MetadataHelper.GetNormalPropertiesNames(serviceStatus.MetadataDocument, entityType.EntityTypeShortName);

                foreach (string name in properties)
                {
                    if (!string.Equals(name, keyName))
                    {
                        toDeletePropertyName = name;
                        break;
                    }
                }

                string individualProUrl = entityId + @"/" + toDeletePropertyName;
                resp = WebHelper.Get(new Uri(individualProUrl), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders);
                detail2 = new ExtensionRuleResultDetail(this.Name, individualProUrl, HttpMethod.Get, StringHelper.MergeHeaders(Constants.AcceptHeaderJson, serviceStatus.DefaultHeaders), resp);
                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    // Delete the individual property
                    resp = WebHelper.DeleteEntity(individualProUrl, context.RequestHeaders, hasEtag);
                    detail3 = new ExtensionRuleResultDetail(this.Name, individualProUrl, HttpMethod.Delete, string.Empty, resp);
                    if (resp.StatusCode == HttpStatusCode.NoContent)
                    {
                        // Check whether the deleted property is set to null
                        if (WebHelper.GetContent(individualProUrl, context.RequestHeaders, out resp))
                        {
                            detail4 = new ExtensionRuleResultDetail(this.Name, individualProUrl, HttpMethod.Get, string.Empty, resp);
                            JObject jo;
                            resp.ResponsePayload.TryToJObject(out jo);
                            if (jo != null && jo[Constants.Value].Type == JTokenType.Null)
                            {
                                passed = true;
                            }
                            else if (jo == null)
                            {
                                passed = false;
                                detail4.ErrorMessage = "Can not get individual property after deleting it. ";
                            }
                            else if (jo != null && jo[Constants.Value].Type != JTokenType.Null)
                            {
                                passed = false;
                                detail4.ErrorMessage = "The individual property is not set to null after deleting it. ";
                            }
                        }
                    }
                    else
                    {
                        passed = false;
                        detail3.ErrorMessage = "Delete individual property from the created entity failed. ";
                    }
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Get individual property from the created entity failed. ";
                }

                // Delete the entity
                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, detail4 }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(this.ErrorMessage, new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            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;
            info = null;
            var svcStatus = ServiceStatus.GetInstance();
            string entityTypeShortName;
            var keyProp = MetadataHelper.GetKeyProperty(out entityTypeShortName);
            if (null == keyProp)
            {
                return passed;
            }

            string keyPropName = keyProp.Item1;
            string keyPropType = keyProp.Item2;
            if (string.IsNullOrEmpty(entityTypeShortName) || string.IsNullOrEmpty(keyPropName) || string.IsNullOrEmpty(keyPropType))
            {
                return passed;
            }

            string entitySetUrl = entityTypeShortName.MapEntityTypeShortNameToEntitySetURL();
            string url = string.Format("{0}/{1}?$orderby={2} desc", svcStatus.RootURL.TrimEnd('/'), entitySetUrl, keyPropName);
            var resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            var detail = new ExtensionRuleResultDetail("ServiceImpl_SystemQueryOptionOrderBy", url, HttpMethod.Get, string.Empty);
            info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
            if (null != resp && HttpStatusCode.OK == resp.StatusCode)
            {
                passed = true;
            }

            var jObj = JObject.Parse(resp.ResponsePayload);
            var jArr = jObj.GetValue("value") as JArray;
            for (int i = 0; i < jArr.Count - 1; i++)
            {
                if (!CompareOperationHelper.Compare(jArr[i][keyPropName], jArr[i + 1][keyPropName], keyPropType, ComparerType.Equal | ComparerType.GreaterThan))
                {
                    passed = false;
                    break;
                }
            }

            return passed;
        }
        /// <summary>
        /// Verifies $expand.
        /// </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 static HttpStatusCode? VerifyExpand(ServiceContext context, out bool? passed, out ExtensionRuleViolationInfo info)
        {
            if (context.ServiceVerResult.ExpandResult != null)
            {
                info = context.ServiceVerResult.ExpandResult.ViolationInfo;
                passed = context.ServiceVerResult.ExpandResult.Passed;
                return context.ServiceVerResult.ExpandResult.ResponseStatusCode;
            }

            passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail();

            var expandRestrictions = AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities);
            if (string.IsNullOrEmpty(expandRestrictions.Item1) ||
                null == expandRestrictions.Item3 || !expandRestrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find an entity-set which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
                context.ServiceVerResult.ExpandResult = new ServiceVerificationResult(passed, info);

                return null;
            }

            string entitySet = expandRestrictions.Item1;
            string navigProp = expandRestrictions.Item3.First().NavigationPropertyName;

            string url = string.Format("{0}/{1}?$expand={2}", context.ServiceBaseUri, entitySet, navigProp);
            Response resp = WebHelper.Get(WebRequest.Create(url), RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            detail = new ExtensionRuleResultDetail(string.Empty, url, "GET", string.Empty, resp);

            if (!string.IsNullOrEmpty(resp.ResponsePayload))
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);
                var entities = JsonParserHelper.GetEntries(feed);

                foreach (JObject ob in entities)
                {
                    if (ob[navigProp] == null)
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format("The expanded property {0} does not exist in response payload.", navigProp);
                        break;
                    }
                    else
                    {
                        passed = true;
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = string.Format("Get response failed from above URI.");
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            context.ServiceVerResult.ExpandResult = new ServiceVerificationResult(passed, info, resp.StatusCode);

            return resp.StatusCode;
        }
        /// <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);
            var detail4 = new ExtensionRuleResultDetail(this.Name);
            var detail5 = new ExtensionRuleResultDetail(this.Name);
            var detail6 = new ExtensionRuleResultDetail(this.Name);
            var entityType = MetadataHelper.GetConcurrencyEntityType(serviceStatus.MetadataDocument);
            if (null == entityType)
            {
                detail1.ErrorMessage = "Cannot find an entity which contains a concurrency property in the service.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            var normalPropNames = entityType.NormalProperties.Where(np => "Edm.String" == np.PropertyType && !np.IsKey).Select(np => np.PropertyName);
            if (normalPropNames.Any())
            {
                string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
                string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
                var additionalInfos = new List<AdditionalInfo>();
                var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
                string reqDataStr = reqData.ToString();
                bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
                var resp = WebHelper.CreateEntity(url, reqData, isMediaType, ref additionalInfos);
                detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

                if (HttpStatusCode.Created == resp.StatusCode)
                {
                    var entityId = additionalInfos.Last().EntityId;
                    var hasEtag = additionalInfos.Last().HasEtag;
                    if (!hasEtag)
                    {
                        detail1.ErrorMessage = "The new inserted entity does not contain an @odata.etag annotation.";
                        info = new ExtensionRuleViolationInfo(new Uri(entityId), resp.ResponsePayload, detail2);

                        return passed;
                    }

                    resp = WebHelper.GetEntity(entityId, serviceStatus.DefaultHeaders);
                    detail2 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, StringHelper.MergeHeaders(string.Empty, serviceStatus.DefaultHeaders), resp);
                    if (HttpStatusCode.OK == resp.StatusCode)
                    {
                        reqDataStr = dFactory.ConstructUpdatedEntityData(reqData, normalPropNames).ToString();
                        var header = new KeyValuePair<string, string>("If-Match", additionalInfos.Last().ODataEtag);
                        var headers = new List<KeyValuePair<string, string>>() { header };
                        resp = WebHelper.UpdateEntity(entityId, reqDataStr, HttpMethod.Patch, headers);
                        detail3 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Patch, StringHelper.MergeHeaders(string.Empty, headers), resp, string.Empty, reqDataStr);
                        if (HttpStatusCode.NoContent == resp.StatusCode)
                        {
                            resp = WebHelper.GetEntity(entityId, serviceStatus.DefaultHeaders);
                            detail4 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, StringHelper.MergeHeaders(string.Empty, serviceStatus.DefaultHeaders), resp);
                            if (HttpStatusCode.OK == resp.StatusCode)
                            {
                                resp = WebHelper.DeleteEntity(entityId, hasEtag);
                                detail5 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Delete, StringHelper.MergeHeaders(string.Empty, new List<KeyValuePair<string, string>>() { header }), resp);
                                if (HttpStatusCode.NoContent == resp.StatusCode)
                                {
                                    resp = WebHelper.GetEntity(entityId);
                                    detail6 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, string.Empty, resp);
                                    if (HttpStatusCode.NotFound == resp.StatusCode)
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                        detail6.ErrorMessage = "It still can get the deleted entity from above URI.";
                                    }
                                }
                                else
                                {
                                    passed = false;
                                    detail5.ErrorMessage = "Delete entity failed.";
                                }
                            }
                            else
                            {
                                passed = false;
                                detail4.ErrorMessage = "Can not get the created entity from above URI.";
                            }
                        }
                        else
                        {
                            passed = false;
                            detail3.ErrorMessage = "Update entity failed.";
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail1.ErrorMessage = "Created the new entity failed for above URI.";
                }
            }

            var details = new List<ExtensionRuleResultDetail>()
            {
                detail1, detail2, detail3, detail4, detail5, detail6
            }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            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;
            info = null;
            var svcStatus = ServiceStatus.GetInstance();
            string entityTypeShortName;
            var propTypes = new string[1] { "Edm.DateTimeOffset" };
            var propNames = MetadataHelper.GetPropertyNames(propTypes, out entityTypeShortName);
            if (null == propNames || !propNames.Any())
            {
                return passed;
            }

            string propName = propNames[0];
            var entitySetUrl = entityTypeShortName.GetAccessEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                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)
            {
                url = string.Format("{0}?$filter={1} le maxdatetime()", url, propName);
                resp = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, svcStatus.DefaultHeaders);
                var detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, string.Empty);
                info = new ExtensionRuleViolationInfo(new Uri(url), string.Empty, detail);
                if (null != resp && HttpStatusCode.OK == resp.StatusCode)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                }
            }

            return passed;
        }