private void UpdateBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues) { List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList(); if (!exactMatches.Any()) { return; } ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath); NamedValue expectedBagValue = exactMatches.Single(); var bagType = memberProperty.PropertyType as QueryCollectionType; if (expectedBagValue.Value == null) { instance.SetValue(memberProperty.Name, bagType.NullValue); this.unusedNamedValuePaths.Remove(expectedBagValue.Name); } else if (expectedBagValue.Value == EmptyData.Value) { QueryCollectionValue value = bagType.CreateCollectionWithValues(new QueryValue[] { }); instance.SetValue(memberProperty.Name, value); this.unusedNamedValuePaths.Remove(expectedBagValue.Name); } }
private void CompareBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues) { List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList(); if (!exactMatches.Any()) { return; } ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath); QueryCollectionValue actualQueryBagValue = instance.GetCollectionValue(memberProperty.Name); NamedValue expectedBagValue = exactMatches.Single(); if (expectedBagValue.Value == null) { this.WriteErrorIfNotNull(propertyPath, actualQueryBagValue); this.unusedNamedValuePaths.Remove(propertyPath); } else if (expectedBagValue.Value == EmptyData.Value) { if (!this.WriteErrorIfNull(propertyPath, actualQueryBagValue)) { this.WriteErrorIfNotEqual(propertyPath, actualQueryBagValue.Elements.Count, 0, "Expected zero elements in BagProperty"); this.unusedNamedValuePaths.Remove(propertyPath); } } }
/// <summary> /// Extends value population to include stream data /// </summary> /// <param name="row">The row containing the data</param> /// <param name="instance">The structural instacne</param> protected override void PopulateInstanceFromRow(EntitySetDataRow row, QueryStructuralValue instance) { base.PopulateInstanceFromRow(row, instance); var rowWithStreams = row as EntitySetDataRowWithStreams; if (rowWithStreams != null) { foreach (var stream in rowWithStreams.Streams) { if (stream.IsEditLinkBasedOnConvention) { ExceptionUtilities.CheckObjectNotNull(this.LinkGenerator, "Cannot compute convention-based edit link without injected generator"); stream.EditLink = this.LinkGenerator.GenerateStreamEditLink(instance, stream.Name); // for the default stream, there must always be a self-link if (stream.Name == null && stream.SelfLink == null) { stream.SelfLink = stream.EditLink; } } instance.SetStreamValue(stream.Name, stream.ContentType, stream.ETag, stream.EditLink, stream.SelfLink, stream.Content); } } instance.MarkDynamicPropertyValues(); }
/// <summary> /// Compares a queryStructualValue to namedValues /// Throws a DataComparisonException if values don't match /// </summary> /// <param name="queryStructuralValue">QueryStructural Value to compare</param> /// <param name="namedValues">NamedValues to compare</param> /// <param name="scalarComparer">The comparer to use for scalar values</param> public void Compare(QueryStructuralValue queryStructuralValue, IEnumerable<NamedValue> namedValues, IQueryScalarValueToClrValueComparer scalarComparer) { ExceptionUtilities.CheckArgumentNotNull(queryStructuralValue, "queryStructuralValue"); ExceptionUtilities.CheckArgumentNotNull(namedValues, "namedValues"); ExceptionUtilities.CheckArgumentNotNull(scalarComparer, "scalarComparer"); this.errors = new List<string>(); this.unusedNamedValuePaths = new List<string>(namedValues.Select(s => s.Name).ToArray()); this.comparer = scalarComparer; this.CompareValues(queryStructuralValue, namedValues, null); foreach (string propertyPath in this.unusedNamedValuePaths) { this.errors.Add(string.Format(CultureInfo.InvariantCulture, "Value for propertyPath {0} was not compared against queryStructuralValue", propertyPath)); } string errorMessage = null; foreach (string error in this.errors) { if (errorMessage == null) { errorMessage = error; } else { errorMessage = errorMessage + ", \r\n" + error; } } if (errorMessage != null) { throw new DataComparisonException(errorMessage); } }
private void PopulateNavigations(EntitySetDataRow row, QueryStructuralValue instance) { var entityInstance = instance as QueryEntityValue; EntitySetData entitySetData = row.Parent; EntityContainerData containerData = entitySetData.Parent; foreach (AssociationSet associationSet in containerData.EntityContainer.AssociationSets) { foreach (var fromSetEnd in associationSet.Ends.Where(e => e.EntitySet == entitySetData.EntitySet)) { var fromEntityType = fromSetEnd.AssociationEnd.EntityType; if (!row.EntityType.IsKindOf(fromEntityType)) { continue; } var toSetEnd = associationSet.GetOtherEnd(fromSetEnd); this.PopulateNavigateResult(row, entityInstance, associationSet, fromSetEnd, toSetEnd); // if Navigation property exists, populate it as well var navProp = row.EntityType.AllNavigationProperties.SingleOrDefault(p => p.Association == associationSet.AssociationType && p.ToAssociationEnd == toSetEnd.AssociationEnd); if (navProp != null) { instance.SetValue(navProp.Name, entityInstance.GetNavigateResult(navProp.Association, navProp.ToAssociationEnd)); } } } }
private void UpdateVisibleEntitiesGraphForEntity(QueryStructuralValue entityValue, string path, string[] spanPaths) { if (!this.VisibleEntitiesGraph.ContainsKey(entityValue)) { if (entityValue.IsNull) { this.VisibleEntitiesGraph[entityValue] = entityValue; return; } else { var visibleEntity = entityValue.Type.CreateNewInstance(); this.PopulateNonNavigationPropertiesOfVisibleEntity(entityValue, visibleEntity); this.VisibleEntitiesGraph[entityValue] = visibleEntity; } } if (!entityValue.IsNull) { foreach (QueryProperty property in entityValue.Type.Properties) { string newPath = this.CreateNewPath(path, property.Name); if (this.IsValidSpanPath(newPath, spanPaths)) { var originalValue = entityValue.GetValue(property.Name); this.UpdateVisibleEntitiesGraphForQueryValue(originalValue, newPath, spanPaths); } } } }
/// <summary> /// Verifies the default stream. /// </summary> /// <param name="dataContext">The data context.</param> /// <param name="queryEntityValue">The query entity value.</param> /// <param name="ed">The entity descriptor.</param> /// <param name="continuation">The stream descriptor continuation.</param> private void VerifyDefaultStream(DataServiceContext dataContext, QueryStructuralValue queryEntityValue, EntityDescriptor ed, IAsyncContinuation continuation) { var expectedStreamValue = queryEntityValue.GetDefaultStreamValue(); this.VerifyStreamDescriptorValues(expectedStreamValue, null, null, ed.StreamETag, ed.EditStreamUri, ed.ReadStreamUri); var expectedReadStreamUri = GetExpectedReadStreamUri(expectedStreamValue); Uri readStreamUri = dataContext.GetReadStreamUri(ed.Entity); this.Assert.AreEqual(expectedReadStreamUri, readStreamUri, "Read stream uri did not match for default stream"); dataContext.GetReadStream( continuation, this.isAsynchronous, ed.Entity, null, new DataServiceRequestArgs() { }, response => { UpdateStreamValueFromHeaders(expectedStreamValue, response); this.VerifyStreamDescriptorValues(expectedStreamValue, null, null, ed.StreamETag, ed.EditStreamUri, ed.ReadStreamUri); // skip this verification when using payload driven verification since we don't have the expected content for streams if (!this.DataProviderSettings.UsePayloadDrivenVerification) { this.Assert.IsTrue(this.VerifyStreams(expectedStreamValue, response), "Failed to compare the default stream"); } continuation.Continue(); }); }
/// <summary> /// Verifies the named streams. /// </summary> /// <param name="dataContext">The data context.</param> /// <param name="queryEntityValue">The query entity value.</param> /// <param name="streamProperty">The stream property.</param> /// <param name="ed">The entity descriptor</param> /// <param name="continuation">The stream descriptor continuation.</param> private void VerifyNamedStreams(DataServiceContext dataContext, QueryStructuralValue queryEntityValue, QueryProperty streamProperty, EntityDescriptor ed, IAsyncContinuation continuation) { var expectedStreamValue = queryEntityValue.GetStreamValue(streamProperty.Name); var streamDescriptor = ed.StreamDescriptors.SingleOrDefault(s => s.StreamLink.Name == streamProperty.Name); this.Assert.IsNotNull(streamDescriptor, "Entity missing stream descriptor for stream '{0}'", streamProperty.Name); this.VerifyStreamLink(expectedStreamValue, streamDescriptor.StreamLink); var expectedReadStreamUri = GetExpectedReadStreamUri(expectedStreamValue); Uri readStreamUri = dataContext.GetReadStreamUri(ed.Entity, streamProperty.Name); this.Assert.AreEqual(expectedReadStreamUri, readStreamUri, "Read stream uri did not match for stream '{0}'", streamProperty.Name); dataContext.GetReadStream( continuation, this.isAsynchronous, ed.Entity, streamProperty.Name, new DataServiceRequestArgs() { }, response => { UpdateStreamValueFromHeaders(expectedStreamValue, response); this.VerifyStreamLink(expectedStreamValue, streamDescriptor.StreamLink); // skip this verification when using payload driven verification since we don't have the expected content for streams if (!this.DataProviderSettings.UsePayloadDrivenVerification) { this.Assert.IsTrue(this.VerifyStreams(expectedStreamValue, response), "Failed to compare value of stream '{0}'", streamProperty.Name); } continuation.Continue(); }); }
/// <summary> /// Adds stream properties to structural value. /// </summary> /// <param name="entity">The target structural value.</param> /// <param name="payload">The payload with the entity values.</param> /// <param name="xmlBaseAnnotations">The xml base annotations ancestors and local element</param> private void AddStreamProperties(QueryStructuralValue entity, EntityInstance payload, IEnumerable <XmlBaseAnnotation> xmlBaseAnnotations) { ExceptionUtilities.CheckArgumentNotNull(entity, "entity"); ExceptionUtilities.CheckArgumentNotNull(payload, "payload"); var xmlBaseAnnotationsList = xmlBaseAnnotations.ToList(); if (payload.IsMediaLinkEntry()) { string editLink = this.NormalizeLinkFromPayload(payload.StreamEditLink, xmlBaseAnnotationsList); string selfLink = this.NormalizeLinkFromPayload(payload.StreamSourceLink, xmlBaseAnnotationsList); entity.SetStreamValue(null, payload.StreamContentType, payload.StreamETag, editLink, selfLink, new byte[0]); } foreach (var stream in payload.Properties.OfType <NamedStreamInstance>()) { string contentType = stream.SourceLinkContentType; if (contentType == null) { contentType = stream.EditLinkContentType; } string editLink = this.NormalizeLinkFromPayload(stream.EditLink, xmlBaseAnnotationsList); string selfLink = this.NormalizeLinkFromPayload(stream.SourceLink, xmlBaseAnnotationsList); entity.SetStreamValue(stream.Name, contentType, stream.ETag, editLink, selfLink, new byte[0]); } }
/// <summary> /// Visits a QueryStructuralValue and returns the clr value of the structural value /// </summary> /// <param name="value">The QueryStructuralValue which contains the clr value of the structural type : complex/entity type</param> /// <returns>The clr instance of the structural value</returns> public object Visit(QueryStructuralValue value) { object clrInstance = null; if (this.objectLookup.TryGetValue(value, out clrInstance)) { return(clrInstance); } ExceptionUtilities.CheckObjectNotNull(value.Type as IQueryClrType, "Structural type does not implement IQueryClrType"); IQueryClrType clrTypeQueryable = value.Type as IQueryClrType; Type clrType = clrTypeQueryable.ClrType; ExceptionUtilities.CheckObjectNotNull(clrType, "ClrType should not be null"); clrInstance = clrType.GetConstructor(Type.EmptyTypes).Invoke(null); this.objectLookup.Add(value, clrInstance); foreach (var member in value.MemberNames) { QueryValue queryValue = value.GetValue(member); var memberValue = queryValue.Accept(this); PropertyInfo memberProperty = clrType.GetProperty(member); memberProperty.SetValue(clrInstance, memberValue, null); } return(clrInstance); }
private QueryValue BuildCollectionQueryValue(string propertyPath, QueryCollectionType queryCollectionType, EntitySetDataRow row) { QueryScalarType scalarElementDataType = queryCollectionType.ElementType as QueryScalarType; QueryComplexType complexTypeElementDataType = queryCollectionType.ElementType as QueryComplexType; List <QueryValue> queryValues = new List <QueryValue>(); if (scalarElementDataType != null) { int i = 0; while (row.PropertyPaths.Any(pp => pp == propertyPath + i)) { var value = row[propertyPath + i]; queryValues.Add(scalarElementDataType.CreateValue(value)); i++; } } else { ExceptionUtilities.CheckObjectNotNull(complexTypeElementDataType, "PropertyPath '{0}' is an invalid type '{1}'", propertyPath, queryCollectionType.ElementType); int i = 0; while (row.PropertyPaths.Where(pp => pp.StartsWith(propertyPath + i, StringComparison.Ordinal)).Count() > 0) { QueryStructuralValue complexChildInstance = complexTypeElementDataType.CreateNewInstance(); this.BuildStructuralPropertiesQueryValue(complexChildInstance, propertyPath + i + ".", complexTypeElementDataType.ComplexType.Properties, complexTypeElementDataType.Properties, row); queryValues.Add(complexChildInstance); i++; } } return(queryCollectionType.CreateCollectionWithValues(queryValues.ToArray())); }
/// <summary> /// Removes the the entity with the given key from the given the given property on the given structural value /// </summary> /// <param name="keyToRemove">The key of the entity to remove</param> /// <param name="related">The structural value to remove from</param> /// <param name="navigation">The property to remove from</param> private void RemoveFromNavigationProperty(EntityDataKey keyToRemove, QueryStructuralValue related, NavigationProperty navigation) { if (navigation.ToAssociationEnd.Multiplicity == EndMultiplicity.Many) { var collection = related.GetCollectionValue(navigation.Name); // remove the element with the given key from the collection, if it is present foreach (var element in collection.Elements.Cast <QueryStructuralValue>().ToList()) { if (this.GetEntityKey(element).Equals(keyToRemove)) { collection.Elements.Remove(element); } } } else { // if the value's key matches, set it to null var value = related.GetStructuralValue(navigation.Name); if (!value.IsNull && this.GetEntityKey(value).Equals(keyToRemove)) { related.SetValue(navigation.Name, value.Type.NullValue); } } }
/// <summary> /// Gets the expected query value for the action request /// </summary> /// <param name="initialExpectedResults">Initial expected values for an action</param> /// <param name="parameterValues">Parameter values for the action</param> /// <returns>A query Value that is the expected value</returns> public QueryValue GetExpectedQueryValue(QueryValue initialExpectedResults, params QueryValue[] parameterValues) { ExceptionUtilities.CheckArgumentNotNull(initialExpectedResults, "initialExpectedResults"); QueryStructuralValue initialStructuralValue = initialExpectedResults as QueryStructuralValue; ExceptionUtilities.CheckArgumentNotNull(initialStructuralValue, "initialStructuralValue"); QueryScalarValue initialScalarValue = initialStructuralValue.GetScalarValue(this.IntegerProperty); ExceptionUtilities.CheckArgumentNotNull(initialScalarValue, "initialScalarValue"); int intPropertyValue = (int)initialScalarValue.Value; ExceptionUtilities.CheckArgumentNotNull(intPropertyValue, "intPropertyValue"); if (intPropertyValue != int.MaxValue) { initialStructuralValue.SetPrimitiveValue(this.IntegerProperty, intPropertyValue + 1); } else { initialStructuralValue.SetPrimitiveValue(this.IntegerProperty, 0); } return(initialStructuralValue); }
private void ComparePrimitiveBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable <NamedValue> namedValues) { int i = 0; var collection = instance.GetCollectionValue(memberProperty.Name); if (!this.WriteErrorIfNull(propertyPath, collection)) { List <NamedValue> primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList(); while (primitiveItemNamedValues.Any()) { if (i < collection.Elements.Count) { var scalarQueryValue = collection.Elements[i] as QueryScalarValue; ExceptionUtilities.Assert(primitiveItemNamedValues.Count() < 2, "Should not get more than one value for a primitive Bag item for path '{0}'", propertyPath + "." + i); var value = primitiveItemNamedValues.Single(); this.WriteErrorIfNotEqual(propertyPath, value.Value, scalarQueryValue); this.unusedNamedValuePaths.Remove(value.Name); } i++; primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList(); } this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath); } }
/// <summary> /// Visits the QueryStructuralValue /// </summary> /// <param name="value">Value to visit</param> /// <returns>a string that represents a query structural value</returns> public string Visit(QueryStructuralValue value) { var indent = this.GenerateIndent(); var queryEntityType = value.Type as QueryEntityType; if (queryEntityType != null) { var idValue = this.LinkGenerator.GenerateEntityId(value).Replace(this.AstoriaServiceDescriptor.ServiceUri.AbsoluteUri, string.Empty); this.builder.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}Id:{1}", indent, idValue)); // do not expand below configured level of expands likely writing out querydataset if (this.currentDepth > this.maxDepth) { return(null); } foreach (var member in queryEntityType.Properties.Where(p => p.IsNavigationProperty())) { var memberValue = value.GetValue(member.Name); this.currentDepth++; var childIndent = this.GenerateIndent(); this.builder.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}{1}", childIndent, member.Name)); this.currentDepth++; memberValue.Accept(this); this.currentDepth -= 2; } } return(null); }
/// <summary> /// Gets the pre and post update representations of the entity updated by the given request /// </summary> /// <param name="request">The request</param> /// <param name="beforeUpdate">The entity before the update</param> /// <param name="afterUpdate">The entity after the update</param> public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate) { ExceptionUtilities.CheckArgumentNotNull(request, "request"); ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin"); ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests"); KeyValuePair <QueryStructuralValue, QueryStructuralValue> beforeAndAfter; if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter)) { var entityUri = request.Uri.ScopeToEntity(); var entity = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false); beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity); SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity)); afterUpdate = entity; beforeAndAfter = new KeyValuePair <QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate); this.updatedEntityCache[request] = beforeAndAfter; } else { beforeUpdate = beforeAndAfter.Key; afterUpdate = beforeAndAfter.Value; } }
public static void SetStreamValue(this QueryStructuralValue instance, string namedStream, AstoriaQueryStreamValue value) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(namedStream, "namedStream"); instance.AssertPropertyType <AstoriaQueryStreamType>(namedStream); instance.SetValue(namedStream, value); }
private static void InitMemberStreamTypes(QueryEntityType type, QueryStructuralValue structural) { // initialize named streams foreach (var namedStream in type.Properties.Streams()) { AstoriaQueryStreamValue qsv = new AstoriaQueryStreamValue((AstoriaQueryStreamType)namedStream.PropertyType, (byte[])null, null, type.EvaluationStrategy); structural.SetStreamValue(namedStream.Name, qsv); } }
private void FixupBothEndsOfNavigationProperty(QueryStructuralValue fromValue, QueryStructuralValue toValue, NavigationProperty navigationProperty) { this.FixupNavigationProperty(fromValue, toValue, navigationProperty.Name); var otherSideNavigation = navigationProperty.ToAssociationEnd.EntityType.AllNavigationProperties.Where(p => p.Association == navigationProperty.Association && p.FromAssociationEnd == navigationProperty.ToAssociationEnd && p.ToAssociationEnd == navigationProperty.FromAssociationEnd).SingleOrDefault(); if (otherSideNavigation != null && !toValue.IsNull) { this.FixupNavigationProperty(toValue, fromValue, otherSideNavigation.Name); } }
/// <summary> /// Fixes up the properties of the structural value based on the given selected paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="selectedPaths">The selected paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForSelect(QueryStructuralValue instance, IEnumerable <string> selectedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckCollectionNotEmpty(selectedPaths, "selectedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Select is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); bool wildCard = selectedPaths.Contains(Endpoints.SelectAll); var propertyNames = entityType.EntityType.AllProperties.Select(p => p.Name).Concat(entityType.Properties.Streams().Select(m => m.Name)); foreach (string propertyName in propertyNames) { if (!selectedPaths.Contains(Uri.EscapeDataString(propertyName)) && !wildCard) { // Primitive, bag and stream properties are either entirely present or entirely missing. // However, complex properties can be partially present if a mapped sub-property has a // null value. For this reason, we recursively hide the property and all sub-properties. HidePropertyRecursive(masked, propertyName); } } foreach (string propertyName in entityType.EntityType.AllNavigationProperties.Select(p => p.Name)) { string pathMarker = Uri.EscapeDataString(propertyName) + "/"; var subpaths = selectedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); if (selectedPaths.Contains(Uri.EscapeDataString(propertyName))) { continue; } else if (subpaths.Any()) { var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, subpaths)); masked.SetValue(propertyName, maskedValue); } else if (wildCard) { masked.SetValue(propertyName, value.Type.NullValue); } else { masked.HideMember(propertyName); } } return(masked); }
/// <summary> /// Fixes up the properties of the structural value based on the given expanded paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="expandedPaths">The expanded paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForExpand(QueryStructuralValue instance, IEnumerable <string> expandedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckArgumentNotNull(expandedPaths, "expandedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Expand is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); foreach (var navigation in entityType.EntityType.AllNavigationProperties) { string expandedPath; string propertyName = navigation.Name; var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); bool match = TryGetExpandedPath(expandedPaths, propertyName, entityType.EntityType, out expandedPath); string pathMarker = expandedPath + "/"; var subpaths = expandedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); // note that if there is match, we still need to fix up up any children, even if there are no subpaths if (subpaths.Any() || match) { // recurse var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, subpaths)); // handle page limit on expanded collections if (maskedValue.Type is QueryCollectionType) { var collection = maskedValue as QueryCollectionValue; ExceptionUtilities.CheckObjectNotNull(collection, "Value was a collection type, but not a collection value"); var relatedSet = entityType.EntitySet.GetRelatedEntitySet(navigation); var relatedSetPageSize = relatedSet.GetEffectivePageSize(); if (relatedSetPageSize.HasValue && this.applyPagingInExpands) { collection = this.AddOrderingForPagingToQueryCollectionValue(collection); maskedValue = collection.Take(relatedSetPageSize.Value); } } masked.SetValue(propertyName, maskedValue); } else if (!match) { // set any non-expanded navigation properties to null masked.SetValue(propertyName, value.Type.NullValue); } } return(masked); }
private void PopulateNonNavigationPropertiesOfVisibleEntity(QueryStructuralValue originalEntity, QueryStructuralValue visibleEntity) { foreach (var property in originalEntity.Type.Properties) { bool isCollectionOfNonEntities = property.PropertyType is QueryCollectionType && !(((QueryCollectionType)property.PropertyType).ElementType is QueryEntityType); if (property.PropertyType is QueryScalarType || property.PropertyType is QueryComplexType || isCollectionOfNonEntities) { visibleEntity.SetValue(property.Name, originalEntity.GetValue(property.Name)); } } }
private static void MarkPropertyAsDynamic(QueryStructuralValue value, string memberName) { var propertyValue = value.GetValue(memberName); if (propertyValue.Type is QueryComplexType) { MarkDynamicSubPropertyValues((QueryStructuralValue)propertyValue); } propertyValue.AsDynamicPropertyValue(); }
/// <summary> /// Compares anonymous object with the expected value. /// </summary> /// <param name="expected">Expected value.</param> /// <param name="actual">Actual value.</param> /// <param name="path">The path to the compared object (for debugging purposes).</param> /// <param name="shouldThrow">Should exception be thrown if error is encountered.</param> /// <returns>Result of the comparison, Success, Failure or Skipped.</returns> protected ComparisonResult CompareAnonymous(QueryStructuralValue expected, object actual, string path, bool shouldThrow) { if (actual != null && !actual.GetType().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) { this.ThrowOrLogError(shouldThrow, "Expecting anonymous type in '{0}'. Got: {1}.", path, actual.GetType()); return(ComparisonResult.Failure); } return(this.CompareStructural(expected, actual, path, shouldThrow)); }
private void SetCollectionProperty(QueryStructuralValue instance, QueryProperty memberProperty, List <QueryValue> collectionElements) { QueryCollectionValue queryCollectionValue = instance.GetCollectionValue(memberProperty.Name); queryCollectionValue.Elements.Clear(); foreach (QueryValue queryValue in collectionElements) { queryCollectionValue.Elements.Add(queryValue); } instance.SetValue(memberProperty.Name, queryCollectionValue); }
private static void MarkDynamicSubPropertyValues(QueryStructuralValue value) { ExceptionUtilities.CheckArgumentNotNull(value, "value"); var complexType = value.Type as QueryComplexType; ExceptionUtilities.CheckObjectNotNull(complexType, "Instance was not a complex type"); foreach (var memberName in value.MemberNames) { MarkPropertyAsDynamic(value, memberName); } }
/// <summary> /// Static method for creating masked structural values. Will not hide any properties initially. /// </summary> /// <param name="toMask">The structural value to mask</param> /// <returns>A masked structural value</returns> internal static MaskedQueryStructuralValue Create(QueryStructuralValue toMask) { ExceptionUtilities.CheckArgumentNotNull(toMask, "toMask"); var masked = new MaskedQueryStructuralValue(toMask.Type, toMask.IsNull, toMask.EvaluationError, toMask.Type.EvaluationStrategy); foreach (var memberName in toMask.MemberNames) { masked.SetValue(memberName, toMask.GetValue(memberName)); } masked.Annotations.AddRange(toMask.Annotations.Select(a => a.Clone())); return masked; }
private static ODataUri GetTopLevelUri(QueryStructuralValue entity) { ExceptionUtilities.CheckArgumentNotNull(entity, "entity"); QueryEntityType entityType = entity.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural value was not an entity type"); var setSegment = ODataUriBuilder.EntitySet(entityType.EntitySet); var keyValues = entityType.EntityType.AllKeyProperties.Select(k => new NamedValue(k.Name, entity.GetScalarValue(k.Name).Value)); var keySegment = ODataUriBuilder.Key(entityType.EntityType, keyValues); return(new ODataUri(setSegment, keySegment)); }
/// <summary> /// Fixes up references between entities in the visible entities graph. /// </summary> protected void FixupReferencesOfVisibleEntities() { foreach (var entry in this.VisibleEntitiesGraph) { QueryStructuralValue originalEntity = entry.Key; QueryStructuralValue visibleEntity = entry.Value; if (!visibleEntity.IsNull) { this.FixupReferencesOfVisibleEntity(originalEntity, visibleEntity); } } }
private void UpdateVisibleEntitiesGraphForAnonymousAndGrouping(QueryStructuralValue anonymousOrGroupingValue, string[] spanPaths) { // we never take Includes into account for anonymous and grouping properties, hence we can terminate/corrupt the current span path string newPath = "[anonymous or grouping]"; if (!anonymousOrGroupingValue.IsNull) { foreach (QueryProperty property in anonymousOrGroupingValue.Type.Properties) { var originalValue = anonymousOrGroupingValue.GetValue(property.Name); this.UpdateVisibleEntitiesGraphForQueryValue(originalValue, newPath, spanPaths); } } }
private QueryStructuralValue PopulateProperties(QueryStructuralValue structuralToPopulate, object resultFragment) { foreach (var property in structuralToPopulate.Type.Properties) { var propertyInfo = resultFragment.GetType().GetProperty(property.Name); ExceptionUtilities.CheckObjectNotNull(propertyInfo, "Could not find appropriate property info for the given property: " + property.Name); var propertyValue = propertyInfo.GetValue(resultFragment, null); var propertyQueryValue = this.Convert(propertyValue, property.PropertyType); structuralToPopulate.SetValue(property.Name, propertyQueryValue); } return(structuralToPopulate); }
/// <summary> /// Static method for creating masked structural values. Will not hide any properties initially. /// </summary> /// <param name="toMask">The structural value to mask</param> /// <returns>A masked structural value</returns> internal static MaskedQueryStructuralValue Create(QueryStructuralValue toMask) { ExceptionUtilities.CheckArgumentNotNull(toMask, "toMask"); var masked = new MaskedQueryStructuralValue(toMask.Type, toMask.IsNull, toMask.EvaluationError, toMask.Type.EvaluationStrategy); foreach (var memberName in toMask.MemberNames) { masked.SetValue(memberName, toMask.GetValue(memberName)); } masked.Annotations.AddRange(toMask.Annotations.Select(a => a.Clone())); return(masked); }
private void CompareComplexBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues) { int i = 0; var collection = instance.GetCollectionValue(memberProperty.Name); if (!this.WriteErrorIfNull(propertyPath, collection)) { List<NamedValue> complexInstanceNamedValues = namedValues.Where(pp => pp.Name.StartsWith(propertyPath + "." + i + ".", StringComparison.Ordinal)).ToList(); while (complexInstanceNamedValues.Any()) { if (i < collection.Elements.Count) { var complexValue = collection.Elements[i] as QueryStructuralValue; this.CompareValues(complexValue, complexInstanceNamedValues, propertyPath + "." + i); } i++; complexInstanceNamedValues = namedValues.Where(pp => pp.Name.StartsWith(propertyPath + "." + i + ".", StringComparison.Ordinal)).ToList(); } this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath); } }
private void CompareBagProperty(QueryStructuralValue instance, QueryProperty memberProperty, QueryType elementType, string propertyPath, IEnumerable<NamedValue> namedValues) { var scalarElementDataType = elementType as QueryScalarType; var complexTypeElementDataType = elementType as QueryComplexType; if (scalarElementDataType != null) { this.ComparePrimitiveBag(instance, memberProperty, propertyPath, namedValues); } else { ExceptionUtilities.CheckObjectNotNull(complexTypeElementDataType, "PropertyPath '{0}' is an invalid type '{1}'", propertyPath, memberProperty.PropertyType); this.CompareComplexBag(instance, memberProperty, propertyPath, namedValues); } }
private void CompareValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { // The following code block handles the case where the value is server generated. // For instance, server generated Keys would fail so if a property is marked as being server generated // we make sure to remove that property from the list of properties to verify MemberProperty memberProperty; var instanceEntityType = instance.Type as QueryEntityType; if (instanceEntityType != null) { memberProperty = instanceEntityType.EntityType.AllProperties.SingleOrDefault(p => p.Name == property.Name); } else { memberProperty = ((QueryComplexType)instance.Type).ComplexType.Properties.SingleOrDefault(p => p.Name == property.Name); } if (memberProperty != null && memberProperty.Annotations.OfType<StoreGeneratedPatternAnnotation>().Any()) { // TODO: be more fine-grained about whether this is an update or insert (ie, look at the annotation's property values) this.unusedNamedValuePaths.Remove(childPropertyPath); continue; } NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { var queryValue = instance.GetScalarValue(property.Name); this.WriteErrorIfNotEqual(childPropertyPath, primitivePropertyNamedValue.Value, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.CompareBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.CompareBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (!this.WriteErrorIfNull(childPropertyPath, complexTypeValue)) { this.CompareValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); } } else { // Check for null case List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); QueryValue queryValue = instance.GetValue(property.Name); this.WriteErrorIfNotNull(childPropertyPath, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }
private QueryStructuralValue CloneStructuralValue(Dictionary<QueryStructuralValue, QueryStructuralValue> identityMap, QueryStructuralValue qsv) { if (qsv.IsNull) { return qsv; } QueryStructuralValue clonedValue; if (identityMap.TryGetValue(qsv, out clonedValue)) { return clonedValue; } clonedValue = qsv.Type.CreateNewInstance(); identityMap.Add(qsv, clonedValue); foreach (var m in qsv.Type.Properties) { // copy scalar properties if (m.PropertyType is QueryScalarType) { clonedValue.SetValue(m.Name, qsv.GetScalarValue(m.Name)); continue; } // copy stream properties if (m.PropertyType is AstoriaQueryStreamType) { if (m.Name.Contains("DefaultStream")) { clonedValue.SetDefaultStreamValue(qsv.GetDefaultStreamValue()); } else { clonedValue.SetStreamValue(m.Name, qsv.GetStreamValue(m.Name)); } continue; } var qst = m.PropertyType as QueryStructuralType; if (m.PropertyType is QueryStructuralType) { if (!qst.IsValueType) { // skip reference types, clone everything else continue; } clonedValue.SetValue(m.Name, this.CloneStructuralValue(identityMap, qsv.GetStructuralValue(m.Name))); } var qct = m.PropertyType as QueryCollectionType; if (qct != null) { var elementStructuralType = qct.ElementType as QueryStructuralType; if (elementStructuralType != null) { if (!elementStructuralType.IsValueType) { // skip collections of reference types, clone everything else continue; } } clonedValue.SetValue(m.Name, this.CloneCollectionValue(identityMap, qsv.GetCollectionValue(m.Name))); } } return clonedValue; }
/// <summary> /// Evaluates the given lambda as if it were called on the given entity. Used for evaluating orderby expressions for the last result in a feed. /// </summary> /// <param name="entity">The entity to evaluate the lambda for.</param> /// <param name="lamda">The lamda to evaluate.</param> /// <returns>The result of evaluating the lamda for the given entity.</returns> private object EvaluateLambdaForEntity(QueryStructuralValue entity, LinqLambdaExpression lamda) { var expression = CommonQueryBuilder.Root("fakeSet", entity.Type.CreateCollectionType()).Select(lamda).Single(); var fakeQueryDataSet = new QueryDataSet(); fakeQueryDataSet.RootQueryData["fakeSet"] = entity.Type.CreateCollectionType().CreateCollectionWithValues(new[] { entity }); QueryValue evaluated; using (this.ExpressionEvaluator.WithTemporaryDataSet(fakeQueryDataSet)) { evaluated = this.ExpressionEvaluator.Evaluate(expression); } return ((QueryScalarValue)evaluated).Value; }
private string CalculateExpectedETagForEntityOrStream(ODataUri uri, QueryStructuralValue entity) { if (uri.IsMediaResource()) { return entity.GetDefaultStreamValue().GetExpectedETag(); } if (uri.IsNamedStream()) { var streamSegment = uri.Segments.OfType<NamedStreamSegment>().Last(); return entity.GetStreamValue(streamSegment.Name).GetExpectedETag(); } return this.LiteralConverter.ConstructWeakETag(entity); }
private static ODataUri GetTopLevelUri(QueryStructuralValue entity) { ExceptionUtilities.CheckArgumentNotNull(entity, "entity"); QueryEntityType entityType = entity.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural value was not an entity type"); var setSegment = ODataUriBuilder.EntitySet(entityType.EntitySet); var keyValues = entityType.EntityType.AllKeyProperties.Select(k => new NamedValue(k.Name, entity.GetScalarValue(k.Name).Value)); var keySegment = ODataUriBuilder.Key(entityType.EntityType, keyValues); return new ODataUri(setSegment, keySegment); }
private QueryValue SynchronizeAndEvaluate(ODataUri entityUri, QueryStructuralValue beforeSync, bool synchronizeEntireSet) { var entityType = beforeSync.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Structural value was not an entity type"); // if an entity was deleted, synchronize the entire set. Otherwise just synchronize the entity if (synchronizeEntireSet) { SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntireEntitySet(c, entityType.EntitySet.Name)); } else { SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, beforeSync)); } return this.Evaluator.Evaluate(entityUri, false, false); }
/// <summary> /// Gets the pre and post update representations of the entity updated by the given request /// </summary> /// <param name="request">The request</param> /// <param name="beforeUpdate">The entity before the update</param> /// <param name="afterUpdate">The entity after the update</param> public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate) { ExceptionUtilities.CheckArgumentNotNull(request, "request"); ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin"); ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests"); KeyValuePair<QueryStructuralValue, QueryStructuralValue> beforeAndAfter; if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter)) { var entityUri = request.Uri.ScopeToEntity(); var entity = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false); beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity); SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity)); afterUpdate = entity; beforeAndAfter = new KeyValuePair<QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate); this.updatedEntityCache[request] = beforeAndAfter; } else { beforeUpdate = beforeAndAfter.Key; afterUpdate = beforeAndAfter.Value; } }
/// <summary> /// Fixes up the properties of the structural value based on the given selected paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="selectedPaths">The selected paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForSelect(QueryStructuralValue instance, IEnumerable<string> selectedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckCollectionNotEmpty(selectedPaths, "selectedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Select is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); bool wildCard = selectedPaths.Contains(Endpoints.SelectAll); var propertyNames = entityType.EntityType.AllProperties.Select(p => p.Name).Concat(entityType.Properties.Streams().Select(m => m.Name)); foreach (string propertyName in propertyNames) { if (!selectedPaths.Contains(Uri.EscapeDataString(propertyName)) && !wildCard) { // Primitive, bag and stream properties are either entirely present or entirely missing. // However, complex properties can be partially present if a mapped sub-property has a // null value. For this reason, we recursively hide the property and all sub-properties. HidePropertyRecursive(masked, propertyName); } } foreach (string propertyName in entityType.EntityType.AllNavigationProperties.Select(p => p.Name)) { string pathMarker = Uri.EscapeDataString(propertyName) + "/"; var subpaths = selectedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); if (selectedPaths.Contains(Uri.EscapeDataString(propertyName))) { continue; } else if (subpaths.Any()) { var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForSelect(s, subpaths)); masked.SetValue(propertyName, maskedValue); } else if (wildCard) { masked.SetValue(propertyName, value.Type.NullValue); } else { masked.HideMember(propertyName); } } return masked; }
/// <summary> /// Fixes up the properties of the structural value based on the given expanded paths /// </summary> /// <param name="instance">The value to fix up</param> /// <param name="expandedPaths">The expanded paths for the value's scope</param> /// <returns>The fixed-up value</returns> private QueryStructuralValue FixupPropertiesForExpand(QueryStructuralValue instance, IEnumerable<string> expandedPaths) { ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); ExceptionUtilities.CheckArgumentNotNull(expandedPaths, "expandedPaths"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Expand is not supported on non-entity types"); var masked = MaskedQueryStructuralValue.Create(instance); foreach (var navigation in entityType.EntityType.AllNavigationProperties) { string expandedPath; string propertyName = navigation.Name; var value = instance.GetValue(propertyName); ExceptionUtilities.CheckObjectNotNull(value, "Value for property '{0}' was null", propertyName); bool match = TryGetExpandedPath(expandedPaths, propertyName, entityType.EntityType, out expandedPath); string pathMarker = expandedPath + "/"; var subpaths = expandedPaths.Where(p => p.StartsWith(pathMarker, StringComparison.Ordinal)).Select(p => p.Substring(pathMarker.Length)); // note that if there is match, we still need to fix up up any children, even if there are no subpaths if (subpaths.Any() || match) { // recurse var maskedValue = this.VisitEntityValues(value, s => this.FixupPropertiesForExpand(s, subpaths)); // handle page limit on expanded collections if (maskedValue.Type is QueryCollectionType) { var collection = maskedValue as QueryCollectionValue; ExceptionUtilities.CheckObjectNotNull(collection, "Value was a collection type, but not a collection value"); var relatedSet = entityType.EntitySet.GetRelatedEntitySet(navigation); var relatedSetPageSize = relatedSet.GetEffectivePageSize(); if (relatedSetPageSize.HasValue && this.applyPagingInExpands) { collection = this.AddOrderingForPagingToQueryCollectionValue(collection); maskedValue = collection.Take(relatedSetPageSize.Value); } } masked.SetValue(propertyName, maskedValue); } else if (!match) { // set any non-expanded navigation properties to null masked.SetValue(propertyName, value.Type.NullValue); } } return masked; }
private void ComparePrimitiveBag(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues) { int i = 0; var collection = instance.GetCollectionValue(memberProperty.Name); if (!this.WriteErrorIfNull(propertyPath, collection)) { List<NamedValue> primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList(); while (primitiveItemNamedValues.Any()) { if (i < collection.Elements.Count) { var scalarQueryValue = collection.Elements[i] as QueryScalarValue; ExceptionUtilities.Assert(primitiveItemNamedValues.Count() < 2, "Should not get more than one value for a primitive Bag item for path '{0}'", propertyPath + "." + i); var value = primitiveItemNamedValues.Single(); this.WriteErrorIfNotEqual(propertyPath, value.Value, scalarQueryValue); this.unusedNamedValuePaths.Remove(value.Name); } i++; primitiveItemNamedValues = namedValues.Where(pp => pp.Name == propertyPath + "." + i).ToList(); } this.WriteErrorIfNotEqual(propertyPath, collection.Elements.Count, i, "The number of expected items '{0}' does not match the actual '{1}' for propertyPath {2}", collection.Elements.Count, i, propertyPath); } }
private void VerifyTypeNameForInsert(ODataRequest request, ODataResponse response, QueryStructuralValue inserted) { var entityInstance = request.Body.RootElement as EntityInstance; ExceptionUtilities.CheckObjectNotNull(entityInstance, "Cannot get expected type name because request payload was not an entity instance. It was: '{0}'", request.Body.RootElement.ElementType); string expectedTypeName = request.Uri.Segments.OfType<EntityTypeSegment>().Select(t => t.EntityType.FullName).LastOrDefault(); if (entityInstance.FullTypeName != null) { expectedTypeName = entityInstance.FullTypeName; } var entityType = inserted.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Cannot verify type because inserted value is not an entity type. Type was: '{0}'.", inserted.Type); this.AssertAreEqual(expectedTypeName, entityType.EntityType.FullName, "Inserted instance type did not match", request, response); }
private void CompareBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues) { List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList(); if (!exactMatches.Any()) { return; } ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath); QueryCollectionValue actualQueryBagValue = instance.GetCollectionValue(memberProperty.Name); NamedValue expectedBagValue = exactMatches.Single(); if (expectedBagValue.Value == null) { this.WriteErrorIfNotNull(propertyPath, actualQueryBagValue); this.unusedNamedValuePaths.Remove(propertyPath); } else if (expectedBagValue.Value == EmptyData.Value) { if (!this.WriteErrorIfNull(propertyPath, actualQueryBagValue)) { this.WriteErrorIfNotEqual(propertyPath, actualQueryBagValue.Elements.Count, 0, "Expected zero elements in BagProperty"); this.unusedNamedValuePaths.Remove(propertyPath); } } }
private void VerifyResponsePayload(ODataRequest request, ODataResponse response, QueryStructuralValue expected) { if (response.StatusCode != HttpStatusCode.NoContent) { QueryValue expectedValue = expected; if (!request.Uri.IsEntity()) { var entityUri = request.Uri.ScopeToEntity(); foreach (var property in request.Uri.Segments.Skip(entityUri.Segments.Count).OfType<PropertySegment>()) { expectedValue = ((QueryStructuralValue)expectedValue).GetValue(property.Property.Name); } } this.VerificationServices.ValidateResponsePayload(request.Uri, response, expectedValue, this.maxProtocolVersion); } }
/// <summary> /// Factory method to create the <see cref="LinqToAstoriaKeyExpression"/>. /// </summary> /// <param name="source">The source query.</param> /// <param name="instance">The instance to get key values from.</param> /// <returns>The <see cref="QueryExpression"/> with a key matching the values of the given instance.</returns> public static LinqToAstoriaKeyExpression Key(this QueryExpression source, QueryStructuralValue instance) { ExceptionUtilities.CheckArgumentNotNull(source, "source"); ExceptionUtilities.CheckArgumentNotNull(instance, "instance"); var entityType = instance.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Given structural instance was not an entity. Type was: {0}", instance.Type); var key = new List<KeyValuePair<QueryProperty, QueryConstantExpression>>(); foreach (var keyProperty in entityType.EntityType.AllKeyProperties) { var queryProperty = entityType.Properties.SingleOrDefault(p => p.Name == keyProperty.Name); ExceptionUtilities.CheckObjectNotNull(queryProperty, "Could not find property with name '{0}' on type '{1}'", keyProperty.Name, entityType); var value = instance.GetScalarValue(keyProperty.Name); key.Add(new KeyValuePair<QueryProperty, QueryConstantExpression>(queryProperty, CommonQueryBuilder.Constant(value))); } return new LinqToAstoriaKeyExpression(source, key, entityType.CreateCollectionType()); }
private void GetUpdatedEntityBeforeAndAfter(ODataRequest request, ODataResponse response, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate) { using (this.Context.Begin(request)) { if (request.GetEffectiveVerb() == HttpVerb.Post) { afterUpdate = this.Context.GetInsertedEntity(request, response); ExceptionUtilities.CheckObjectNotNull(afterUpdate, "Structural value returned by GetInsertedEntity unexpectedly null"); beforeUpdate = afterUpdate.Type.NullValue; } else { this.Context.GetUpdatedEntity(request, out beforeUpdate, out afterUpdate); ExceptionUtilities.CheckObjectNotNull(beforeUpdate, "Before-update structural value returned by GetUpdatedEntity unexpectedly null"); ExceptionUtilities.CheckObjectNotNull(afterUpdate, "After-update structural value returned by GetUpdatedEntity unexpectedly null"); } } }
/// <summary> /// Generates the next link for an expanded feed. /// </summary> /// <param name="containingEntity">The containing entity.</param> /// <param name="navigation">The expanded navigation property.</param> /// <param name="lastEntityValue">The last entity value.</param> /// <returns> /// The expected next link /// </returns> public string GenerateExpandedNextLink(EntityInstance containingEntity, NavigationPropertyInstance navigation, QueryStructuralValue lastEntityValue) { ExceptionUtilities.CheckArgumentNotNull(containingEntity, "containingEntity"); ExceptionUtilities.CheckArgumentNotNull(navigation, "navigation"); ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue"); var navigationUriString = ((ExpandedLink)navigation.Value).UriString; if (string.IsNullOrEmpty(navigationUriString)) { navigationUriString = UriHelpers.ConcatenateUriSegments(containingEntity.EditLink, navigation.Name); } var skipTokenValues = new List<object>(); foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey)) { skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value); } var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues); var nextLinkUri = new ODataUri(new UnrecognizedSegment(navigationUriString)); nextLinkUri.SkipToken = skiptoken; return this.UriToStringConverter.ConvertToString(nextLinkUri); }
/// <summary> /// Verify ETag and Id values of given payload element. /// </summary> /// <param name="entityType">type of the element</param> /// <param name="payloadElement">payload element to verify</param> /// <param name="value">expected values</param> private void VerifyEntityMetadata(QueryEntityType entityType, EntityInstance payloadElement, QueryStructuralValue value) { if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeETags)) { if (entityType.EntityType.HasETag()) { // TODO: are ETags always based on property values? var expectedETag = this.parent.LiteralConverter.ConstructWeakETag(value); this.parent.Assert.AreEqual(expectedETag, payloadElement.ETag, "Entity's ETag did not match"); } else { this.parent.Assert.IsNull(payloadElement.ETag, "Entity should not have had an ETag"); } } if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeEntityIdentifier)) { this.parent.Assert.IsNotNull(payloadElement.Id, "Entity's ID unexpectedly null"); if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.UseConventionBasedIdentifiers)) { var expectedId = this.parent.LinkGenerator.GenerateEntityId(value); this.parent.Assert.AreEqual(expectedId, payloadElement.Id, "Entity's ID did not match"); } } else { this.parent.Assert.IsNull(payloadElement.Id, "Entity's ID unexpectedly non-null"); } if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.UseConventionBasedLinks)) { var expectedEditLink = this.parent.LinkGenerator.GenerateEntityEditLink(value); this.CompareUri(expectedEditLink, payloadElement.EditLink, "Entity's edit-link did not match"); } if (entityType.EntityType.GetBaseTypesAndSelf().Any(t => t.HasStream())) { using (this.parent.Assert.WithMessage("Entity's stream metadata did not match")) { var defaultStreamValue = value.GetDefaultStreamValue(); this.parent.Assert.AreEqual(defaultStreamValue.ContentType, payloadElement.StreamContentType, "Content type did not match"); this.CompareStreamETag(defaultStreamValue, payloadElement.StreamETag); if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeMediaResourceEditLinks)) { this.CompareUri(defaultStreamValue.EditLink, payloadElement.StreamEditLink, "Edit link did not match"); } else { this.parent.Assert.IsNull(payloadElement.StreamEditLink, "Edit link unexpectedly non-null"); } if (this.parent.ExpectedPayloadOptions.HasFlag(ODataPayloadOptions.IncludeMediaResourceSourceLinks)) { this.CompareUri(defaultStreamValue.SelfLink, payloadElement.StreamSourceLink, "Source link did not match"); } else { this.parent.Assert.IsNull(payloadElement.StreamSourceLink, "Source link unexpectedly non-null"); } } } }
/// <summary> /// Generates the expected next link for a top-level feed /// </summary> /// <param name="requestUri">The request URI.</param> /// <param name="pageSize">The page size.</param> /// <param name="lastEntityValue">The last entity value.</param> /// <returns> /// The expected next link /// </returns> public string GenerateNextLink(ODataUri requestUri, int pageSize, QueryStructuralValue lastEntityValue) { ExceptionUtilities.CheckArgumentNotNull(requestUri, "requestUri"); ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue"); var queryBasedUri = requestUri as QueryBasedODataUri; ExceptionUtilities.CheckObjectNotNull(queryBasedUri, "Only uris which were generated from queries are supported"); var skipTokenValues = new List<object>(); foreach (var keySelector in queryBasedUri.OrderByExpressions.SelectMany(o => o.KeySelectors)) { skipTokenValues.Add(this.EvaluateLambdaForEntity(lastEntityValue, keySelector)); } foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey)) { skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value); } var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues); // copy request uri segments exactly var nextLinkUri = new ODataUri(requestUri.Segments); // copy over $orderby, $filter, $expand, $select, and $inlinecount nextLinkUri.OrderBy = ModifyQueryOptionToMatchProduct(requestUri.OrderBy); nextLinkUri.Filter = ModifyQueryOptionToMatchProduct(requestUri.Filter); nextLinkUri.ExpandSegments = requestUri.ExpandSegments; nextLinkUri.SelectSegments = requestUri.SelectSegments; string inlineCountValue; if (requestUri.TryGetInlineCountValue(out inlineCountValue)) { nextLinkUri.InlineCount = inlineCountValue; } // add the $skiptoken generated above nextLinkUri.SkipToken = skiptoken; // generate a new $top value if (requestUri.Top.HasValue) { var top = requestUri.Top.Value - pageSize; if (top > 0) { nextLinkUri.Top = top; } } if (requestUri.IsServiceOperation()) { var functionSegment = requestUri.Segments.OfType<FunctionSegment>().Last(); foreach (var paramName in functionSegment.Function.Parameters.Select(p => p.Name)) { string paramValue; if (requestUri.CustomQueryOptions.TryGetValue(paramName, out paramValue)) { nextLinkUri.CustomQueryOptions[paramName] = paramValue; } } } return this.UriToStringConverter.ConvertToString(nextLinkUri); }
internal void UpdateValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { instance.SetPrimitiveValue(property.Name, primitivePropertyNamedValue.Value); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.UpdateBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.UpdateBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (complexTypeValue.IsNull) { complexTypeValue = complexDataType.CreateNewInstance(); } this.UpdateValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); instance.SetValue(property.Name, complexTypeValue); } else { // Check for null case List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); instance.SetValue(property.Name, complexDataType.NullValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }
private void VerifyPropertyValues(ODataRequest request, IQueryScalarValueToClrValueComparer primitiveComparer, QueryStructuralValue afterUpdate) { var namedValues = this.ExtractNamedValues(request); this.Comparer.Compare(afterUpdate, namedValues, primitiveComparer); }
private void QueryEntityInstanceAndGenerateStreams(QueryStructuralValue queryStructuralValue, IAsyncContinuation continuation) { var rootQuery = (QueryExpression)this.queryStructuralValueToRootQueryLookup[queryStructuralValue]; var query = ODataQueryTestCase.GetExistingEntityQuery(rootQuery, queryStructuralValue); var requestUriString = this.QueryExpressionToUriConverter.ComputeUri(query); requestUriString = UriHelpers.ConcatenateUriSegments(this.Workspace.ServiceUri.OriginalString, requestUriString); HttpWebRequest requestForExistingEntity = (HttpWebRequest)HttpWebRequest.Create(requestUriString); requestForExistingEntity.GetResponse<HttpWebResponse>( this.Asynchronous, continuation, (response) => { ProcessEntityAndGenerateStreams(queryStructuralValue, continuation, response); }); }
private void VerifyStoreData(ODataRequest request, ODataResponse response, QueryStructuralValue storeValue) { string contentType; ExceptionUtilities.Assert(request.Headers.TryGetValue(HttpHeaders.ContentType, out contentType), "Could not get Content-Type header from request"); if (request.Uri.IsNamedStream()) { string streamName = request.Uri.Segments.OfType<NamedStreamSegment>().Last().Name; var streamValue = storeValue.GetStreamValue(streamName); this.VerifyStream(streamValue, contentType, request, response); return; } bool isInsert = request.GetEffectiveVerb() == HttpVerb.Post; bool isMediaResource = request.Uri.IsMediaResource(); if (isInsert) { EntitySet expectedEntitySet; if (request.Uri.TryGetExpectedEntitySet(out expectedEntitySet)) { isMediaResource = expectedEntitySet.EntityType.HasStream(); } } if (isMediaResource) { var streamValue = storeValue.GetDefaultStreamValue(); this.VerifyStream(streamValue, contentType, request, response); } else { if (isInsert) { this.VerifyTypeNameForInsert(request, response, storeValue); } var formatStrategy = this.FormatSelector.GetStrategy(contentType, request.Uri); var primitiveComparer = formatStrategy.GetPrimitiveComparer(); // TODO: verify relationships // TODO: verify PUT vs PATCH semantics this.VerifyPropertyValues(request, primitiveComparer, storeValue); } }
private void ProcessEntityAndGenerateStreams(QueryStructuralValue queryStructuralValue, IAsyncContinuation continuation, HttpWebResponse response) { ExceptionUtilities.Assert(response.StatusCode == HttpStatusCode.OK, "Error generating stream data, response code incorrect:" + response.StatusCode.ToString()); var responseValue = new StreamReader(response.GetResponseStream()).ReadToEnd(); try { var existingEntityInXML = XElement.Parse(responseValue); var feedInstance = this.XmlToPayloadConverter.ConvertToPayloadElement(existingEntityInXML) as EntitySetInstance; ExceptionUtilities.CheckObjectNotNull(feedInstance, "Error generating stream data, cannot deserialize response:" + existingEntityInXML); var type = queryStructuralValue.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(type, "Type was not an entity type. Type was {0}", type.StringRepresentation); Func<AstoriaQueryStreamValue, bool> valueFilter = v => v.IsNull || v.Value.Length == 0; Func<QueryProperty, bool> propertyFilter = p => p.IsStream() && valueFilter(queryStructuralValue.GetStreamValue(p.Name)); var streamPropertiesToUpdate = type.Properties.Where(propertyFilter).ToList(); if (feedInstance.Count != 0) { var entityInstance = feedInstance.SingleOrDefault(); ExceptionUtilities.CheckObjectNotNull(entityInstance, "Payload did not contain a single entity instance"); var baseAddressAnnotation = feedInstance.Annotations.OfType<XmlBaseAnnotation>().SingleOrDefault(); foreach (var streamProperty in streamPropertiesToUpdate) { var streamPropertyType = streamProperty.PropertyType as AstoriaQueryStreamType; ExceptionUtilities.CheckObjectNotNull(streamPropertyType, "PropertyType is not an AstoriaQueryStreamType!", streamProperty.PropertyType); var streamData = this.GenerateStreamData(entityInstance, baseAddressAnnotation, streamProperty); this.streamsToUpdate.Add(streamData); } } continuation.Continue(); } catch (XmlException) { this.Logger.WriteLine(LogLevel.Error, "Error in Xml payload:" + responseValue); throw; } }
private void UpdateBagPropertyWithNullOrEmpty(QueryStructuralValue instance, QueryProperty memberProperty, string propertyPath, IEnumerable<NamedValue> namedValues) { List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == propertyPath).ToList(); if (!exactMatches.Any()) { return; } ExceptionUtilities.Assert(exactMatches.Count == 1, "Should only find at most one property path {0} when looking for null or empty value", propertyPath); NamedValue expectedBagValue = exactMatches.Single(); var bagType = memberProperty.PropertyType as QueryCollectionType; if (expectedBagValue.Value == null) { instance.SetValue(memberProperty.Name, bagType.NullValue); this.unusedNamedValuePaths.Remove(expectedBagValue.Name); } else if (expectedBagValue.Value == EmptyData.Value) { QueryCollectionValue value = bagType.CreateCollectionWithValues(new QueryValue[] { }); instance.SetValue(memberProperty.Name, value); this.unusedNamedValuePaths.Remove(expectedBagValue.Name); } }
private void VisitStructuralInstance(ComplexInstance payloadElement, QueryStructuralValue value) { ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement"); ExceptionUtilities.CheckArgumentNotNull(value, "value"); var entityType = value.Type as QueryEntityType; var complexType = value.Type as QueryComplexType; ExceptionUtilities.Assert(entityType != null || complexType != null, "Value was neither an entity type nor a complex type"); bool isEntity = entityType != null; string errorType = isEntity ? "Entity" : "Complex"; if (value.IsNull) { this.parent.Assert.IsTrue(payloadElement.IsNull, errorType + " instance unexpectedly non-null"); return; } else { this.parent.Assert.IsFalse(payloadElement.IsNull, errorType + " instance unexpectedly null"); this.VerifyTypeName(value, payloadElement.FullTypeName, errorType + " instance type name did not match expectation."); } // get all the payload properties, and remove them as we go to detect any extras var payloadProperties = payloadElement.Properties.ToList(); // this is data-driven to deal with open types, but we need to skip over the 'default stream' property if it exists foreach (var propertyName in value.MemberNames.Where(m => m != AstoriaQueryStreamType.DefaultStreamPropertyName)) { var propertyInstance = payloadProperties.SingleOrDefault(p => p.Name == propertyName); this.parent.Assert.IsNotNull(propertyInstance, string.Format(CultureInfo.InvariantCulture, "Could not find property '{0}' in payload", propertyName)); payloadProperties.Remove(propertyInstance); var propertyValue = value.GetValue(propertyName); this.RecurseWithMessage(propertyInstance, propertyValue, "{0} instance did not match expectation", errorType); } string extraPropertyNames = string.Join(", ", payloadProperties.Select(p => '\'' + p.Name + '\'').ToArray()); this.parent.Assert.IsTrue(payloadProperties.Count == 0, string.Format(CultureInfo.InvariantCulture, "{0} instance contained unexpected properties: {1}", errorType, extraPropertyNames)); }