/// <summary>
        /// Asserts whether any properties of an entity-type matches the specified conditions.
        /// </summary>
        /// <param name="entityType">An entity-type.</param>
        /// <param name="containedKeyPropSum">Limits the contained key properties' sum of an entity-type.</param>
        /// <param name="containedKeyPropTypes">Limits the contained key properties' types of an entity-type.</param>
        /// <param name="containedNormalPropTypes">Limits contained normal properties' types of an entity-type.</param>
        /// <param name="containedNavigRoughType">Limits the contained navigation properties' rough-type of an entity-type.</param>
        /// <returns>Returns the decision outcome.</returns>
        public static bool EntityTypeAnyPropertiesMeetsSpecifiedConditions(
            EntityTypeElement entityType,
            uint? containedKeyPropSum,
            IEnumerable<string> containedKeyPropTypes,
            IEnumerable<string> containedNormalPropTypes,
            NavigationRoughType containedNavigRoughType)
        {
            if (entityType == null)
            {
                return false;
            }

            if (null != containedKeyPropSum)
            {
                if (containedKeyPropSum != entityType.KeyProperties.Count())
                {
                    return false;
                }
            }

            if (null != containedKeyPropTypes)
            {
                var appropriateKeyProps = entityType.KeyProperties
                    .Where(keyProp => containedKeyPropTypes.Contains(keyProp.PropertyType))
                    .Select(keyProp => keyProp);

                if (0 == appropriateKeyProps.Count())
                {
                    return false;
                }
            }

            if (null != containedNormalPropTypes)
            {
                var appropriateNormalProps = entityType.NormalProperties
                    .Where(norProp => containedNormalPropTypes.Contains(norProp.PropertyType))
                    .Select(norProp => norProp);

                if (0 == appropriateNormalProps.Count())
                {
                    return false;
                }
            }

            if (NavigationRoughType.None != containedNavigRoughType)
            {
                var appropriateNavigProps = entityType.NavigationProperties
                    .Where(navProp => containedNavigRoughType == navProp.NavigationRoughType)
                    .Select(navProp => navProp);

                if (0 == appropriateNavigProps.Count())
                {
                    return false;
                }
            }

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

            bool? passed = null;
            info = null;
            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            TermDocuments termDocs = TermDocuments.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            var 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" };
            var entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.CollectionValued);

            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 entitySet = string.Empty;
            string navigProp = string.Empty;
            EntityTypeElement entityType = new EntityTypeElement();
            foreach (var en in entityTypeElements)
            {
                entitySet = en.EntitySetName;
                var funcs = new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                {
                    AnnotationsHelper.GetExpandRestrictions, AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetDeleteRestrictions
                };

                var restrictions = entitySet.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc, funcs, null, NavigationRoughType.CollectionValued);
                if (!string.IsNullOrEmpty(restrictions.Item1)
                    && null != restrictions.Item2 && restrictions.Item2.Any()
                    && null != restrictions.Item3 && restrictions.Item3.Any())
                {
                    navigProp = restrictions.Item3.First().NavigationPropertyName;
                    entityType = en;
                    break;
                }
            }

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

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySet;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, new List<string>() { navigProp }, out additionalInfos);
            string reqDataStr = reqData.ToString();
            bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode)
            {
                var entityId = additionalInfos.Last().EntityId;
                resp = WebHelper.GetEntity(entityId);
                detail2 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                if (HttpStatusCode.OK == resp.StatusCode && additionalInfos.Count > 1)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Can not get the created entity. ";
                }

                // 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(url), serviceStatus.ServiceDocument, details);

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

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

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

                return passed;
            }

            string entitySet = string.Empty;
            EntityTypeElement entityType = new EntityTypeElement();
            foreach (var en in entityTypeElements)
            {
                entitySet = en.EntitySetName;
                var funcs = new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                {
                    AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetDeleteRestrictions
                };

                var restrictions = entitySet.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc, funcs, null, NavigationRoughType.None);
                if (!string.IsNullOrEmpty(restrictions.Item1)
                    && null != restrictions.Item2 && restrictions.Item2.Any()
                    && null != restrictions.Item3 && restrictions.Item3.Any())
                {
                    entityType = en;
                    break;
                }
            }

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

                return passed;
            }

            string entitySetUrl = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySet;
            string rootUrl = serviceStatus.RootURL.TrimEnd('/') + @"/";
            string url = entitySetUrl;

            Response resp = null;

            bool gotDeltaLink = false;
            string deltaLink = string.Empty;

            while (!gotDeltaLink)
            {
                resp = WebHelper.GetDeltaLink(url, context.RequestHeaders);
                JObject payload;
                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    bool hasNextOrDelta = false;
                    resp.ResponsePayload.TryToJObject(out payload);
                    foreach (JProperty child in payload.Children<JProperty>())
                    {
                        if (child.Name.Equals(Constants.V4OdataDeltaLink))
                        {
                            gotDeltaLink = true;
                            hasNextOrDelta = true;
                            deltaLink = child.Value.ToString();

                            if (Uri.IsWellFormedUriString(deltaLink, UriKind.Relative))
                            {
                                deltaLink = rootUrl + deltaLink;
                            }

                            break;
                        }

                        if (child.Name.Equals(Constants.V4OdataNextLink))
                        {
                            url = child.Value.ToString();
                            hasNextOrDelta = true;

                            if (Uri.IsWellFormedUriString(url, UriKind.Relative))
                            {
                                url = rootUrl + deltaLink;
                            }

                            break;
                        }
                    }

                    if (!hasNextOrDelta)
                        break;
                }
            }

            if (!gotDeltaLink)
            {
                detail.ErrorMessage = "The service does not support delta tracking.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);
                passed = false;

                return passed;
            }

            bool isCreated = false;
            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);
            resp = WebHelper.CreateEntity(entitySetUrl, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
            detail = new ExtensionRuleResultDetail(this.Name, entitySetUrl, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode)
            {
                var entityId = additionalInfos.Last().EntityId;
                resp = WebHelper.GetEntity(entityId);
                detail = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                if (HttpStatusCode.OK == resp.StatusCode)
                {
                    isCreated = true;
                }
            }

            resp = WebHelper.GetEntity(deltaLink);

            if ((isCreated && resp.StatusCode == HttpStatusCode.OK) || resp.StatusCode == HttpStatusCode.NoContent)
            {
                passed = true;
                detail = new ExtensionRuleResultDetail(this.Name, deltaLink, HttpMethod.Get, string.Empty, resp, string.Empty, null);
            }
            else
            {
                passed = false;
                detail = new ExtensionRuleResultDetail(this.Name, deltaLink, HttpMethod.Get, string.Empty, resp, string.Empty, null);
                detail.ErrorMessage = "Cannot get delta changes from the delta link.";
            }

            // Restore the service.
            var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos);

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

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

                return passed;
            }

            string entitySet = string.Empty;
            string navigPropName = string.Empty;
            NavigationRoughType navigPropRoughType = NavigationRoughType.None;
            EntityTypeElement entityType = new EntityTypeElement();
            foreach (var en in entityTypeElements)
            {
                entitySet = en.EntitySetName;
                var funcs = new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                {
                    AnnotationsHelper.GetExpandRestrictions, AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetDeleteRestrictions
                };

                var restrictions = entitySet.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc, funcs, null, NavigationRoughType.None);
                if (!string.IsNullOrEmpty(restrictions.Item1)
                    && null != restrictions.Item2 && restrictions.Item2.Any()
                    && null != restrictions.Item3 && restrictions.Item3.Any())
                {
                    navigPropName = restrictions.Item3.First().NavigationPropertyName;
                    navigPropRoughType = restrictions.Item3.First().NavigationRoughType;
                    entityType = en;
                    break;
                }
            }

            if (string.IsNullOrEmpty(entitySet) || string.IsNullOrEmpty(navigPropName))
            {
                detail.ErrorMessage = "Cannot find an entity set whose entity type has navigation property.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail);

                return passed;
            }

            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
            string updateUrl = serviceStatus.RootURL;
            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('/') + "/" + navigPropName;
                updateUrl = entityId.TrimEnd('/') + "/" + navigPropName + "/$ref";
                bool hasEtag = additionalInfos.Last().HasEtag;

                string navigPropType = MetadataHelper.GetNavigPropertyTypeFromMetadata(navigPropName, entityType.EntityTypeShortName, serviceStatus.MetadataDocument);
                string navigPropTypeShortName = string.Empty;
                if (navigPropRoughType == NavigationRoughType.CollectionValued)
                {
                    navigPropTypeShortName = navigPropType.Substring(navigPropType.IndexOf("(")).GetLastSegment().TrimEnd(')');
                }
                else
                {
                    navigPropTypeShortName = navigPropType.GetLastSegment();
                }
                string nEntitySetName = navigPropName.MapNavigationPropertyNameToEntitySetName(entityType.EntityTypeShortName);
                string nEntitySetUrl = nEntitySetName.MapEntitySetNameToEntitySetURL();
                nEntitySetUrl = serviceStatus.RootURL.TrimEnd('/') + @"/" + nEntitySetUrl;

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

                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)
                {
                    foreach (AdditionalInfo a in additionalInfos)
                    {
                        additionalInfosAll.Add(a);
                    }

                    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 addedNavigRefUrl = string.Empty;

                        if (navigPropRoughType == NavigationRoughType.CollectionValued)
                        {
                            addedNavigRefUrl = updateUrl + @"?$id=" + refEntityId;

                            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);
                        }
                        else if (navigPropRoughType == NavigationRoughType.SingleValued)
                        {
                            addedNavigRefUrl = updateUrl;

                            string refEntityIdUrl = additionalInfos.Last().EntityId;

                            string refEntitySetNameWithEnityID = refEntityIdUrl.Split('/').Last();
                            string refEntityValue = serviceStatus.RootURL.TrimEnd('/') + @"/" + refEntitySetNameWithEnityID;

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

                            resp = WebHelper.UpdateEntity(updateUrl, context.RequestHeaders, navigationRefSet.ToString(), HttpMethod.Put, false);
                            detail = new ExtensionRuleResultDetail(this.Name, updateUrl, HttpMethod.Put, string.Empty, resp, string.Empty, refEntityValue.ToString());
                        }

                        if (HttpStatusCode.NoContent == resp.StatusCode)
                        {
                            resp = WebHelper.GetEntity(addedNavigRefUrl);
                            detail = new ExtensionRuleResultDetail(this.Name, addedNavigRefUrl, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr);

                            if (HttpStatusCode.OK == resp.StatusCode)
                            {
                                resp = WebHelper.DeleteEntity(addedNavigRefUrl, context.RequestHeaders, false);

                                if (HttpStatusCode.NoContent == resp.StatusCode)
                                {
                                    passed = true;
                                    detail = new ExtensionRuleResultDetail(this.Name, addedNavigRefUrl, HttpMethod.Delete, string.Empty, resp, string.Empty, null);
                                }
                                else
                                {
                                    passed = false;
                                    detail.ErrorMessage = "Cannot remove the reference to the entity.";
                                }
                            }
                            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.";
                }

                // 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(url), serviceStatus.ServiceDocument, details);

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

            bool? passed = null;
            info = null;

            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            var detail1 = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL, HttpMethod.Post, string.Empty);
            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.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;
            }

            EntityTypeElement eTypeElement = new EntityTypeElement();

            Dictionary<string, string> entityAndpaths = MetadataHelper.StreamPropertyEntities(serviceStatus.MetadataDocument);

            string relativePath = string.Empty;

            foreach (var en in entityTypeElements)
            {
                if (entityAndpaths.Keys.Contains(en.EntityTypeShortName) && !string.IsNullOrEmpty(eTypeElement.EntitySetName))
                {
                    eTypeElement = en;
                    relativePath = entityAndpaths[en.EntityTypeShortName];
                    break;
                }
            }

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

                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();
            bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode || HttpStatusCode.NoContent == resp.StatusCode)
            {
                url = additionalInfos.Last().EntityId.TrimEnd('/') + "/" + relativePath.TrimEnd('/');
                if(string.IsNullOrEmpty(url))
                {
                    passed = false;
                    detail3.ErrorMessage = string.Format("Fail to find the stream property read and edit URL.", resp.StatusCode);
                }

                resp = WebHelper.Get(new Uri(url), null, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, null);
                detail2 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp);
                if ((resp.StatusCode.HasValue && HttpStatusCode.OK == resp.StatusCode) || HttpStatusCode.NoContent == resp.StatusCode)
                {
                    string xpath = string.Format(@"//*[local-name()='Property' and @Name='{0}' and @Type='Edm.Stream']", url.Split('/').Last());
                    XElement md = XElement.Parse(serviceStatus.MetadataDocument);
                    XElement propElem = md.XPathSelectElement(xpath, ODataNamespaceManager.Instance);

                    bool nullable = true;

                    if( propElem.Attribute("Nullable") !=null && propElem.Attribute("Nullable").Value.ToLower().Equals("false"))
                    {
                        nullable = false;
                    }

                    resp = WebHelper.DeleteEntity(url, context.RequestHeaders, additionalInfos.Last().HasEtag);
                    detail3 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Delete, string.Empty, resp, string.Empty, "Successfully updated the stream of the image.");

                    if (null != resp && (HttpStatusCode.NoContent == resp.StatusCode || (nullable == false && Convert.ToInt32(resp.StatusCode) >= 400)))
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail3.ErrorMessage = string.Format("HTTP delete to delete the stream property value to null failed with the error {0}.", resp.StatusCode);
                    }

                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Get stream property failed from above URI.";
                }

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

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

            return passed;
        }
Пример #7
0
        /// <summary>
        /// Parse a parameter with the type XElement.
        /// </summary>
        /// <param name="entityTypeElement">A parameter with the type XElement.</param>
        /// <returns>Returns the result with the type EntityTypeElement.</returns>
        public static EntityTypeElement Parse(XElement entityTypeElement)
        {
            if (entityTypeElement == null)
            {
                throw new ArgumentNullException("entityTypeElement", "The value of the input parameter 'entityTypeElement' MUST NOT be null.");
            }

            if (!entityTypeElement.Name.LocalName.Equals("EntityType"))
            {
                throw new ArgumentException("The local-name of the input parameter 'entityTypeElement' is incorrect.");
            }

            // Set the alias and namespace of the entity-type element.
            var    aliasAndNamespace   = entityTypeElement.GetAliasAndNamespace();
            string entityTypeNamespace = aliasAndNamespace.Namespace;
            string entityTypeAlias     = aliasAndNamespace.Alias;

            // Set the entity-type short name.
            string entityTypeShortName =
                entityTypeElement.Attribute("Name") != null?
                entityTypeElement.GetAttributeValue("Name") :
                    null;

            // Set the entity-type full name.
            string entityTypeFullName = entityTypeShortName;

            if (!string.IsNullOrEmpty(entityTypeNamespace))
            {
                entityTypeFullName = string.Format("{0}.{1}", entityTypeNamespace, entityTypeShortName);
            }
            else if (!string.IsNullOrEmpty(entityTypeAlias))
            {
                entityTypeFullName = string.Format("{0}.{1}", entityTypeAlias, entityTypeShortName);
            }

            // Set the base-type full name.
            string baseTypeFullName =
                entityTypeElement.Attribute("BaseType") != null?
                entityTypeElement.GetAttributeValue("BaseType") :
                    null;

            // Set the HasStream attribute of the entity-type element.
            bool hasStream =
                entityTypeElement.Attribute("HasStream") != null?
                Convert.ToBoolean(entityTypeElement.GetAttributeValue("HasStream")) :
                    false;

            // Set the OpenType attribute of the entity-type element.
            bool isOpenType =
                entityTypeElement.Attribute("OpenType") != null?
                Convert.ToBoolean(entityTypeElement.GetAttributeValue("OpenType")) :
                    false;

            EntityTypeElement     baseTypeElem     = null;
            List <NormalProperty> normalProperties = new List <NormalProperty>();
            var propsDict = new Dictionary <string, string>();
            List <NavigProperty> navigProperties = new List <NavigProperty>();
            var           navigPropsDict         = new Dictionary <string, string>();
            List <string> keyPropNames           = new List <string>();

            if (!string.IsNullOrEmpty(baseTypeFullName))
            {
                var baseTypeShortName = baseTypeFullName.GetLastSegment();
                var baseTypeElement   = entityTypeElement.Parent != null?
                                        entityTypeElement.Parent.Elements()
                                        .Where(et => null != et.Attribute("Name") && et.GetAttributeValue("Name").Equals(baseTypeShortName))
                                        .Select(et => et).First() : null;

                baseTypeElem = EntityTypeElement.Parse(baseTypeElement);
                normalProperties.AddRange(baseTypeElem.NormalProperties);
                foreach (var dict in baseTypeElem.PropertiesDict)
                {
                    propsDict.Add(dict.Key, dict.Value);
                }

                navigProperties.AddRange(baseTypeElem.NavigationProperties);
                foreach (var dict in baseTypeElem.NavigPropertiesDict)
                {
                    navigPropsDict.Add(dict.Key, dict.Value);
                }

                keyPropNames.AddRange(baseTypeElem.KeyProperties.Select(kp => kp.PropertyName));
            }

            var allProperties = entityTypeElement.Elements();

            foreach (var prop in allProperties)
            {
                // Records the key properties of the entity-type.
                if (prop.Name.LocalName.Equals("Key"))
                {
                    var keyProps = prop.Elements();
                    if (null == keyProps || !keyProps.Any())
                    {
                        continue;
                    }

                    foreach (var kp in keyProps)
                    {
                        if (null != kp.Attribute("Name"))
                        {
                            var kpName = kp.GetAttributeValue("Name");
                            if (kpName.Contains("/"))
                            {
                                kpName = kpName.Substring(0, kpName.IndexOf('/'));
                            }

                            keyPropNames.Add(kpName);
                        }
                    }
                }
                // Records the properties of the entity-type.
                else if (prop.Name.LocalName.Equals("Property"))
                {
                    string propName = prop.Attribute("Name") != null?prop.GetAttributeValue("Name") : null;

                    string propType = prop.Attribute("Type") != null?prop.GetAttributeValue("Type") : null;

                    bool isKey      = keyPropNames.Contains(propName) ? true : false;
                    bool isNullable = prop.Attribute("Nullable") != null?Convert.ToBoolean(prop.GetAttributeValue("Nullable")) : true;

                    bool   isValueNull = prop.Value == null;
                    string srid        = prop.Attribute("SRID") != null?prop.GetAttributeValue("SRID") : null;

                    propsDict.Add(propName, entityTypeFullName);
                    normalProperties.Add(new NormalProperty(propName, propType, isKey, isNullable, isValueNull, srid));
                }
                // Records the navigation properties of the entity-type.
                else if (prop.Name.LocalName.Equals("NavigationProperty"))
                {
                    string navigPropName = prop.Attribute("Name") != null?prop.GetAttributeValue("Name") : null;

                    string navigPropType = prop.Attribute("Type") != null?prop.GetAttributeValue("Type") : null;

                    string navigPropPartner = prop.Attribute("Partner") != null?prop.GetAttributeValue("Partner") : null;

                    navigPropsDict.Add(navigPropName, entityTypeFullName);
                    navigProperties.Add(new NavigProperty(navigPropName, navigPropType, navigPropPartner));
                }
            }

            return(new EntityTypeElement(entityTypeShortName, normalProperties, propsDict, navigProperties, navigPropsDict, entityTypeNamespace, entityTypeAlias, hasStream, isOpenType, baseTypeFullName));
        }
        /// <summary>
        /// Verifies the service implementation feature.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if the service implementation feature passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

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

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

                return passed;
            }

            string entitySet = string.Empty;
            string navigProp = string.Empty;
            NavigationRoughType navigRoughType = NavigationRoughType.None;
            EntityTypeElement entityType = new EntityTypeElement();
            foreach (var en in entityTypeElements)
            {
                if(!string.IsNullOrEmpty(en.EntitySetName) && en.NavigationProperties.Count > 0)
                {
                    foreach (NavigProperty np in en.NavigationProperties)
                    {
                        string npTypeFullName = np.NavigationPropertyType.RemoveCollectionFlag();

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

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

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

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySet;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, new List<string>() { navigProp }, out additionalInfos);
            string reqDataStr = reqData.ToString();
            bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos);
            detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);
            if (HttpStatusCode.Created == resp.StatusCode)
            {
                var mainEntityID = additionalInfos.Last().EntityId;
                string entityId = additionalInfos.First().EntityId;

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

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

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

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

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

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

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

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

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

            return passed;
        }