Exemple #1
0
            /// <summary>
            /// Visits a payload element whose root is a DeferredLink.
            /// </summary>
            /// <param name="payloadElement">The root node of payload element being visited.</param>
            public void Visit(DeferredLink payloadElement)
            {
                ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
                bool needsWrapping = this.isRootElement;

                this.isRootElement = false;
                if (needsWrapping)
                {
                    this.writer.StartObjectScope();
                    this.writer.WriteName("uri");
                    this.writer.WriteString(payloadElement.UriString);
                }
                else
                {
                    this.writer.StartObjectScope();

                    // __deferred is used for payloads generated by the server and sent to the client
                    // __metadata is used if the client wants to update the navigation property
                    // We are always using __metadata because the test framework is only used to generate
                    // a payload on the client and sent them to the server.
                    this.writer.WriteName("__metadata");
                    this.writer.StartObjectScope();
                    this.writer.WriteName("uri");
                    this.writer.WriteString(payloadElement.UriString);
                    this.writer.EndScope();
                    this.writer.EndScope();
                }

                if (needsWrapping)
                {
                    this.writer.EndScope();
                }
            }
        /// <summary>
        /// Visits the payload element
        /// </summary>
        /// <param name="payloadElement">The payload element to visit</param>
        public override void Visit(DeferredLink payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
            ExceptionUtilities.CheckObjectNotNull(this.currentXElement, "Current XElement is not defined");

            XElement linkElement;

            var navPropertyInstance = this.payloadStack.OfType <NavigationPropertyInstance>().FirstOrDefault();

            if (navPropertyInstance == null)
            {
                linkElement = CreateMetadataElement(this.currentXElement, "ref");
                CreateAtomAttribute(linkElement, "id", payloadElement.UriString ?? string.Empty);
            }
            else
            {
                string relAttributeValue;
                if (navPropertyInstance.AssociationLink == payloadElement)
                {
                    relAttributeValue = DataServicesRelatedLinksNamespace + navPropertyInstance.Name;
                }
                else
                {
                    relAttributeValue = DataServicesRelatedNamespace + navPropertyInstance.Name;
                }

                string contentType = payloadElement.Annotations.OfType <ContentTypeAnnotation>().Select(a => a.Value).SingleOrDefault();
                string title       = payloadElement.Annotations.OfType <TitleAnnotation>().Select(a => a.Value).SingleOrDefault();

                linkElement = CreateAtomLinkElement(this.currentXElement, relAttributeValue, payloadElement.UriString, contentType, null, title);
            }

            PostProcessXElement(payloadElement, linkElement);
        }
        private void AddFoldedLinksToEntityInsertPayload(DataServiceContextData contextData, EntityDescriptorData entityDescriptorData, EntityInstance payload)
        {
            var entityType = this.ModelSchema.EntityTypes.Single(t => t.FullName == entityDescriptorData.EntityClrType.FullName);

            foreach (var linkDescriptor in contextData.LinkDescriptorsData.Where(l => l.SourceDescriptor == entityDescriptorData))
            {
                if (linkDescriptor.TargetDescriptor.State == EntityStates.Added)
                {
                    continue;
                }

                var navigationProperty = entityType.AllNavigationProperties.Single(n => n.Name == linkDescriptor.SourcePropertyName);

                string contentType = MimeTypes.ApplicationAtomXml + ";type=";
                if (navigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
                {
                    contentType += "feed";
                }
                else
                {
                    contentType += "entry";
                }

                // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity
                // and the identity may not be an actual uri
                var link = new DeferredLink()
                {
                    UriString = linkDescriptor.TargetDescriptor.EditLink.OriginalString
                }
                .WithContentType(contentType).WithTitleAttribute(linkDescriptor.SourcePropertyName);

                payload.Add(new NavigationPropertyInstance(linkDescriptor.SourcePropertyName, link));
            }
        }
        /// <summary>
        /// Creates a set of interesting entity reference link instances.
        /// </summary>
        /// <param name="settings">The test descriptor settings to use.</param>
        /// <returns>List of test descriptors with interesting entity reference link instances as payload.</returns>
        internal static IEnumerable <LinkCollection> CreateEntityReferenceLinksValues()
        {
            DeferredLink link1 = PayloadBuilder.DeferredLink("http://odata.org/deferred1");
            DeferredLink link2 = PayloadBuilder.DeferredLink("http://odata.org/deferred2");
            DeferredLink link3 = PayloadBuilder.DeferredLink("http://odata.org/deferred3");

            yield return(PayloadBuilder.LinkCollection());

            yield return(PayloadBuilder.LinkCollection().Item(link1));

            yield return(PayloadBuilder.LinkCollection().Item(link1).Item(link2).Item(link3));

            yield return(PayloadBuilder.LinkCollection().Item(link1).InlineCount(1));

            yield return(PayloadBuilder.LinkCollection().Item(link1).InlineCount(-1));

            yield return(PayloadBuilder.LinkCollection().Item(link1).NextLink("http://odata.org/nextlink"));

            yield return(PayloadBuilder.LinkCollection().Item(link1).InlineCount(1).NextLink("http://odata.org/nextlink"));

            yield return(PayloadBuilder.LinkCollection().Item(link1).Item(link2).Item(link3).InlineCount(1));

            yield return(PayloadBuilder.LinkCollection().Item(link1).Item(link2).Item(link3).InlineCount(-1));

            yield return(PayloadBuilder.LinkCollection().Item(link1).Item(link2).Item(link3).NextLink("http://odata.org/nextlink"));

            yield return(PayloadBuilder.LinkCollection().Item(link1).Item(link2).Item(link3).InlineCount(1).NextLink("http://odata.org/nextlink"));
        }
Exemple #5
0
        /// <summary>
        /// Visits the children of the given payload element and replaces it with a copy if any child changes
        /// </summary>
        /// <param name="payloadElement">The payload element to potentially replace</param>
        /// <returns>The original element or a copy to replace it with</returns>
        public virtual ODataPayloadElement Visit(NavigationPropertyInstance payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");

            ODataPayloadElement replacedValue = null;

            if (payloadElement.Value != null)
            {
                replacedValue = this.Recurse(payloadElement.Value);
            }

            DeferredLink replacedAssociationLink = null;

            if (payloadElement.AssociationLink != null)
            {
                replacedAssociationLink = (DeferredLink)this.Recurse(payloadElement.AssociationLink);
            }

            if (!this.ShouldReplace(payloadElement.Value, replacedValue) && !this.ShouldReplace(payloadElement.AssociationLink, replacedAssociationLink))
            {
                return(payloadElement);
            }

            return(payloadElement.ReplaceWith(new NavigationPropertyInstance(payloadElement.Name, replacedValue, replacedAssociationLink)));
        }
Exemple #6
0
        /// <summary>
        /// Verify the relationship link is as expected.
        /// </summary>
        /// <param name="navigation">navigation property instance for the navigation link</param>
        /// <param name="associationLink">relationship link to verify</param>
        /// <param name="expectedAssociationUri">expected link value</param>
        /// <param name="request">The request needed for error report.</param>
        /// <param name="response">The response to check the content type.</param>
        private void VerifyAssociationLink(NavigationPropertyInstance navigation, DeferredLink associationLink, string expectedAssociationUri, ODataRequest request, ODataResponse response)
        {
            bool linkShouldBeNull = false;

            if (!this.IsAtomResponse(response) && navigation.IsExpanded)
            {
                var expandedValue = ((ExpandedLink)navigation.Value).ExpandedElement;
                linkShouldBeNull = expandedValue == null;
            }

            if (linkShouldBeNull)
            {
                // json navigations are null, then there are no links
                if (associationLink != null)
                {
                    this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected association link == null, observed '{0}'", associationLink.UriString);
                    this.ReportFailure(request, response);
                    throw new ResponseVerificationException();
                }
            }
            else
            {
                this.VerifyLinkUriValue(associationLink.UriString, expectedAssociationUri, request, response);

                // extra verifications for atom payload
                if (this.IsAtomResponse(response))
                {
                    this.VerifyAtomAssociationLinkTypeAttribute(associationLink, request, response);
                    this.VerifyAtomTitleAttribute(navigation, associationLink, request, response);
                }
            }
        }
        public void EntityReferenceLinkReadingNullUriTest()
        {
            EdmModel model = (EdmModel)Microsoft.Test.OData.Utils.Metadata.TestModels.BuildTestModel();

            model.Fixup();

            DeferredLink link = PayloadBuilder.DeferredLink(/*uri*/ null);

            this.CombinatorialEngineProvider.RunCombinations(
                baseUriValues,
                this.ReaderTestConfigurationProvider.AtomFormatConfigurations.Where(tc => !tc.IsRequest),
                (baseUriValue, testConfiguration) =>
            {
                // NOTE: In JSON, the value of the 'uri' property will be 'null' which is not allowed (and we expect an error)
                //       In ATOM, the HREF of an entity reference link is stored as element content and will thus be read as
                //       string.Empty even if the source link was null
                NullUriValueTestCase <DeferredLink> testCase = new NullUriValueTestCase <DeferredLink>
                {
                    SetNullUriAction     = (instance, uri, testConfig) => instance.UriString = null,
                    SetExpectedUriAction = (instance, uri, testConfig) => instance.UriString = UriToString(uri),
                };

                this.RunNullUriReadingTest(link, testCase, model, baseUriValue, testConfiguration);
            });
        }
        /// <summary>
        /// Visits a DeferredLink, if IncludeRelationshipLinks = true then this will be a V3 payload
        /// </summary>
        /// <param name="payloadElement">payload Element</param>
        public override void Visit(DeferredLink payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");

            this.IncreaseVersionIfIncludeRelationshipLinksIsTrue(payloadElement);

            base.Visit(payloadElement);
        }
Exemple #9
0
        private static void AddLinkMetadata(DeferredLink payloadElement, ODataNavigationLink link)
        {
            AtomLinkMetadata metadata = CreateLinkMetadata(payloadElement.Annotations.OfType <XmlTreeAnnotation>());

            if (metadata != null)
            {
                link.SetAnnotation <AtomLinkMetadata>(metadata);
            }
        }
Exemple #10
0
        /// <summary>
        /// Visits the payload element and annotates it with metadata
        /// </summary>
        /// <param name="payloadElement">The payload element to visit</param>
        public override void Visit(DeferredLink payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
            var navigation = this.MetadataStack.OfType <NavigationProperty>().FirstOrDefault();

            ExceptionUtilities.CheckObjectNotNull(navigation, "No navigation property found on metadata stack");
            payloadElement.AddAnnotationIfNotExist(new NavigationPropertyAnnotation()
            {
                Property = navigation
            });
        }
Exemple #11
0
        /// <summary>
        /// Visits a payload element whose root is a DeferredLink.
        /// </summary>
        /// <param name="payloadElement">The root node of the payload element being visited.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            base.Visit(payloadElement);

            if (!this.requestPayload || this.depth > 2)
            {
                // Any deferred link inside a navigation link needs to be annotated (request/response payload differs)
                // Top level response payloads need to be annotated as they serialize differently in JSON-L
                this.AddVersionAnnotation(payloadElement);
            }
        }
Exemple #12
0
        /// <summary>
        /// Visits the children of the given payload element and replaces it with a copy if any child changes
        /// </summary>
        /// <param name="payloadElement">The payload element to potentially replace</param>
        /// <returns>The original element or a copy to replace it with</returns>
        public virtual ODataPayloadElement Visit(DeferredLink payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
            if (this.alwaysReplace)
            {
                return(payloadElement.ReplaceWith <DeferredLink>(new DeferredLink()
                {
                    UriString = payloadElement.UriString
                }));
            }

            return(payloadElement);
        }
Exemple #13
0
        /// <summary>
        /// Computes the XmlTreeAnnotation for an association link.
        /// </summary>
        /// <param name="associationLink">The association link to compute the stream property for.</param>
        /// <returns>The <see cref="XmlTreeAnnotation"/> for the link specified in <paramref name="associationLink"/>.</returns>
        private XmlTreeAnnotation GetAssociationLinkXmlTreeAnnotation(DeferredLink associationLink)
        {
            if (associationLink == null)
            {
                return(null);
            }

            // Add all the attributes that are already stored on the association link as annotations
            List <XmlTreeAnnotation> attributes = new List <XmlTreeAnnotation>();

            attributes.AddRange(associationLink.Annotations.OfType <XmlTreeAnnotation>().Where(a => a.IsAttribute));
            return(XmlTreeAnnotation.Atom(TestAtomConstants.AtomLinkElementName, null, attributes.ToArray()));
        }
        /// <summary>
        /// Initializes an ODataNestedResourceInfo instance for the deferred link payload.
        /// </summary>
        /// <param name="payloadElement">The deferred link to process.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            Debug.Assert(this.currentLink != null);
            ODataNestedResourceInfo navigationLink = this.currentLink;

            this.currentLink = null;

            // TODO, ckerer: where do I get the info whether this links is a singleton or collection?
            navigationLink.Url = new Uri(payloadElement.UriString);

            this.writer.WriteStart(navigationLink);
            base.Visit(payloadElement);
            this.writer.WriteEnd();
        }
Exemple #15
0
        /// <summary>
        /// Initializes an ODataNestedResourceInfo instance for the deferred link payload.
        /// </summary>
        /// <param name="payloadElement">The deferred link to process.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            if (this.items.Peek() is ODataNestedResourceInfo)
            {
                ODataNestedResourceInfo navigationLink = (ODataNestedResourceInfo)this.items.Pop();
                navigationLink.Url = new Uri(payloadElement.UriString);

                var contentType = payloadElement.Annotations.Where(a => a is ContentTypeAnnotation).SingleOrDefault();
                if (contentType != null)
                {
                    navigationLink.IsCollection = contentType.StringRepresentation.Contains("feed");
                }

                var entry = (ODataResource)this.items.Peek();

                var annotation = entry.GetAnnotation <ODataEntryNavigationLinksObjectModelAnnotation>();
                if (annotation == null)
                {
                    annotation = new ODataEntryNavigationLinksObjectModelAnnotation();
                    entry.SetAnnotation <ODataEntryNavigationLinksObjectModelAnnotation>(annotation);
                }

                annotation.Add(navigationLink, this.currentPropertyPosition);

                this.items.Push(navigationLink);

                if (!this.response)
                {
                    navigationLink.SetAnnotation(new ODataNavigationLinkExpandedItemObjectModelAnnotation()
                    {
                        ExpandedItem = new ODataEntityReferenceLink()
                        {
                            Url = navigationLink.Url
                        }
                    });
                }

                base.Visit(payloadElement);
                this.currentPropertyPosition++;
            }
            else
            {
                this.items.Push(new ODataEntityReferenceLink()
                {
                    Url = new Uri(payloadElement.UriString)
                });
            }
        }
        /// <summary>
        /// Normalizes navigation property specific atom metadata.
        /// </summary>
        /// <param name="payloadElement">The payload element to normalize.</param>
        public override void Visit(NavigationPropertyInstance payloadElement)
        {
            DeferredLink deferredLink = payloadElement.Value as DeferredLink;

            if (deferredLink != null)
            {
                // If there is a type annotation specified as a XmlTreeAnnotion, copy its value over to a ContentTypeAnnotation as well.
                XmlTreeAnnotation typeAnnotation = deferredLink.Annotations.OfType <XmlTreeAnnotation>().SingleOrDefault(a => a.LocalName == TestAtomConstants.AtomLinkTypeAttributeName);
                if (typeAnnotation != null)
                {
                    deferredLink.WithContentType(typeAnnotation.PropertyValue);
                }
            }

            base.Visit(payloadElement);
        }
        /// <summary>
        /// Sets the expected navigation property for a top-level entity reference link.
        /// </summary>
        /// <param name="entityReferenceLink">The entity reference link instance to set the expected navigation property for.</param>
        /// <param name="owningType">The type owning the expected property.</param>
        /// <param name="navigationPropertyName">The name of the navigation property to set as the expected property.</param>
        /// <returns>The <paramref name="entityReferenceLink"/> after its expected property was set.</returns>
        public static DeferredLink ExpectedNavigationProperty(
            this DeferredLink entityReferenceLink,
            EntitySet entitySet,
            EntityType owningType,
            string navigationPropertyName)
        {
            ExceptionUtilities.CheckArgumentNotNull(entityReferenceLink, "entityReferenceLink");
            ExceptionUtilities.CheckArgumentNotNull(owningType, "owningType");
            ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(navigationPropertyName, "navigationPropertyName");

            ExpectedTypeODataPayloadElementAnnotation annotation = AddExpectedTypeAnnotation(entityReferenceLink);

            annotation.EntitySet          = entitySet;
            annotation.OwningType         = owningType;
            annotation.NavigationProperty = owningType.GetNavigationProperty(navigationPropertyName);
            return(entityReferenceLink);
        }
            /// <summary>
            /// Visits a payload element whose root is a DeferredLink.
            /// </summary>
            /// <param name="expected">The root node of payload element being visited.</param>
            public void Visit(DeferredLink expected)
            {
                ExceptionUtilities.CheckArgumentNotNull(expected, "expected");
                var observed = this.GetNextObservedElement <DeferredLink>();

                using (this.Assert.WithMessage("Deferred link did not match expectation"))
                {
                    if (this.comparingJsonLightResponse && expected.UriString == null)
                    {
                        this.Assert.IsNotNull(observed.UriString, "Conventional template evaluation should compute the Uri string of the deferred link.");
                    }
                    else
                    {
                        this.Assert.AreEqual(expected.UriString, observed.UriString, "Uri string did not match expectation");
                    }

                    this.CompareAnnotations(expected.Annotations, observed.Annotations);
                }
            }
        public void EntityReferenceLinkReadingBaseUriTest()
        {
            EdmModel model = (EdmModel)Microsoft.Test.OData.Utils.Metadata.TestModels.BuildTestModel();

            model.Fixup();

            DeferredLink link = PayloadBuilder.DeferredLink("http://odata.org/dummy");

            this.CombinatorialEngineProvider.RunCombinations(
                payloadUris,
                baseUriValues,
                resolvers,
                new bool[] { false, true },
                this.ReaderTestConfigurationProvider.AtomFormatConfigurations.Where(tc => !tc.IsRequest),
                (payloadUri, baseUriValue, resolver, runInBatch, testConfiguration) =>
            {
                Action <DeferredLink, Uri, ReaderTestConfiguration> setLinkAction =
                    (instance, uri, testConfig) => instance.UriString             = UriToString(uri);
                this.RunBaseUriReadingTest(link, setLinkAction, model, payloadUri, baseUriValue, resolver, testConfiguration, runInBatch);
            });
        }
Exemple #20
0
            /// <summary>
            /// Visits an entity reference link collection.
            /// </summary>
            /// <param name="entityReferenceLinks">The entity reference link collection to visit.</param>
            protected override ODataPayloadElement VisitEntityReferenceLinks(ODataEntityReferenceLinks entityReferenceLinks)
            {
                ExceptionUtilities.CheckArgumentNotNull(entityReferenceLinks, "entityReferenceLinks");

                LinkCollection linkCollection = PayloadBuilder.LinkCollection()
                                                .InlineCount(entityReferenceLinks.Count)
                                                .NextLink(entityReferenceLinks.NextPageLink == null ? null : entityReferenceLinks.NextPageLink.OriginalString);

                IEnumerable <ODataEntityReferenceLink> links = entityReferenceLinks.Links;

                if (links != null)
                {
                    foreach (ODataEntityReferenceLink link in links)
                    {
                        DeferredLink deferredLink = (DeferredLink)this.Visit(link);
                        linkCollection.Add(deferredLink);
                    }
                }

                return(linkCollection);
            }
Exemple #21
0
        private bool TryGetLink(JsonObject jsonObject, out ODataPayloadElement elem)
        {
            elem = null;

            // if this object has exactly one property with key "uri"
            // Example:
            // {
            //     "uri": "http://services.odata.org/OData/OData.svc/Products(0)"
            // },
            // return DeferredLink
            if (jsonObject.Properties.Count() == 1 && jsonObject.Properties.Single().Name == UriFieldName)
            {
                elem = new DeferredLink()
                {
                    UriString = ((JsonPrimitiveValue)jsonObject.Properties.Single().Value).Value as string,
                };

                return(true);
            }

            return(false);
        }
            /// <summary>
            /// Visits a navigation link item.
            /// </summary>
            /// <param name="navigationLink">The navigation link to visit.</param>
            /// <returns>An ODataPayloadElement representing the navigation link.</returns>
            protected override ODataPayloadElement VisitNavigationLink(ODataNavigationLink navigationLink)
            {
                ExceptionUtilities.CheckArgumentNotNull(navigationLink, "navigationLink");

                NavigationPropertyInstance navigationProperty = (NavigationPropertyInstance)base.VisitNavigationLink(navigationLink);

                // In ATOM even deferred links may know if they point to collection or singleton.
                // So add the content type annotation to them (through it IsCollection) so that comparison is precise.
                DeferredLink deferredLink = navigationProperty.Value as DeferredLink;

                if (deferredLink != null && navigationLink.IsCollection.HasValue)
                {
                    deferredLink.IsCollection(navigationLink.IsCollection.Value);
                }

                AtomLinkMetadata atomMetadata = navigationLink.GetAnnotation <AtomLinkMetadata>();

                if (atomMetadata != null && deferredLink != null)
                {
                    ConvertAtomLinkChildrenMetadata(atomMetadata, navigationProperty.Value);
                }

                return(navigationProperty);
            }
        /// <summary>
        /// Visits a payload element whose root is a DeferredLink.
        /// </summary>
        /// <param name="payloadElement">The root node of the payload element being visited.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            base.Visit(payloadElement);

            if (this.CurrentElementIsRoot() && payloadElement.GetAnnotation <JsonLightContextUriAnnotation>() == null)
            {
                var expectedTypeAnnotation = payloadElement.GetAnnotation <ExpectedTypeODataPayloadElementAnnotation>();
                if (expectedTypeAnnotation == null)
                {
                    // Any navigation property in the model should suffice for generating the context uri because there is no
                    // type information in the link.
                    if (this.testDescriptor.PayloadEdmModel != null)
                    {
                        IEdmEntityType edmEntityTypeWithNavProps = this.testDescriptor.PayloadEdmModel.EntityTypes().FirstOrDefault(e => e.NavigationProperties().Any());
                        ExceptionUtilities.CheckObjectNotNull(edmEntityTypeWithNavProps, "No navigation properties found in the model");
                        IEdmNavigationProperty edmNavProperty = edmEntityTypeWithNavProps.NavigationProperties().First();

                        payloadElement.AddAnnotation(new ExpectedTypeODataPayloadElementAnnotation {
                            EdmOwningType = edmEntityTypeWithNavProps, EdmNavigationProperty = edmNavProperty
                        });
                    }
                }
            }
        }
Exemple #24
0
            /// <summary>
            /// Visits a navigation link item.
            /// </summary>
            /// <param name="navigationLink">The navigation link to visit.</param>
            protected override ODataPayloadElement VisitNavigationLink(ODataNestedResourceInfo navigationLink)
            {
                ExceptionUtilities.CheckArgumentNotNull(navigationLink, "navigationLink");

                ODataPayloadElement navigationPropertyContent = null;

                // check whether there is an entry or feed associated with the link
                var expandedItemAnnotation = navigationLink.GetAnnotation <ODataNavigationLinkExpandedItemObjectModelAnnotation>();

                if (expandedItemAnnotation != null)
                {
                    string navigationLinkUrlString = !this.payloadContainsIdentityMetadata || navigationLink.Url == null ? null : navigationLink.Url.OriginalString;

                    if (expandedItemAnnotation.ExpandedItem is ODataResource)
                    {
                        navigationPropertyContent = new ExpandedLink(this.Visit((ODataResource)expandedItemAnnotation.ExpandedItem))
                        {
                            UriString = navigationLinkUrlString
                        };
                    }
                    else if (expandedItemAnnotation.ExpandedItem is ODataResourceSet)
                    {
                        navigationPropertyContent = new ExpandedLink(this.Visit((ODataResourceSet)expandedItemAnnotation.ExpandedItem))
                        {
                            UriString = navigationLinkUrlString
                        };
                    }
                    else if (expandedItemAnnotation.ExpandedItem is ODataEntityReferenceLink)
                    {
                        ExceptionUtilities.Assert(!this.response, "Entity reference links in navigation links can only appear in requests.");
                        navigationPropertyContent = this.VisitEntityReferenceLink((ODataEntityReferenceLink)expandedItemAnnotation.ExpandedItem);
                    }
                    else if (expandedItemAnnotation.ExpandedItem is List <ODataItem> )
                    {
                        ExceptionUtilities.Assert(!this.response, "Navigation links with multiple items in content can only appear in requests.");
                        LinkCollection linkCollection = new LinkCollection();
                        foreach (ODataItem item in (List <ODataItem>)expandedItemAnnotation.ExpandedItem)
                        {
                            if (item is ODataResourceSet)
                            {
                                linkCollection.Add(new ExpandedLink(this.Visit((ODataResourceSet)item)));
                            }
                            else
                            {
                                ExceptionUtilities.Assert(item is ODataEntityReferenceLink, "Only feed and entity reference links can appear in navigation link content with multiple items.");
                                linkCollection.Add(this.VisitEntityReferenceLink((ODataEntityReferenceLink)item));
                            }
                        }

                        navigationPropertyContent = linkCollection;
                    }
                    else
                    {
                        ExceptionUtilities.Assert(expandedItemAnnotation.ExpandedItem == null, "Only expanded entry, feed or null is allowed.");
                        navigationPropertyContent = new ExpandedLink(new EntityInstance(null, true))
                        {
                            UriString = navigationLinkUrlString
                        };
                    }
                }
                else
                {
                    ExceptionUtilities.Assert(this.response, "Deferred links are only valid in responses.");

                    // this is a deferred link
                    DeferredLink deferredLink = new DeferredLink()
                    {
                        UriString = !this.payloadContainsIdentityMetadata || navigationLink.Url == null ? null : navigationLink.Url.OriginalString,
                    };
                    navigationPropertyContent = deferredLink;
                }

                DeferredLink associationLink = null;

                if (this.payloadContainsIdentityMetadata && navigationLink.AssociationLinkUrl != null)
                {
                    associationLink = new DeferredLink()
                    {
                        UriString = navigationLink.AssociationLinkUrl.OriginalString
                    };
                }

                return(new NavigationPropertyInstance(navigationLink.Name, navigationPropertyContent, associationLink));
            }
        /// <summary>
        /// Asserts that two payload elements are equal.
        /// </summary>
        /// <param name="expected">The expected element</param>
        /// <param name="observed">The actual, observed element</param>
        public void Compare(ODataPayloadElement expected, ODataPayloadElement observed)
        {
            if (expected == null)
            {
                this.Assert.IsNull(observed, "Observed element must be null");
                return;
            }

            this.Assert.AreEqual(expected.ElementType, observed.ElementType, "Element types are not equal.");
            this.Assert.AreEqual(expected.Annotations.Count, observed.Annotations.Count, "Annotation counts are not equal");

            Compare(expected.Annotations, observed.Annotations);

            switch (expected.ElementType)
            {
            case ODataPayloadElementType.EntitySetInstance:
                EntitySetInstance expectedSet = expected as EntitySetInstance;
                EntitySetInstance observedSet = observed as EntitySetInstance;
                this.Assert.AreEqual(expectedSet.NextLink, observedSet.NextLink, "Next links are not equal");
                this.Assert.AreEqual(expectedSet.InlineCount, observedSet.InlineCount, "Inline counts are not equal");

                this.Assert.AreEqual(expectedSet.Count, observedSet.Count, "Entity counts are not equal");

                for (int i = 0; i < expectedSet.Count; i++)
                {
                    Compare(expectedSet[i], observedSet[i]);
                }

                break;

            case ODataPayloadElementType.EntityInstance:
                EntityInstance expectedEntity = expected as EntityInstance;
                EntityInstance observedEntity = observed as EntityInstance;
                this.Assert.AreEqual(expectedEntity.Id, observedEntity.Id, "Entity IDs are not equal");
                this.Assert.AreEqual(expectedEntity.ETag, observedEntity.ETag, "ETags are not equal");
                this.Assert.AreEqual(expectedEntity.IsNull, observedEntity.IsNull, "IsNull flags are not equal");
                this.Assert.AreEqual(expectedEntity.FullTypeName, observedEntity.FullTypeName, "FullTypeNames are not equal");
                this.Assert.AreEqual(expectedEntity.StreamContentType, observedEntity.StreamContentType, "Stream content types are not equal");
                this.Assert.AreEqual(expectedEntity.StreamEditLink, observedEntity.StreamEditLink, "Stream edit links are not equal");
                this.Assert.AreEqual(expectedEntity.StreamETag, observedEntity.StreamETag, "Stream ETags are not equal");
                this.Assert.AreEqual(expectedEntity.StreamSourceLink, observedEntity.StreamSourceLink, "Stream source links are not equal");
                this.Assert.AreEqual(expectedEntity.Properties.Count(), observedEntity.Properties.Count(), "Property counts are not equal");
                for (int i = 0; i < expectedEntity.Properties.Count(); i++)
                {
                    Compare(expectedEntity.Properties.ElementAt(i), observedEntity.Properties.ElementAt(i));
                }

                break;

            case ODataPayloadElementType.ComplexInstance:
                ComplexInstance expectedCI = expected as ComplexInstance;
                ComplexInstance observedCI = observed as ComplexInstance;
                this.Assert.AreEqual(expectedCI.IsNull, observedCI.IsNull, "IsNull flags are not equal");
                this.Assert.AreEqual(expectedCI.FullTypeName, observedCI.FullTypeName, "Full type names are not equal");
                this.Assert.AreEqual(expectedCI.Properties.Count(), observedCI.Properties.Count(), "Property counts are not equal");
                for (int i = 0; i < expectedCI.Properties.Count(); i++)
                {
                    Compare(expectedCI.Properties.ElementAt(i), observedCI.Properties.ElementAt(i));
                }

                break;

            case ODataPayloadElementType.NamedStreamInstance:
                NamedStreamInstance expectedNsi = expected as NamedStreamInstance;
                NamedStreamInstance observedNsi = observed as NamedStreamInstance;
                this.Assert.AreEqual(expectedNsi.Name, observedNsi.Name, "Stream names are not equal");
                this.Assert.AreEqual(expectedNsi.ETag, observedNsi.ETag, "Stream ETags are not equal");
                this.Assert.AreEqual(expectedNsi.EditLink, observedNsi.EditLink, "Edit links are not equal");
                this.Assert.AreEqual(expectedNsi.EditLinkContentType, observedNsi.EditLinkContentType, "Edit link content types are not equal");
                this.Assert.AreEqual(expectedNsi.SourceLink, observedNsi.SourceLink, "Source links are not equal");
                this.Assert.AreEqual(expectedNsi.SourceLinkContentType, observedNsi.SourceLinkContentType, "Source links content types are not equal");
                break;

            case ODataPayloadElementType.NavigationPropertyInstance:
                NavigationPropertyInstance expectedNav = expected as NavigationPropertyInstance;
                NavigationPropertyInstance observedNav = observed as NavigationPropertyInstance;
                Assert.AreEqual(expectedNav.Name, observedNav.Name, "Navigation property names are not equal");
                Compare(expectedNav.AssociationLink, observedNav.AssociationLink);
                Compare(expectedNav.Value, observedNav.Value);
                break;

            case ODataPayloadElementType.ComplexProperty:
                ComplexProperty expectedCP = expected as ComplexProperty;
                ComplexProperty observedCP = observed as ComplexProperty;
                this.Assert.AreEqual(expectedCP.Name, observedCP.Name, "Complex property names are not equal");
                Compare(expectedCP.Value, observedCP.Value);
                break;

            case ODataPayloadElementType.PrimitiveProperty:
                PrimitiveProperty expectedProperty = expected as PrimitiveProperty;
                PrimitiveProperty observedProperty = observed as PrimitiveProperty;
                this.Assert.AreEqual(expectedProperty.Name, observedProperty.Name, "Primitive property names are not equal");
                Compare(expectedProperty.Value, observedProperty.Value);
                break;

            case ODataPayloadElementType.PrimitiveValue:
                PrimitiveValue expectedValue = expected as PrimitiveValue;
                PrimitiveValue observedValue = observed as PrimitiveValue;
                this.Assert.AreEqual(expectedValue.IsNull, observedValue.IsNull, "IsNull flags are not equal");
                if (expectedValue.FullTypeName != null)
                {
                    if (expectedValue.FullTypeName.Equals("Edm.String"))
                    {
                        this.Assert.IsTrue(string.IsNullOrEmpty(observedValue.FullTypeName) || observedValue.FullTypeName.Equals("Edm.String"), "FullTypeName should be null or Edm.String");
                    }
                    else
                    {
                        this.Assert.AreEqual(expectedValue.FullTypeName, observedValue.FullTypeName, "Full type names are not equal");
                    }
                }
                else
                {
                    this.Assert.IsNull(observedValue.FullTypeName, "observed full type name should be null");
                }

                this.Assert.AreEqual(expectedValue.ClrValue, observedValue.ClrValue, "Clr values are not equal");
                break;

            case ODataPayloadElementType.DeferredLink:
                DeferredLink expectedLink = expected as DeferredLink;
                DeferredLink observedLink = observed as DeferredLink;
                this.Assert.AreEqual(expectedLink.UriString, observedLink.UriString, "Uris are not equal");
                break;

            case ODataPayloadElementType.ExpandedLink:
                ExpandedLink expectedExpand = expected as ExpandedLink;
                ExpandedLink observedExpand = observed as ExpandedLink;
                Compare(expectedExpand.ExpandedElement, observedExpand.ExpandedElement);
                break;

            case ODataPayloadElementType.LinkCollection:
                LinkCollection expectedLinks = expected as LinkCollection;
                LinkCollection observedLinks = observed as LinkCollection;
                this.Assert.AreEqual(expectedLinks.Count, observedLinks.Count, "Link counts are not equal");
                this.Assert.AreEqual(expectedLinks.InlineCount, observedLinks.InlineCount, "Link inline counts are not equal");
                for (int i = 0; i < expectedLinks.Count; i++)
                {
                    Compare(expectedLinks[i], observedLinks[i]);
                }

                break;

            case ODataPayloadElementType.PrimitiveMultiValueProperty:
                var expectedPrimitiveCollectionProperty = expected as PrimitiveMultiValueProperty;
                var observedPrimitiveCollectionProperty = observed as PrimitiveMultiValueProperty;
                this.Assert.AreEqual(expectedPrimitiveCollectionProperty.Name, observedPrimitiveCollectionProperty.Name, "Property names are not equal");
                this.Assert.AreEqual(expectedPrimitiveCollectionProperty.Value.FullTypeName, observedPrimitiveCollectionProperty.Value.FullTypeName, "Full type names are not equal");
                this.Assert.AreEqual(expectedPrimitiveCollectionProperty.Value.Count, observedPrimitiveCollectionProperty.Value.Count, "Collection counts are not equal");
                for (int i = 0; i < expectedPrimitiveCollectionProperty.Value.Count; i++)
                {
                    Compare(expectedPrimitiveCollectionProperty.Value[i], observedPrimitiveCollectionProperty.Value[i]);
                }

                break;

            case ODataPayloadElementType.ComplexMultiValueProperty:
                var expectedComplexCollectionProperty = expected as ComplexMultiValueProperty;
                var observedComplexCollectionProperty = observed as ComplexMultiValueProperty;
                this.Assert.AreEqual(expectedComplexCollectionProperty.Name, observedComplexCollectionProperty.Name, "Property names are not equal");
                this.Assert.AreEqual(expectedComplexCollectionProperty.Value.FullTypeName, observedComplexCollectionProperty.Value.FullTypeName, "Full type names are not equal");
                this.Assert.AreEqual(expectedComplexCollectionProperty.Value.Count, observedComplexCollectionProperty.Value.Count, "Collection counts are not equal");
                for (int i = 0; i < expectedComplexCollectionProperty.Value.Count; i++)
                {
                    Compare(expectedComplexCollectionProperty.Value[i], observedComplexCollectionProperty.Value[i]);
                }

                break;

            case ODataPayloadElementType.PrimitiveCollection:
                var expectedPrimitiveCollection = expected as PrimitiveCollection;
                var observedPrimitiveCollection = observed as PrimitiveCollection;
                this.Assert.AreEqual(expectedPrimitiveCollection.Count, observedPrimitiveCollection.Count, "Collection counts are not equal");
                for (int i = 0; i < expectedPrimitiveCollection.Count; i++)
                {
                    Compare(expectedPrimitiveCollection[i], observedPrimitiveCollection[i]);
                }

                break;

            case ODataPayloadElementType.ComplexInstanceCollection:
                var expectedComplexCollection = expected as ComplexInstanceCollection;
                var observedComplexCollection = observed as ComplexInstanceCollection;
                this.Assert.AreEqual(expectedComplexCollection.Count, observedComplexCollection.Count, "Collection counts are not equal");
                for (int i = 0; i < expectedComplexCollection.Count; i++)
                {
                    Compare(expectedComplexCollection[i], observedComplexCollection[i]);
                }

                break;

            case ODataPayloadElementType.NullPropertyInstance:
            case ODataPayloadElementType.EmptyUntypedCollection:
                break;

            default:
                this.Assert.Fail("Case " + expected.ElementType + " unhandled");
                break;
            }
        }
        private void AddFoldedLinksToEntityInsertPayload(DataServiceContextData contextData, EntityDescriptorData entityDescriptorData, EntityInstance payload)
        {
            var entityType = this.ModelSchema.EntityTypes.Single(t => t.FullName == entityDescriptorData.EntityClrType.FullName);

            foreach (var linkDescriptor in contextData.LinkDescriptorsData.Where(l => l.SourceDescriptor == entityDescriptorData))
            {
                if (linkDescriptor.TargetDescriptor.State == EntityStates.Added)
                {
                    continue;
                }

                var navigationProperty = entityType.AllNavigationProperties.Single(n => n.Name == linkDescriptor.SourcePropertyName);

                string contentType = MimeTypes.ApplicationAtomXml + ";type=";
                if (navigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
                {
                    contentType += "feed";
                }
                else
                {
                    contentType += "entry";
                }

                // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity
                // and the identity may not be an actual uri
                var link = new DeferredLink() { UriString = linkDescriptor.TargetDescriptor.EditLink.OriginalString }
                    .WithContentType(contentType).WithTitleAttribute(linkDescriptor.SourcePropertyName);

                payload.Add(new NavigationPropertyInstance(linkDescriptor.SourcePropertyName, link));
            }
        }
            /// <summary>
            /// Visits a payload element whose root is a DeferredLink.
            /// </summary>
            /// <param name="payloadElement">The root node of payload element being visited.</param>
            public void Visit(DeferredLink payloadElement)
            {
                ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
                bool needsWrapping = this.isRootElement;
                this.isRootElement = false;
                if (needsWrapping)
                {                    
                    this.writer.StartObjectScope();
                    this.writer.WriteName("uri");
                    this.writer.WriteString(payloadElement.UriString);
                }
                else
                {
                    this.writer.StartObjectScope();

                    // __deferred is used for payloads generated by the server and sent to the client
                    // __metadata is used if the client wants to update the navigation property
                    // We are always using __metadata because the test framework is only used to generate                    
                    // a payload on the client and sent them to the server.
                    this.writer.WriteName("__metadata");
                    this.writer.StartObjectScope();
                    this.writer.WriteName("uri");
                    this.writer.WriteString(payloadElement.UriString);
                    this.writer.EndScope();
                    this.writer.EndScope();
                }

                if (needsWrapping)
                {
                    this.writer.EndScope();
                }
            }
 public override void Visit(DeferredLink payloadElement)
 {
     base.Visit(payloadElement);
     payloadElement.RemoveAnnotations(typeof(ContentTypeAnnotation));
 }
 /// <summary>
 /// Visits the payload element and annotates it with metadata
 /// </summary>
 /// <param name="payloadElement">The payload element to visit</param>
 public override void Visit(DeferredLink payloadElement)
 {
     ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
     var navigation = this.MetadataStack.OfType<NavigationProperty>().FirstOrDefault();
     ExceptionUtilities.CheckObjectNotNull(navigation, "No navigation property found on metadata stack");
     payloadElement.AddAnnotationIfNotExist(new NavigationPropertyAnnotation() { Property = navigation });
 }
        public void EntityReferenceLinksReaderAtomTest()
        {
            const string link1String = "http://odata.org/deferred1";
            const string link2String = "http://odata.org/deferred2";
            const string nextString  = "http://odata.org/next";

            DeferredLink link1 = PayloadBuilder.DeferredLink(link1String);
            DeferredLink link2 = PayloadBuilder.DeferredLink(link2String);

            #region Extra attributes on the <feed> element
            var linksElementPayloads = new[]
            {
                // extra attributes on <links> element without content
                new { PayloadElement = PayloadBuilder.LinkCollection(), Template = "<feed {0} />", },

                // extra attributes on <m:count> element
                new { PayloadElement = PayloadBuilder.LinkCollection().InlineCount(1), Template = "<feed><m:count {0}>1</m:count></feed>", },

                // extra attributes on <d:next> element
                new { PayloadElement = PayloadBuilder.LinkCollection().NextLink("http://odata.org/next"), Template = "<feed><d:next {0}>http://odata.org/next</d:next></feed>", },
            };

            string[] attributes = new string[]
            {
                "foo='bar'",
                "m:foo='bar'",
                "foo=''",
                "lang='invalid'",
            };

            IEnumerable <PayloadReaderTestDescriptor> testDescriptors = linksElementPayloads.SelectMany(linkPayload =>
                                                                                                        attributes.Select(attribute =>
            {
                var payloadElement = linkPayload.PayloadElement.DeepCopy();
                string xmlRep      = string.Format(CultureInfo.InvariantCulture, linkPayload.Template, attribute);
                payloadElement     = payloadElement.XmlRepresentation(xmlRep);

                return(new PayloadReaderTestDescriptor(this.Settings)
                {
                    PayloadElement = payloadElement,
                });
            }));
            #endregion Extra attributes on the <links> element

            #region Extra padding between the elements
            var extraPaddingPayloads = new[]
            {
                new { PayloadElement = PayloadBuilder.LinkCollection(), Template = "<feed>{0}</feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().InlineCount(1), Template = "<feed>{0}<m:count>1</m:count></feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().InlineCount(1), Template = "<feed><m:count>1</m:count>{0}</feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().NextLink(nextString), Template = "<feed>{0}<d:next>http://odata.org/next</d:next></feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().NextLink(nextString), Template = "<feed><d:next>http://odata.org/next</d:next>{0}</feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().InlineCount(1).NextLink(nextString), Template = "<feed><m:count>1</m:count>{0}<d:next>http://odata.org/next</d:next></feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().Item(link1).InlineCount(1).NextLink(nextString), Template = "<feed><m:count>1</m:count>{0}<m:ref id =\"" + link1String + "\"/><d:next>" + nextString + "</d:next></feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().Item(link1).InlineCount(1).NextLink(nextString), Template = "<feed><m:count>1</m:count><m:ref id=\"" + link1String + "\"/>{0}<d:next>" + nextString + "</d:next></feed>", },
                new { PayloadElement = PayloadBuilder.LinkCollection().Item(link1).Item(link2).InlineCount(2).NextLink(nextString), Template = "<feed><m:count>2</m:count><m:ref id=\"" + link1String + "\"/>{0}<m:ref id=\"" + link2String + "\"/><d:next>" + nextString + "</d:next></feed>", },
            };

            string[] xmlPaddingToIgnore = new string[]
            {
                string.Empty,                                           // Nothing
                "  \r\n\t",                                             // Whitespace only
                "<!--s--> <?value?>",                                   // Insignificant nodes
                "some text <![CDATA[cdata]]>",                          // Significant nodes to be ignored
                "<foo xmlns=''/>",                                      // Element in no namespace
                "<c:foo xmlns:c='ref' attr='1'><c:child/>text</c:foo>", // Element in custom namespace
                "<d:properties/>",                                      // Element in data namespace (should be ignored as well)
                "<entry/>",                                             // Element in atom namespace (should also be ignored)
            };

            IEnumerable <PayloadReaderTestDescriptor> extraPaddingTestDescriptors = extraPaddingPayloads.SelectMany(extraPaddingPayload =>
                                                                                                                    xmlPaddingToIgnore.Select(xmlPadding =>
            {
                var payloadElement = extraPaddingPayload.PayloadElement.DeepCopy();
                string xmlRep      = string.Format(CultureInfo.InvariantCulture, extraPaddingPayload.Template, xmlPadding);
                payloadElement     = payloadElement.XmlRepresentation(xmlRep);

                return(new PayloadReaderTestDescriptor(this.Settings)
                {
                    PayloadElement = payloadElement,
                });
            }));
            testDescriptors = testDescriptors.Concat(extraPaddingTestDescriptors);
            #endregion Extra padding between the elements

            #region Extra elements in the <feed> content
            LinkCollection links = PayloadBuilder.LinkCollection()
                                   .InlineCount(2)
                                   .Item(PayloadBuilder.DeferredLink(link1String))
                                   .Item(PayloadBuilder.DeferredLink(link2String))
                                   .NextLink(nextString);

            XElement ref1Element = new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataRefElementName);
            ref1Element.SetAttributeValue(TestAtomConstants.AtomIdElementName, link1String);

            XElement ref2Element = new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataRefElementName);
            ref1Element.SetAttributeValue(TestAtomConstants.AtomIdElementName, link2String);

            XElement xmlRepresentation = new XElement(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomFeedElementName,
                                                      new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataCountElementName, "2"),
                                                      ref1Element,
                                                      ref2Element,
                                                      new XElement(TestAtomConstants.ODataXNamespace + TestAtomConstants.ODataNextLinkElementName, nextString));

            XElement[] extraElements = new XElement[]
            {
                new XElement(XName.Get("foo"), "bar"),
                new XElement(XName.Get("foo"), new XElement(XName.Get("bar"))),
                new XElement(XName.Get("foo"), new XAttribute(XName.Get("bar"), "attribute-value")),
                new XElement(TestAtomConstants.ODataMetadataXNamespace + "foo", "bar"),
                new XElement(TestAtomConstants.ODataMetadataXNamespace + "foo", new XElement(TestAtomConstants.ODataMetadataXNamespace + "bar")),
                new XElement(XName.Get("foo"), new XAttribute(TestAtomConstants.ODataMetadataXNamespace + "bar", "attribute-value")),
                // "Ref" element in the OData metadaata namespace, should be ignored, "ref" is expected
                new XElement(TestAtomConstants.ODataMetadataXNamespace + "Ref"),
                // "uri" element in the OData namespace, should be ignored, OData metadata namespace is expected
                new XElement(TestAtomConstants.ODataXNamespace + "ref"),
            };

            IEnumerable <PayloadReaderTestDescriptor> extraElementTestDescriptors = extraElements.SelectMany(extraElement =>
            {
                return(InjectElement(extraElement, xmlRepresentation).Select(linksWithInjectedElement =>
                                                                             new PayloadReaderTestDescriptor(this.Settings)
                {
                    PayloadElement = links.DeepCopy().XmlRepresentation(linksWithInjectedElement),
                }
                                                                             ));
            });
            testDescriptors = testDescriptors.Concat(extraElementTestDescriptors);
            #endregion Extra elements in the <links> content

            #region Various payload orders for an error element
            links = PayloadBuilder.LinkCollection()
                    .InlineCount(1)
                    .Item(PayloadBuilder.DeferredLink(link1String))
                    .NextLink(nextString);

            ref1Element = new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataRefElementName);
            ref1Element.SetAttributeValue(TestAtomConstants.AtomIdElementName, link1String);

            xmlRepresentation = new XElement(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomFeedElementName,
                                             new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataCountElementName, "1"),
                                             ref1Element,
                                             new XElement(TestAtomConstants.ODataXNamespace + TestAtomConstants.ODataNextLinkElementName, nextString));

            IEnumerable <PayloadReaderTestDescriptor> payloadOrderTestDescriptors = GeneratePayloadOrders(xmlRepresentation).Select(xmlRep =>
                                                                                                                                    new PayloadReaderTestDescriptor(this.Settings)
            {
                PayloadElement = links.DeepCopy().XmlRepresentation(xmlRep),
            }
                                                                                                                                    );

            testDescriptors = testDescriptors.Concat(payloadOrderTestDescriptors);
            #endregion Various payload orders for an error element

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors,
                this.ReaderTestConfigurationProvider.AtomFormatConfigurations,
                (testDescriptor, testConfiguration) =>
            {
                testDescriptor.RunTest(testConfiguration);
            });
        }
        /// <summary>
        /// Initializes an ODataNavigationLink instance for the deferred link payload.
        /// </summary>
        /// <param name="payloadElement">The deferred link to process.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            if (this.items.Peek() is ODataNavigationLink)
            {
                ODataNavigationLink navigationLink = (ODataNavigationLink)this.items.Pop();
                navigationLink.Url = new Uri(payloadElement.UriString);
                AddLinkMetadata(payloadElement, navigationLink);

                var contentType = payloadElement.Annotations.Where(a => a is ContentTypeAnnotation).SingleOrDefault();
                if (contentType != null)
                {
                    navigationLink.IsCollection = contentType.StringRepresentation.Contains("feed");
                }

                var entry = (ODataEntry)this.items.Peek();

                var annotation = entry.GetAnnotation<ODataEntryNavigationLinksObjectModelAnnotation>();
                if (annotation == null)
                {
                    annotation = new ODataEntryNavigationLinksObjectModelAnnotation();
                    entry.SetAnnotation<ODataEntryNavigationLinksObjectModelAnnotation>(annotation);
                }

                annotation.Add(navigationLink, this.currentPropertyPosition);

                this.items.Push(navigationLink);

                if (!this.response)
                {
                    navigationLink.SetAnnotation(new ODataNavigationLinkExpandedItemObjectModelAnnotation()
                    {
                        ExpandedItem = new ODataEntityReferenceLink() { Url = navigationLink.Url }
                    });
                }

                base.Visit(payloadElement);
                this.currentPropertyPosition++;
            }
            else
            {
                this.items.Push(new ODataEntityReferenceLink() { Url = new Uri(payloadElement.UriString) });
            }
        }
            /// <summary>
            /// Visits a payload element whose root is a DeferredLink.
            /// </summary>
            /// <param name="payloadElement">The root node of payload element being visited.</param>
            public void Visit(DeferredLink payloadElement)
            {
                ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");

                // if the link is deferred, there is no data to validate
                // TODO: add mising verification
                var originalXmlPayloadElementAnnotation = payloadElement.Annotations.OfType<XmlPayloadElementRepresentationAnnotation>().SingleOrDefault();
                if (originalXmlPayloadElementAnnotation != null)
                {
                    // Validate that the namespace used is not the metadata namespace
                    var element = (XElement)originalXmlPayloadElementAnnotation.XmlNodes.Single();
                    this.parent.Assert.AreEqual(element.Name.Namespace.NamespaceName, ODataConstants.DataServicesNamespaceName, "Producing uri in Metadata Namespace is invalid OData");
                }
            }
        /// <summary>
        /// Visits a payload element whose root is a DeferredLink.
        /// </summary>
        /// <param name="payloadElement">The root node of the payload element being visited.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            base.Visit(payloadElement);

            if (this.CurrentElementIsRoot() && payloadElement.GetAnnotation<JsonLightContextUriAnnotation>() == null)
            {
                var expectedTypeAnnotation = payloadElement.GetAnnotation<ExpectedTypeODataPayloadElementAnnotation>();
                if (expectedTypeAnnotation == null)
                {
                    // Any navigation property in the model should suffice for generating the context uri because there is no
                    // type information in the link.
                    if (this.testDescriptor.PayloadEdmModel != null)
                    {
                        IEdmEntityType edmEntityTypeWithNavProps = this.testDescriptor.PayloadEdmModel.EntityTypes().FirstOrDefault(e => e.NavigationProperties().Any());
                        ExceptionUtilities.CheckObjectNotNull(edmEntityTypeWithNavProps, "No navigation properties found in the model");
                        IEdmNavigationProperty edmNavProperty = edmEntityTypeWithNavProps.NavigationProperties().First();

                        payloadElement.AddAnnotation(new ExpectedTypeODataPayloadElementAnnotation { EdmOwningType = edmEntityTypeWithNavProps, EdmNavigationProperty = edmNavProperty });
                    }
                }
            }
        }
Exemple #34
0
        private bool TryGetEntityInstance(JsonObject jsonObject, out ODataPayloadElement elem)
        {
            string uri         = this.GetMetadataPropertyValue(jsonObject, UriFieldName);
            string id          = this.GetMetadataPropertyValue(jsonObject, IdFieldName);
            string typeName    = this.GetMetadataPropertyValue(jsonObject, TypeFieldName);
            string etag        = this.GetMetadataPropertyValue(jsonObject, ETagFieldName);
            string streamSrc   = this.GetMetadataPropertyValue(jsonObject, MediaSrcFieldName);
            string streamEdit  = this.GetMetadataPropertyValue(jsonObject, EditMediaFieldName);
            string streamEtag  = this.GetMetadataPropertyValue(jsonObject, MediaETagFieldName);
            string contentType = this.GetMetadataPropertyValue(jsonObject, ContentTypeFieldName);

            List <ServiceOperationDescriptor> actions = null;
            bool containsActionsMetadata = this.TryGetServiceOperationDescriptorAnnotations(jsonObject, "actions", out actions);

            List <ServiceOperationDescriptor> functions = null;
            bool containsFunctionMetadata = this.TryGetServiceOperationDescriptorAnnotations(jsonObject, "functions", out functions);

            // if this object has any property with key "__deferred"
            // or metadata tag returns a "uri", "id", or "etag" property,
            // or actions, or functions
            // return EntityInstance
            // else return ComplexInstance
            bool isEntity = this.ContainsDeferredLink(jsonObject);

            isEntity |= uri != null;
            isEntity |= id != null;
            isEntity |= etag != null;
            isEntity |= containsActionsMetadata;
            isEntity |= containsFunctionMetadata;

            if (isEntity)
            {
                EntityInstance entity = new EntityInstance(typeName, false, id, etag);

                if (containsActionsMetadata)
                {
                    foreach (var action in actions)
                    {
                        entity.ServiceOperationDescriptors.Add(action);
                    }
                }

                if (containsFunctionMetadata)
                {
                    foreach (var function in functions)
                    {
                        entity.ServiceOperationDescriptors.Add(function);
                    }
                }

                entity.EditLink          = uri;
                entity.StreamSourceLink  = streamSrc;
                entity.StreamEditLink    = streamEdit;
                entity.StreamETag        = streamEtag;
                entity.StreamContentType = contentType;

                foreach (JsonProperty prop in jsonObject.Properties)
                {
                    if (prop.Name != MetadataFieldName && prop.Name != LinkInfoFieldName)
                    {
                        entity.Add(this.ConvertProperty(prop) as PropertyInstance);
                    }
                }

                // Update entity navigation properties with association links
                JsonObject   metaDataJsonObject = (JsonObject)jsonObject.Properties.Single(p1 => p1.Name == MetadataFieldName).Value;
                JsonProperty properties         = metaDataJsonObject.Properties.FirstOrDefault(p2 => p2.Name == PropertiesFieldName);

                if (properties != null)
                {
                    foreach (JsonProperty jp in ((JsonObject)properties.Value).Properties)
                    {
                        JsonProperty associationUriJsonProperty = ((JsonObject)jp.Value).Properties.SingleOrDefault(p => p.Name == AssociationUriFieldName);
                        DeferredLink newAssociationLink         = new DeferredLink();
                        if (associationUriJsonProperty != null)
                        {
                            newAssociationLink.UriString = ((JsonPrimitiveValue)associationUriJsonProperty.Value).Value as string;
                        }

                        var navigation = entity.Properties.SingleOrDefault(np => np.Name.Equals(jp.Name));

                        NavigationPropertyInstance navigationPropertyInstance = navigation as NavigationPropertyInstance;
                        if (navigationPropertyInstance != null)
                        {
                            navigationPropertyInstance.AssociationLink = newAssociationLink;
                        }
                        else if (navigation is EmptyCollectionProperty)
                        {
                            ExpandedLink newExpandedLink = new ExpandedLink()
                            {
                                ExpandedElement = new EntitySetInstance()
                            };
                            NavigationPropertyInstance newNavigation = new NavigationPropertyInstance(navigation.Name, newExpandedLink, newAssociationLink);
                            entity.Replace(navigation, newNavigation);
                        }
                        else
                        {
                            ExceptionUtilities.Assert(navigation.ElementType == ODataPayloadElementType.NullPropertyInstance, "Invalid type of PropertyInstance : {0}", navigation.ElementType);
                            ExpandedLink newExpandedLink = new ExpandedLink()
                            {
                                ExpandedElement = new EntityInstance()
                            };
                            NavigationPropertyInstance newNavigation = new NavigationPropertyInstance(navigation.Name, newExpandedLink, newAssociationLink);
                            entity.Replace(navigation, newNavigation);
                        }
                    }
                }

                elem = entity;
                return(true);
            }

            elem = null;
            return(false);
        }
        /// <summary>
        /// Computes the XmlTreeAnnotation for an association link.
        /// </summary>
        /// <param name="associationLink">The association link to compute the stream property for.</param>
        /// <returns>The <see cref="XmlTreeAnnotation"/> for the link specified in <paramref name="associationLink"/>.</returns>
        private XmlTreeAnnotation GetAssociationLinkXmlTreeAnnotation(DeferredLink associationLink)
        {
            if (associationLink == null)
            {
                return null;
            }

            // Add all the attributes that are already stored on the association link as annotations
            List<XmlTreeAnnotation> attributes = new List<XmlTreeAnnotation>();
            attributes.AddRange(associationLink.Annotations.OfType<XmlTreeAnnotation>().Where(a => a.IsAttribute));
            return XmlTreeAnnotation.Atom(TestAtomConstants.AtomLinkElementName, null, attributes.ToArray());
        }
Exemple #36
0
        /// <summary>
        /// Converts the given name/value pair into a property element.
        /// And infers the type of property from the converted value.
        /// </summary>
        /// <param name="jsonProperty">the property value</param>
        /// <returns>the converted property</returns>
        private PropertyInstance ConvertProperty(JsonProperty jsonProperty)
        {
            if (jsonProperty.Value.JsonType == JsonValueType.JsonPrimitiveValue && ((JsonPrimitiveValue)jsonProperty.Value).Value == null)
            {
                return(new NullPropertyInstance()
                {
                    Name = jsonProperty.Name
                });
            }
            else
            {
                ODataPayloadElement elem = this.ConvertValue(jsonProperty.Value);
                ExceptionUtilities.CheckObjectNotNull(elem, "Converted property value was null");

                if (elem.ElementType == ODataPayloadElementType.PrimitiveValue)
                {
                    return(new PrimitiveProperty(jsonProperty.Name, (PrimitiveValue)elem));
                }
                else if (elem.ElementType == ODataPayloadElementType.ComplexInstance)
                {
                    return(new ComplexProperty(jsonProperty.Name, (ComplexInstance)elem));
                }
                else if (elem.ElementType == ODataPayloadElementType.EntityInstance)
                {
                    return(new NavigationPropertyInstance(jsonProperty.Name, new ExpandedLink(elem)));
                }
                else if (elem.ElementType == ODataPayloadElementType.DeferredLink)
                {
                    DeferredLink deferredLink = (DeferredLink)elem;
                    return(new NavigationPropertyInstance(jsonProperty.Name, deferredLink));
                }
                else if (elem.ElementType == ODataPayloadElementType.EntitySetInstance)
                {
                    return(new NavigationPropertyInstance(jsonProperty.Name, elem));
                }
                else if (elem.ElementType == ODataPayloadElementType.ComplexMultiValue)
                {
                    ComplexMultiValue complexMultiValue = (ComplexMultiValue)elem;
                    return(new ComplexMultiValueProperty(jsonProperty.Name, complexMultiValue));
                }
                else if (elem.ElementType == ODataPayloadElementType.PrimitiveMultiValue)
                {
                    PrimitiveMultiValue primitiveMultiValue = (PrimitiveMultiValue)elem;
                    return(new PrimitiveMultiValueProperty(jsonProperty.Name, primitiveMultiValue));
                }
                else if (elem.ElementType == ODataPayloadElementType.ComplexInstanceCollection)
                {
                    ComplexInstanceCollection complexCollection = (ComplexInstanceCollection)elem;
                    return(new ComplexMultiValueProperty(jsonProperty.Name, new ComplexMultiValue(null, false, complexCollection.ToArray())));
                }
                else if (elem.ElementType == ODataPayloadElementType.PrimitiveCollection)
                {
                    PrimitiveCollection primitiveCollection = (PrimitiveCollection)elem;
                    return(new PrimitiveMultiValueProperty(jsonProperty.Name, new PrimitiveMultiValue(null, false, primitiveCollection.ToArray())));
                }
                else if (elem.ElementType == ODataPayloadElementType.NamedStreamInstance)
                {
                    NamedStreamInstance nsi = (NamedStreamInstance)elem;
                    nsi.Name = jsonProperty.Name;
                    return(nsi);
                }
                else
                {
                    ExceptionUtilities.Assert(elem.ElementType == ODataPayloadElementType.EmptyUntypedCollection, "Do not know how to handle element of type" + elem.ElementType);
                    return(new EmptyCollectionProperty(jsonProperty.Name, (EmptyUntypedCollection)elem));
                }
            }
        }
        /// <summary>
        /// Deserialize a single link
        /// </summary>
        /// <param name="link">the xml representing the link</param>
        /// <returns>Either an expanded or deferred link</returns>
        private ODataPayloadElement DeserializeLink(XElement link)
        {
            if (link.Name == MetadataUri)
            {
                var result = new DeferredLink()
                {
                    UriString = link.Attribute(AtomId).Value
                };
                AddXmlBaseAnnotation(result, link);

                // add the element so that later validation can happen to validate the MetadataUri namespace is not used
                var xmlPayloadRep = new XmlPayloadElementRepresentationAnnotation()
                {
                    XmlNodes = new XNode[] { link }
                };
                result.Annotations.Add(xmlPayloadRep);
                return(result);
            }

            string     hrefValue     = null;
            XAttribute hrefAttribute = link.Attribute(Href);

            if (hrefAttribute != null)
            {
                if (string.IsNullOrEmpty(hrefAttribute.Value))
                {
                    // special case: navigation properties with null values are represented as empty href tags
                    return(null);
                }

                hrefValue = hrefAttribute.Value;
            }

            // if the link has an inline element, assume it is expanded
            XElement inline = link.Element(MetadataInline);

            if (inline != null)
            {
                // deserialize the expanded element
                ExpandedLink expanded = new ExpandedLink()
                {
                    UriString = hrefValue
                };

                if (inline.HasElements)
                {
                    expanded.ExpandedElement = this.ConvertToPayloadElement(inline.Elements().Single());
                }

                this.AddLinkAttributes(expanded, link);

                return(expanded);
            }
            else
            {
                // otherwise it must be deferred
                DeferredLink deferred = new DeferredLink()
                {
                    UriString = hrefValue
                };

                this.AddLinkAttributes(deferred, link);

                return(deferred);
            }
        }
Exemple #38
0
 /// <summary>
 /// Visits the payload element
 /// </summary>
 /// <param name="payloadElement">The payload element to visit</param>
 public virtual void Visit(DeferredLink payloadElement)
 {
     ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");
 }
        /// <summary>
        /// Visits a DeferredLink, if IncludeRelationshipLinks = true then this will be a V3 payload
        /// </summary>
        /// <param name="payloadElement">payload Element</param>
        public override void Visit(DeferredLink payloadElement)
        {
            ExceptionUtilities.CheckArgumentNotNull(payloadElement, "payloadElement");

            this.IncreaseVersionIfIncludeRelationshipLinksIsTrue(payloadElement);

            base.Visit(payloadElement);
        }
        /// <summary>
        /// Initializes an ODataNavigationLink instance for the deferred link payload.
        /// </summary>
        /// <param name="payloadElement">The deferred link to process.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            Debug.Assert(this.currentLink != null);
            ODataNavigationLink navigationLink = this.currentLink;
            this.currentLink = null;

            // TODO, ckerer: where do I get the info whether this links is a singleton or collection?
            navigationLink.Url = new Uri(payloadElement.UriString);

            AddLinkMetadata(payloadElement, navigationLink);

            this.writer.WriteStart(navigationLink);
            base.Visit(payloadElement);
            this.writer.WriteEnd();
        }
        /// <summary>
        /// Verify the relationship link is as expected.
        /// </summary>
        /// <param name="navigation">navigation property instance for the navigation link</param>
        /// <param name="associationLink">relationship link to verify</param>
        /// <param name="expectedAssociationUri">expected link value</param>
        /// <param name="request">The request needed for error report.</param>
        /// <param name="response">The response to check the content type.</param>
        private void VerifyAssociationLink(NavigationPropertyInstance navigation, DeferredLink associationLink, string expectedAssociationUri, ODataRequest request, ODataResponse response)
        {
            bool linkShouldBeNull = false;
            if (!this.IsAtomResponse(response) && navigation.IsExpanded)
            {
                var expandedValue = ((ExpandedLink)navigation.Value).ExpandedElement;
                linkShouldBeNull = expandedValue == null;
            }

            if (linkShouldBeNull)
            {   
                // json navigations are null, then there are no links
                if (associationLink != null)
                {
                    this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected association link == null, observed '{0}'", associationLink.UriString);
                    this.ReportFailure(request, response);
                    throw new ResponseVerificationException();
                }
            }
            else
            {
                this.VerifyLinkUriValue(associationLink.UriString, expectedAssociationUri, request, response);

                // extra verifications for atom payload
                if (this.IsAtomResponse(response))
                {
                    this.VerifyAtomAssociationLinkTypeAttribute(associationLink, request, response);
                    this.VerifyAtomTitleAttribute(navigation, associationLink, request, response);
                }
            }
        }
 private static void AddLinkMetadata(DeferredLink payloadElement, ODataNavigationLink link)
 {
     AtomLinkMetadata metadata = CreateLinkMetadata(payloadElement.Annotations.OfType<XmlTreeAnnotation>());
     if (metadata != null)
     {
         link.SetAnnotation<AtomLinkMetadata>(metadata);
     }
 }
        /// <summary>
        /// Deserialize a single link
        /// </summary>
        /// <param name="link">the xml representing the link</param>
        /// <returns>Either an expanded or deferred link</returns>
        private ODataPayloadElement DeserializeLink(XElement link)
        {
            if (link.Name == MetadataUri)
            {
                var result = new DeferredLink() { UriString = link.Attribute(AtomId).Value };
                AddXmlBaseAnnotation(result, link);
                
                // add the element so that later validation can happen to validate the MetadataUri namespace is not used
                var xmlPayloadRep = new XmlPayloadElementRepresentationAnnotation() { XmlNodes = new XNode[] { link } };
                result.Annotations.Add(xmlPayloadRep);
                return result;
            }

            string hrefValue = null;
            XAttribute hrefAttribute = link.Attribute(Href);
            if (hrefAttribute != null)
            {
                if (string.IsNullOrEmpty(hrefAttribute.Value))
                {
                    // special case: navigation properties with null values are represented as empty href tags
                    return null;
                }

                hrefValue = hrefAttribute.Value;
            }

            // if the link has an inline element, assume it is expanded
            XElement inline = link.Element(MetadataInline);
            if (inline != null)
            {
                // deserialize the expanded element
                ExpandedLink expanded = new ExpandedLink() { UriString = hrefValue };
                
                if (inline.HasElements)
                {
                    expanded.ExpandedElement = this.ConvertToPayloadElement(inline.Elements().Single());
                }

                this.AddLinkAttributes(expanded, link);

                return expanded;
            }
            else
            {
                // otherwise it must be deferred
                DeferredLink deferred = new DeferredLink() { UriString = hrefValue };

                this.AddLinkAttributes(deferred, link);

                return deferred;
            }
        }
        /// <summary>
        /// Visits a payload element whose root is a DeferredLink.
        /// </summary>
        /// <param name="payloadElement">The root node of the payload element being visited.</param>
        public override void Visit(DeferredLink payloadElement)
        {
            base.Visit(payloadElement);

            if (!this.requestPayload || this.depth > 2)
            {
                // Any deferred link inside a navigation link needs to be annotated (request/response payload differs)
                // Top level response payloads need to be annotated as they serialize differently in JSON-L
                this.AddVersionAnnotation(payloadElement);
            }
        }