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