/// <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> /// 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> /// Initializes the NavigProperty class. /// </summary> /// <param name="navigPropName"></param> /// <param name="navigPropType"></param> /// <param name="navigPropPartner"></param> public NavigProperty(string navigPropName, string navigPropType, string navigPropPartner) { if (string.IsNullOrEmpty(navigPropName)) { throw new ArgumentNullException("navigPropName", "The value of the input parameter 'navigPropName' MUST NOT be null or empty."); } if (string.IsNullOrEmpty(navigPropType)) { throw new ArgumentNullException("navigPropType", "The value of the input parameter 'navigPropType' MUST NOT be null or empty."); } this.navigationPropertyName = navigPropName; this.navigationPropertyType = navigPropType; this.navigationPropertyPartner = navigPropPartner; this.navigationRoughType = this.navigationPropertyType.StartsWith(@"Collection(") && this.navigationPropertyType.EndsWith(@")") ? Helper.NavigationRoughType.CollectionValued : Helper.NavigationRoughType.SingleValued; }
/// <summary> /// Verifies the service implementation feature. /// </summary> /// <param name="context">The Interop service context</param> /// <param name="info">out parameter to return violation information when rule does not pass</param> /// <returns>true if the service implementation feature passes; false otherwise</returns> public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info) { if (context == null) { throw new ArgumentNullException("context"); } bool?passed = null; info = null; ServiceStatus serviceStatus = ServiceStatus.GetInstance(); TermDocuments termDocs = TermDocuments.GetInstance(); DataFactory dFactory = DataFactory.Instance(); var detail = new ExtensionRuleResultDetail(this.Name, serviceStatus.RootURL, HttpMethod.Post, string.Empty); List <string> keyPropertyTypes = new List <string>() { "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String" }; var entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None); if (null == entityTypeElements || 0 == entityTypeElements.Count()) { detail.ErrorMessage = "To verify this rule it expects an entity type with Int32/Int64/Int16/Guid/String key property, but there is no this entity type in metadata so can not verify this rule."; info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail); return(passed); } string entitySet = string.Empty; string navigProp = string.Empty; NavigationRoughType navigRoughType = NavigationRoughType.None; EntityTypeElement entityType = new EntityTypeElement(); foreach (var en in entityTypeElements) { if (!string.IsNullOrEmpty(en.EntitySetName) && en.NavigationProperties.Count > 0) { foreach (NavigProperty np in en.NavigationProperties) { string npTypeFullName = np.NavigationPropertyType.RemoveCollectionFlag(); XmlDocument metadata = new XmlDocument(); metadata.LoadXml(context.MetadataDocument); if (context.ContainsExternalSchema) { metadata.LoadXml(context.MergedMetadataDocument); } XmlNode npXmlNode; MetadataHelper.GetTypeNode(npTypeFullName, metadata, out npXmlNode); if (!(npXmlNode.Attributes["HasStream"] != null ? npXmlNode.Attributes["HasStream"].Value.Equals("true") : false)) { navigProp = np.NavigationPropertyName; navigRoughType = np.NavigationRoughType; entityType = en; entitySet = en.EntitySetName; break; } } if (!string.IsNullOrEmpty(entitySet)) { break; } } } if (string.IsNullOrEmpty(entitySet) || string.IsNullOrEmpty(navigProp)) { detail.ErrorMessage = "Cannot find an entity-set URL which can be execute the deep insert operation on it."; info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail); return(passed); } string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySet; var additionalInfos = new List <AdditionalInfo>(); var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, new List <string>() { navigProp }, out additionalInfos); string reqDataStr = reqData.ToString(); bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag); var resp = WebHelper.CreateEntity(url, context.RequestHeaders, reqData, isMediaType, ref additionalInfos); detail = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr); if (HttpStatusCode.Created == resp.StatusCode) { var mainEntityID = additionalInfos.Last().EntityId; string entityId = additionalInfos.First().EntityId; string refUrl = mainEntityID.TrimEnd('/') + "/" + navigProp + "/$ref"; string refEntitySetNameWithEnityID = entityId.Split('/').Last(); string refEntityValue = serviceStatus.RootURL.TrimEnd('/') + @"/" + refEntitySetNameWithEnityID; string refEntityID = refEntitySetNameWithEnityID.Contains("'") ? refEntitySetNameWithEnityID.Split('\'')[1] : refEntitySetNameWithEnityID.Split('(')[1].TrimEnd(')'); resp = WebHelper.GetEntity(mainEntityID); detail = new ExtensionRuleResultDetail(this.Name, mainEntityID, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr); if (HttpStatusCode.OK == resp.StatusCode && additionalInfos.Count > 1) { resp = WebHelper.GetEntity(refUrl); detail = new ExtensionRuleResultDetail(this.Name, refUrl, HttpMethod.Get, string.Empty, resp, string.Empty, reqDataStr); if (HttpStatusCode.OK == resp.StatusCode) { JObject refPayload; resp.ResponsePayload.TryToJObject(out refPayload); bool created = false; if (navigRoughType == NavigationRoughType.CollectionValued) { var entries = JsonParserHelper.GetEntries(refPayload); if (entries != null) { foreach (JObject entry in entries) { foreach (JProperty prop in entry.Children()) { if (prop.Name.Equals(Constants.V4OdataId)) { created = prop.Value.ToString().Contains(refEntityID); if (created) { passed = true; break; } } } } } } else { foreach (JProperty prop in refPayload.Children()) { if (prop.Name.Equals(Constants.V4OdataId)) { created = prop.Value.ToString().Contains(refEntityID); if (created) { passed = true; break; } } } } if (!created) { passed = false; detail.ErrorMessage = string.Format("The HTTP deep insert failed to add a reference to a collection-valued navigation property failed."); } } } else { passed = false; detail.ErrorMessage = "Can not get the created entity. "; } // Restore the service. var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos); } else { passed = false; detail.ErrorMessage = "Created the new entity failed for above URI. "; } var details = new List <ExtensionRuleResultDetail>() { detail }; info = new ExtensionRuleViolationInfo(new Uri(url), serviceStatus.ServiceDocument, details); return(passed); }
/// <summary> /// Gets filter restrictions. /// </summary> /// <param name="metadataDoc">The metadata document.</param> /// <param name="vocCapabilitiesDoc">The vocabulary-capabilities document.</param> /// <param name="primitiveTypes">The expected primitive types' list.</param> /// <param name="navigationType">The expected navigation rough type.</param> /// <param name="methods">The delegate function to filter other term restrictions with the type 'Core.Tag'.</param> /// <returns>Returns an appropriate tuple element which supports filter restrictions.</returns> public static Tuple<string, List<NormalProperty>, List<NavigProperty>> GetFilterRestrictions( string metadataDoc, string vocCapabilitiesDoc, List<string> primitiveTypes = null, NavigationRoughType navigationType = NavigationRoughType.None, List<Func<string, string, List<string>, bool?>> methods = null) { if (string.IsNullOrEmpty(metadataDoc) || !metadataDoc.IsXmlPayload() || string.IsNullOrEmpty(vocCapabilitiesDoc) || !vocCapabilitiesDoc.IsXmlPayload()) { return new Tuple<string, List<NormalProperty>, List<NavigProperty>>(string.Empty, new List<NormalProperty>(), new List<NavigProperty>()); } var feeds = MetadataHelper.GetFeeds(metadataDoc); if (!feeds.Any()) { return new Tuple<string, List<NormalProperty>, List<NavigProperty>>(string.Empty, new List<NormalProperty>(), new List<NavigProperty>()); } string entitySet = string.Empty; List<NormalProperty> normalProps = new List<NormalProperty>(); List<NavigProperty> navigationProps = new List<NavigProperty>(); foreach (var f in feeds) { string entityTypeShortName = f.MapEntitySetNameToEntityTypeShortName(); bool flag = true; if (null != methods && methods.Any()) { foreach (var func in methods) { if (true != func(f, metadataDoc, new List<string>() { vocCapabilitiesDoc })) { flag = false; break; } } } if (string.IsNullOrEmpty(entityTypeShortName) || !flag) { continue; } var props = MetadataHelper.GetNormalProperties(metadataDoc, entityTypeShortName).ToList(); var nprops = MetadataHelper.GetNavigationProperties(metadataDoc, entityTypeShortName).ToList(); if (null == props || !props.Any() || null == nprops || !nprops.Any()) { continue; } if (null != primitiveTypes && primitiveTypes.Any() && !AnnotationsHelper.IsSuitableProperty(props, primitiveTypes)) { continue; } if (NavigationRoughType.None != navigationType && !AnnotationsHelper.IsSuitableNavigationProperty(nprops, navigationType)) { continue; } if (AnnotationsHelper.GetFilterRestrictions(f, metadataDoc, vocCapabilitiesDoc, props, nprops) && props.Any() && nprops.Any()) { entitySet = f; normalProps.AddRange(props); navigationProps.AddRange(nprops); break; } } return new Tuple<string, List<NormalProperty>, List<NavigProperty>>(entitySet, normalProps, navigationProps); }
/// <summary> /// Verify whether the navigation properties' list contain specified navigation rough type. /// </summary> /// <param name="navigProperties">The navigation properties.</param> /// <param name="navigRoughType">The navigation rough type.</param> /// <returns>Returns the boolean result.</returns> private static bool IsSuitableNavigationProperty(List<NavigProperty> navigProperties, NavigationRoughType navigRoughType) { bool flag = false; List<NavigProperty> nprops = new List<NavigProperty>(); foreach (var np in navigProperties) { if (navigRoughType == np.NavigationRoughType) { flag = true; nprops.Add(np); } } navigProperties.Clear(); navigProperties.AddRange(nprops); return flag; }
/// <summary> /// Filter out unsuitable navigation properties. /// </summary> /// <param name="navigationRoughType">The navigation rough type.</param> /// <param name="restrictions">All the appropriate entity-sets which match the specified restrictions.</param> /// (Note: This parameter is a reference parameter, but without 'ref' flag before its type.)</param> /// <returns>Returns whether find any entity-sets match the specified restrictions or not.</returns> public static bool IsSuitableNavigationProperty( NavigationRoughType navigationRoughType, ref Dictionary<string, Tuple<List<NormalProperty>, List<NavigProperty>>> restrictions) { if (NavigationRoughType.None == navigationRoughType || null == restrictions || !restrictions.Any()) { return false; } var result = new Dictionary<string, Tuple<List<NormalProperty>, List<NavigProperty>>>(); foreach (var r in restrictions) { List<NavigProperty> nprops = new List<NavigProperty>(); foreach (var np in r.Value.Item2) { if (navigationRoughType == np.NavigationRoughType) { nprops.Add(np); } } result.Add(r.Key, new Tuple<List<NormalProperty>, List<NavigProperty>>(r.Value.Item1, nprops)); } restrictions = result; return result.Any() ? true : false; }
/// <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); detail.URI = nEntitySetUrl; detail.ResponsePayload = resp.ResponsePayload; detail.ResponseHeaders = resp.ResponseHeaders; detail.HTTPMethod = "GET"; detail.ResponseStatusCode = resp.StatusCode.ToString(); 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()); detail.URI = updateUrl; detail.ResponsePayload = resp.ResponsePayload; detail.ResponseHeaders = resp.ResponseHeaders; detail.HTTPMethod = "PUT"; detail.ResponseStatusCode = resp.StatusCode.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(); 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); 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; NavigationRoughType navigPropRoughType = NavigationRoughType.None; 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; navigPropRoughType = np.NavigationRoughType; break; } } if (!string.IsNullOrEmpty(navigPropRelatedEntityTypeKeyName) && !string.IsNullOrEmpty(navigPropRelatedEntitySetUrl)) { string entitySetUrl = et.EntitySetName.MapEntitySetNameToEntitySetURL(); string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl; var resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders); detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.AcceptHeaderJson, serviceStatus.DefaultHeaders), resp); if (resp.StatusCode == HttpStatusCode.OK) { JObject feed; resp.ResponsePayload.TryToJObject(out feed); var entries = JsonParserHelper.GetEntries(feed); DataFactory factory = DataFactory.Instance(); var additionalInfos = new List <AdditionalInfo>(); var reqData = factory.ConstructInsertedEntityData(et.EntitySetName, et.EntityTypeShortName, null, out additionalInfos); string reqDataStr = reqData.ToString(); resp = WebHelper.CreateEntity(url, reqDataStr); detail2 = 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('/') + @"/" + navigPropRelatedEntitySetUrl; resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders); detail3 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp); if (resp.StatusCode == HttpStatusCode.OK) { resp.ResponsePayload.TryToJObject(out feed); var entities = JsonParserHelper.GetEntries(feed); if (null != entities && entities.Any()) { string odataID = entities.First[Constants.V4OdataId] != null ? entities.First[Constants.V4OdataId].Value <string>() : string.Empty; reqDataStr = @"{""" + Constants.V4OdataId + @""" : """ + odataID + @"""}"; url = string.Format("{0}/{1}/$ref", entityId.TrimEnd('/'), navigPropName.TrimEnd('/')); resp = WebHelper.CreateEntity(url, reqDataStr); detail4 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr); if (resp.StatusCode == HttpStatusCode.NoContent) { string deleteUrl = url; if (navigPropRoughType == NavigationRoughType.CollectionValued) { deleteUrl = string.Format("{0}/{1}/$entity?$id={2}", entityId.TrimEnd('/'), navigPropName.TrimEnd('/'), entities[0][Constants.V4OdataId].ToString()); } resp = WebHelper.DeleteEntity(deleteUrl); detail5 = new ExtensionRuleResultDetail(this.Name, deleteUrl, HttpMethod.Delete, string.Empty, resp); if (null != resp && HttpStatusCode.NoContent == resp.StatusCode) { passed = true; } else { passed = false; detail5.ErrorMessage = "Delete the above reference failed."; } } else { passed = false; detail4.ErrorMessage = "Created navigation failed."; } } } else { passed = false; detail3.ErrorMessage = "Can not get the created entity from above URI."; } // Restore the service. var resps = WebHelper.DeleteEntities(context.RequestHeaders, additionalInfos); } else { passed = false; detail2.ErrorMessage = "Created the new entity failed for above URI."; } break; } } } var details = new List <ExtensionRuleResultDetail>() { detail1, detail2, detail3, detail4, detail5 }.RemoveNullableDetails(); info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details); return(passed); }
/// <summary> /// Get the names of appropriate navigation properties from metadata. /// </summary> /// <param name="entityTypeShortName">The entity type short name.</param> /// <param name="metadataDoc">The metadata document.</param> /// <param name="type">The navigation property rough type.</param> /// <returns>Returns the list of navigation property names.</returns> public static List<string> GetAppropriateNavigationPropertyNames(string entityTypeShortName, string metadataDoc, NavigationRoughType type) { if (string.IsNullOrEmpty(entityTypeShortName) || string.IsNullOrEmpty(metadataDoc)) { return null; } List<string> result = new List<string>(); string xpath = string.Format("//*[local-name()='EntityType' and @Name='{0}']/*[local-name()='NavigationProperty']", entityTypeShortName); XElement metadata = XElement.Parse(metadataDoc); var navigProps = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance); if (null == navigProps || !navigProps.Any()) { return null; } foreach (var np in navigProps) { if (null == np.Attribute("Name") || null == np.Attribute("Type")) { break; } if (NavigationRoughType.None == type) { result.Add(np.GetAttributeValue("Name")); } else if (NavigationRoughType.SingleValued == type) { if (!np.GetAttributeValue("Type").StartsWith("Collection(")) { result.Add(np.GetAttributeValue("Name")); } } else { if (np.GetAttributeValue("Type").StartsWith("Collection(")) { result.Add(np.GetAttributeValue("Name")); } } } return result; }
/// <summary> /// Judge whether the entity has specified rough type navigation type property. /// </summary> /// <param name="metadata">The string of metadata document.</param> /// <param name="entityTypeShortName">The short name of entity type.</param> /// <param name="type">The rough type of navigation type.</param> /// <param name="context">The service context.</param> /// <param name="collectionPropNames">Output the list of navigation properties' names.</param> /// <returns> True, if the entity has the navigation type property; false, otherwise. </returns> public static bool HasEntityNavigationProp(string metadata, string entityTypeShortName, NavigationRoughType type, ServiceContext context, out List<string> navigationPropNames) { bool hasBaseType = true; navigationPropNames = new List<string>(); while (hasBaseType) { string typeString = string.Empty; switch (type) { case NavigationRoughType.CollectionValued: typeString = @"and contains(@Type, 'Collection(')"; break; case NavigationRoughType.SingleValued: typeString = @"and not contains(@Type, 'Collection(')"; break; default: typeString = string.Empty; break; } string xpath1 = string.Format(@"//*[local-name()='EntityType' and @Name='{0}']/*[local-name()='NavigationProperty'", entityTypeShortName) + typeString + "]"; XElement md = XElement.Parse(metadata); IEnumerable<XElement> navigationProps = md.XPathSelectElements(xpath1, ODataNamespaceManager.Instance); if (navigationProps != null) { foreach (XElement prop in navigationProps) { navigationPropNames.Add(prop.Attribute("Name").Value); } } xpath1 = string.Format(@"//*[local-name()='EntityType' and @Name='{0}']", entityTypeShortName); XElement entity = md.XPathSelectElement(xpath1, ODataNamespaceManager.Instance); if (null != entity.Attribute("BaseType") && !string.IsNullOrEmpty(entity.Attribute("BaseType").Value)) { string baseEntityQualifiedName = entity.GetAttributeValue("BaseType"); entity = GetTypeDefinitionEleInScope("EntityType", baseEntityQualifiedName, context); entityTypeShortName = entity.Attribute("Name").Value; } else { hasBaseType = false; } } return navigationPropNames != null && navigationPropNames.Any(); }
/// <summary> /// Gets all entity types which were satisfied specified conditions from metadata document. /// </summary> /// <param name="metadataDoc">The metadata document.</param> /// <param name="entityTypePredicate">Assert whether an entity-type matches specified conditions.</param> /// <param name="containedKeyPropSum">The sum of the key properties in current entity-type.</param> /// <param name="containedKeyPropTypes">The contained key property types.</param> /// <param name="containedNorPropTypes">The contained normal property types.</param> /// <param name="containedNavigRoughType">The contained navigation properties' rough-type at least one was satisfied the input value.</param> /// <returns></returns> public static IEnumerable<EntityTypeElement> GetEntityTypes( string metadataDoc, uint? containedKeyPropSum = 1, IEnumerable<string> containedKeyPropTypes = null, IEnumerable<string> containedNorPropTypes = null, NavigationRoughType containedNavigRoughType = NavigationRoughType.None) { if (string.IsNullOrEmpty(metadataDoc)) { return null; } List<EntityTypeElement> result = new List<EntityTypeElement>(); XElement metadata = XElement.Parse(metadataDoc); string xpath = @"//*[local-name()='Schema']/*[local-name()='EntityType']"; var elements = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance); foreach (var ele in elements) { EntityTypeElement entityType = EntityTypeElement.Parse(ele); if (PredicateHelper.EntityTypeAnyPropertiesMeetsSpecifiedConditions(entityType, containedKeyPropSum, containedKeyPropTypes, containedNorPropTypes, containedNavigRoughType)) { result.Add(entityType); } } return result; }