/// <summary> /// Reads an element in the ATOM namespace in feed or source content. /// </summary> /// <param name="atomFeedMetadata">The atom feed metadata object to store metadata details in.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:*) - the ATOM element to read. /// Post-Condition: Any - the node after the ATOM element which was read. /// /// If the the property InSourceElement is true (i.e., we're reading within source content), then the value /// of the atom:id element will be stored in the feed metadata as SourceId, otherwise it will be ignored. /// </remarks> internal void ReadAtomElementAsFeedMetadata(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:* elements can be read by this method."); switch (this.XmlReader.LocalName) { case AtomConstants.AtomAuthorElementName: this.ReadAuthorElement(atomFeedMetadata); break; case AtomConstants.AtomCategoryElementName: this.ReadCategoryElement(atomFeedMetadata); break; case AtomConstants.AtomContributorElementName: this.ReadContributorElement(atomFeedMetadata); break; case AtomConstants.AtomGeneratorElementName: this.ReadGeneratorElement(atomFeedMetadata); break; case AtomConstants.AtomIconElementName: this.ReadIconElement(atomFeedMetadata); break; case AtomConstants.AtomIdElementName: // Only store the ID as ATOM metadata if we're within an atom:source element. if (this.InSourceElement) { this.ReadIdElementAsSourceId(atomFeedMetadata); } else { this.XmlReader.Skip(); } break; case AtomConstants.AtomLinkElementName: this.ReadLinkElementIntoLinksCollection(atomFeedMetadata); break; case AtomConstants.AtomLogoElementName: this.ReadLogoElement(atomFeedMetadata); break; case AtomConstants.AtomRightsElementName: this.ReadRightsElement(atomFeedMetadata); break; case AtomConstants.AtomSubtitleElementName: this.ReadSubtitleElement(atomFeedMetadata); break; case AtomConstants.AtomTitleElementName: this.ReadTitleElement(atomFeedMetadata); break; case AtomConstants.AtomUpdatedElementName: this.ReadUpdatedElement(atomFeedMetadata); break; default: // Not something we recognize, so just ignore it. this.XmlReader.Skip(); break; } }
/// <summary> /// Reads an atom:contributor element in a feed. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:contributor) - the atom:contributor element to read. /// Post-Condition: Any - the node after the atom:contributor element which was read. /// </remarks> private void ReadContributorElement(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomContributorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:contributor elements can be read by this method."); atomFeedMetadata.AddContributor(this.ReadAtomPersonConstruct()); }
/// <summary> /// Reads an atom:logo element in a feed. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:logo) - the atom:logo element to read. /// Post-Condition: Any - the node after the atom:logo element which was read. /// </remarks> private void ReadLogoElement(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomLogoElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:logo elements can be read by this method."); this.VerifyNotPreviouslyDefined(atomFeedMetadata.Logo); atomFeedMetadata.Logo = this.ReadUriValuedElement(); }
/// <summary> /// Reads an atom:updated element in a feed. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:updated) - the atom:updated element to read. /// Post-Condition: Any - the node after the atom:updated element which was read. /// </remarks> private void ReadUpdatedElement(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomUpdatedElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:updated elements can be read by this method."); this.VerifyNotPreviouslyDefined(atomFeedMetadata.Updated); atomFeedMetadata.Updated = this.ReadAtomDateConstruct(); }
/// <summary> /// Reads an atom:link element into the Links collection of feed metadata (i.e., links that are not special to the OData protocol). /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:link) - the atom:link element to read. /// Post-Condition: Any - the node after the atom:link element which was read. /// </remarks> private void ReadLinkElementIntoLinksCollection(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomLinkElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:link elements can be read by this method."); // By sending nulls to ReadAtomLinkElementInFeed(), the method will read and store values for href and rel from the wire (inside of using parameter overrides). AtomLinkMetadata linkMetadata = this.ReadAtomLinkElementInFeed(null, null); atomFeedMetadata.AddLink(linkMetadata); }
/// <summary> /// Writes a feed link. /// </summary> /// <param name="feed">The feed that contains the link.</param> /// <param name="relation">Relation attribute of the link.</param> /// <param name="href">href attribute of the link.</param> /// <param name="getLinkMetadata">Function to get the AtomLinkMetadata for the feed link.</param> internal void WriteFeedLink(ODataFeed feed, string relation, Uri href, Func <AtomFeedMetadata, AtomLinkMetadata> getLinkMetadata) { AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>(); AtomLinkMetadata mergedLink = ODataAtomWriterMetadataUtils.MergeLinkMetadata( getLinkMetadata(feedMetadata), relation, href, null, /*title*/ null /*mediaType*/); this.atomFeedMetadataSerializer.WriteAtomLink(mergedLink, null); }
/// <summary> /// Reads an atom:id element in a source element. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:icon) - the atom:icon element to read. /// Post-Condition: Any - the node after the atom:icon element which was read. /// </remarks> private void ReadIdElementAsSourceId(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomIdElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:id elements can be read by this method."); this.VerifyNotPreviouslyDefined(atomFeedMetadata.SourceId); string elementValue = this.XmlReader.ReadElementValue(); atomFeedMetadata.SourceId = UriUtils.CreateUriAsEntryOrFeedId(elementValue, UriKind.Absolute); }
public static AtomFeedMetadata Atom(this ODataFeed feed) { ExceptionUtils.CheckArgumentNotNull(feed, "feed"); AtomFeedMetadata feedMetadata = feed.GetAnnotation<AtomFeedMetadata>(); if (feedMetadata == null) { feedMetadata = new AtomFeedMetadata(); feed.SetAnnotation(feedMetadata); } return feedMetadata; }
public static AtomFeedMetadata Atom(this ODataFeed feed) { ExceptionUtils.CheckArgumentNotNull(feed, "feed"); AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>(); if (feedMetadata == null) { feedMetadata = new AtomFeedMetadata(); feed.SetAnnotation(feedMetadata); } return(feedMetadata); }
/// <summary> /// Reads the atom:source element in the entry content. /// </summary> /// <returns>The information in the source element as <see cref="AtomFeedMetadata"/>.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:source) - the atom:source element to read. /// Post-Condition: Any - the node after the atom:source which was read. /// </remarks> internal AtomFeedMetadata ReadAtomSourceInEntryContent() { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomSourceElementName, "Only atom:source element can be read by this method."); AtomFeedMetadata atomFeedMetadata = AtomMetadataReaderUtils.CreateNewAtomFeedMetadata(); if (this.XmlReader.IsEmptyElement) { // Advance reader past this element. this.XmlReader.Read(); return(atomFeedMetadata); } // Read the start tag of the source element. this.XmlReader.Read(); while (this.XmlReader.NodeType != XmlNodeType.EndElement) { if (this.XmlReader.NodeType != XmlNodeType.Element) { Debug.Assert(this.XmlReader.NodeType != XmlNodeType.EndElement, "EndElement should have been handled already."); // Skip everything but elements, including insignificant nodes, text nodes and CDATA nodes. this.XmlReader.Skip(); continue; } if (this.XmlReader.NamespaceEquals(this.AtomNamespace)) { // Use a feed metadata deserializer to process this element and modify atomFeedMetadata appropriately. this.SourceMetadataDeserializer.ReadAtomElementAsFeedMetadata(atomFeedMetadata); } else { // Skip all elements not in the ATOM namespace. this.XmlReader.Skip(); } } // Advance the reader past the end tag of the source element. this.XmlReader.Read(); return(atomFeedMetadata); }
/// <summary> /// Reads an atom:generator element in a feed. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:generator) - the atom:generator element to read. /// Post-Condition: Any - the node after the atom:generator element which was read. /// </remarks> private void ReadGeneratorElement(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.LocalName == AtomConstants.AtomGeneratorElementName && this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Only atom:generator elements can be read by this method."); this.VerifyNotPreviouslyDefined(atomFeedMetadata.Generator); AtomGeneratorMetadata generatorMetadata = new AtomGeneratorMetadata(); // Read the attributes. while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { switch (this.XmlReader.LocalName) { case AtomConstants.AtomGeneratorUriAttributeName: generatorMetadata.Uri = this.ProcessUriFromPayload(this.XmlReader.Value, this.XmlReader.XmlBaseUri); break; case AtomConstants.AtomGeneratorVersionAttributeName: generatorMetadata.Version = this.XmlReader.Value; break; default: // Ignore all other attributes. break; } } } // Read the value of the element as a string and store it as the name of the generator. this.XmlReader.MoveToElement(); if (this.XmlReader.IsEmptyElement) { // Move to the next node after this one. this.XmlReader.Skip(); } else { generatorMetadata.Name = this.XmlReader.ReadElementValue(); } atomFeedMetadata.Generator = generatorMetadata; }
/// <summary> /// Write the given feed metadata in atom format /// </summary> /// <param name="feed">The feed for which to write the meadata or null if it is the metadata of an atom:source element.</param> /// <param name="updatedTime">Value for the atom:updated element.</param> /// <param name="authorWritten">Set to true if the author element was written, false otherwise.</param> internal void WriteFeedMetadata(ODataFeed feed, string updatedTime, out bool authorWritten) { Debug.Assert(feed != null, "feed != null"); Debug.Assert(!string.IsNullOrEmpty(updatedTime), "!string.IsNullOrEmpty(updatedTime)"); #if DEBUG DateTimeOffset tempDateTimeOffset; Debug.Assert(DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset), "DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset)"); #endif AtomFeedMetadata feedMetadata = feed.GetAnnotation <AtomFeedMetadata>(); if (feedMetadata == null) { // create the required metadata elements with default values. // <atom:id>idValue</atom:id> Debug.Assert(feed.Id != null, "The feed Id should have been validated by now."); this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, UriUtils.UriToString(feed.Id)); // <atom:title></atom:title> this.WriteEmptyElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace); // <atom:updated>dateTimeOffset</atom:updated> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updatedTime); authorWritten = false; } else { this.atomFeedMetadataSerializer.WriteFeedMetadata(feedMetadata, feed, updatedTime, out authorWritten); } }
/// <summary> /// Reads an atom:category element in a feed. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to augment.</param> /// <remarks> /// Pre-Condition: XmlNodeType.Element (atom:category) - the atom:category element to read. /// Post-Condition: Any - the node after the atom:category which was read. /// </remarks> private void ReadCategoryElement(AtomFeedMetadata atomFeedMetadata) { this.AssertXmlCondition(XmlNodeType.Element); Debug.Assert( this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace && this.XmlReader.LocalName == AtomConstants.AtomCategoryElementName, "Only atom:category element can be read by this method."); AtomCategoryMetadata categoryMetadata = new AtomCategoryMetadata(); // Read the attributes while (this.XmlReader.MoveToNextAttribute()) { if (this.XmlReader.NamespaceEquals(this.EmptyNamespace)) { switch (this.XmlReader.LocalName) { case AtomConstants.AtomCategorySchemeAttributeName: categoryMetadata.Scheme = this.XmlReader.Value; break; case AtomConstants.AtomCategoryTermAttributeName: categoryMetadata.Term = this.XmlReader.Value; break; case AtomConstants.AtomCategoryLabelAttributeName: categoryMetadata.Label = this.XmlReader.Value; break; default: // Ignore all other attributes. break; } } } atomFeedMetadata.AddCategory(categoryMetadata); // Skip the rest of the category element. this.XmlReader.Skip(); }
/// <summary> /// Writes the feed element for the atom payload. /// </summary> /// <param name="expanded">Expanded properties for the result.</param> /// <param name="elements">Collection of entries in the feed element.</param> /// <param name="expectedType">ExpectedType of the elements in the collection.</param> /// <param name="title">Title of the feed element.</param> /// <param name="getRelativeUri">Callback to get the relative uri of the feed.</param> /// <param name="getAbsoluteUri">Callback to get the absolute uri of the feed.</param> /// <param name="topLevel">True if the feed is the top level feed, otherwise false for the inner expanded feed.</param> private void WriteFeedElements( IExpandedResult expanded, QueryResultInfo elements, ResourceType expectedType, string title, Func<Uri> getRelativeUri, Func<Uri> getAbsoluteUri, bool topLevel) { Debug.Assert(elements != null, "elements != null"); Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType"); Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)"); ODataFeed feed = new ODataFeed(); feed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName }); // Write the other elements for the feed this.PayloadMetadataPropertyManager.SetId(feed, () => getAbsoluteUri()); #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { // Create the atom feed metadata and set the self link and title value. AtomFeedMetadata feedMetadata = new AtomFeedMetadata(); feed.SetAnnotation(feedMetadata); feedMetadata.Title = new AtomTextConstruct { Text = title }; feedMetadata.SelfLink = new AtomLinkMetadata { Href = getRelativeUri(), Title = title }; } // support for $count // in ATOM we write it at the beginning (we always have) // in JSON for backward compatiblity reasons we write it at the end, so we must not fill it here. if (topLevel && this.RequestDescription.CountOption == RequestQueryCountOption.CountQuery) { feed.Count = this.RequestDescription.CountValue; } var feedArgs = elements.GetDataServiceODataWriterFeedArgs(feed, this.Service.OperationContext); this.dataServicesODataWriter.WriteStart(feedArgs); object lastObject = null; IExpandedResult lastExpandedSkipToken = null; while (elements.HasMoved) { object o = elements.Current; IExpandedResult skipToken = this.GetSkipToken(expanded); if (o != null) { IExpandedResult expandedO = o as IExpandedResult; if (expandedO != null) { expanded = expandedO; o = GetExpandedElement(expanded); skipToken = this.GetSkipToken(expanded); } this.WriteEntry(expanded, o, true /*resourceInstanceInFeed*/, expectedType); } elements.MoveNext(); lastObject = o; lastExpandedSkipToken = skipToken; } // After looping through the objects in the sequence, decide if we need to write the next // page link and if yes, write it by invoking the delegate if (this.NeedNextPageLink(elements)) { this.PayloadMetadataPropertyManager.SetNextPageLink( feed, this.AbsoluteServiceUri, this.GetNextLinkUri(lastObject, lastExpandedSkipToken, getAbsoluteUri())); } this.dataServicesODataWriter.WriteEnd(feedArgs); #if ASTORIA_FF_CALLBACKS this.Service.InternalOnWriteFeed(feed); #endif }
public void EntryMetadataWriterTest() { const string testPublished = "2010-10-20T20:10:00Z"; AtomTextConstruct testRights = new AtomTextConstruct { Text = "Copyright Data Fx team." }; AtomTextConstruct testSummary = new AtomTextConstruct { Text = "Test summary." }; AtomTextConstruct testTitle = new AtomTextConstruct { Text = "Test title" }; const string testUpdated = "2010-11-01T00:04:00Z"; const string testIcon = "http://odata.org/icon"; const string testSourceId = "http://odata.org/id/random"; const string testLogo = "http://odata.org/logo"; AtomTextConstruct testSubtitle = new AtomTextConstruct { Text = "Test subtitle." }; const string testAuthorName = "Test Author 1"; const string testAuthorEmail = "*****@*****.**"; const string testAuthorUri = "http://odata.org/authors/1"; var testAuthors = new AtomPersonMetadata[] { new AtomPersonMetadata() { Email = testAuthorEmail, Name = testAuthorName, Uri = new Uri(testAuthorUri) } }; var testAuthors2 = new AtomPersonMetadata[0]; const string testCategoryTerm = "Test category 1 term"; const string testCategoryLabel = "Test category 1 label"; const string testCategoryScheme = "http://odata.org/categories/1"; var testCategories = new AtomCategoryMetadata[] { new AtomCategoryMetadata() { Term = testCategoryTerm, Label = testCategoryLabel, Scheme = testCategoryScheme } }; const string testContributorName = "Test Contributor 1"; const string testContributorEmail = "*****@*****.**"; const string testContributorUri = "http://odata.org/contributors/1"; var testContributors = new AtomPersonMetadata[] { new AtomPersonMetadata() { Email = testContributorEmail, Name = testContributorName, Uri = new Uri(testContributorUri) } }; const string testGeneratorName = "Test generator"; const string testGeneratorUri = "http://odata.org/generator"; const string testGeneratorVersion = "3.0"; var testGenerator = new AtomGeneratorMetadata() { Name = testGeneratorName, Uri = new Uri(testGeneratorUri), Version = testGeneratorVersion }; const string testLinkRelation = "http://odata.org/links/1"; const string testLinkTitle = "Test link 1"; const string testLinkHref = "http://odata.org/links/1"; const string testLinkHrefLang = "de-AT"; int? testLinkLength = 999; const string testLinkMediaType = "image/png"; var testLinks = new AtomLinkMetadata[] { new AtomLinkMetadata() { Relation = testLinkRelation, Title = testLinkTitle, Href = new Uri(testLinkHref), HrefLang = testLinkHrefLang, Length = testLinkLength, MediaType = testLinkMediaType } }; AtomFeedMetadata testSource = new AtomFeedMetadata() { Authors = testAuthors, Categories = testCategories, Contributors = testContributors, Generator = testGenerator, Icon = new Uri(testIcon), SourceId = new Uri(testSourceId), Links = testLinks, Logo = new Uri(testLogo), Rights = testRights, Subtitle = testSubtitle, Title = testTitle, Updated = DateTimeOffset.Parse(testUpdated) }; Func<string, Func<XElement, XElement>> fragmentExtractor = (localName) => (e) => e.Element(TestAtomConstants.AtomXNamespace + localName); // TODO, ckerer: specify an Id via metadata if the entry does not specify one; we first have to decide what rules // we want to apply to merging of metadata and ODataLib OM data. var testCases = new[] { new { // specify an author via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors), Xml = string.Join( "$(NL)", @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @" <name>" + testAuthorName + @"</name>", @" <uri>" + testAuthorUri + @"</uri>", @" <email>" + testAuthorEmail + @"</email>", @"</author>"), Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName) }, new { // specify an empty author array via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = testAuthors2), Xml = string.Join( "$(NL)", @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @" <name />", @"</author>"), Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName) }, new { // specify no authors via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Authors = null), Xml = string.Join( "$(NL)", @"<author xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @"$(Indent)<name />", @"</author>"), Extractor = fragmentExtractor(TestAtomConstants.AtomAuthorElementName) }, new { // specify a category via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Categories = testCategories), Xml = @"<category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" xmlns=""" + TestAtomConstants.AtomNamespace + @""" />", Extractor = fragmentExtractor(TestAtomConstants.AtomCategoryElementName) }, new { // specify a contributor via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Contributors = testContributors), Xml = string.Join( "$(NL)", @"<contributor xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @" <name>" + testContributorName + @"</name>", @" <uri>" + testContributorUri + @"</uri>", @" <email>" + testContributorEmail + @"</email>", @"</contributor>"), Extractor = fragmentExtractor(TestAtomConstants.AtomContributorElementName) }, new { // specify a link via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Links = testLinks), Xml = @"<link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @""" xmlns=""" + TestAtomConstants.AtomNamespace + @"""/>", Extractor = new Func<XElement, XElement>( (e) => e.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName) .Where(l => l.Attribute(TestAtomConstants.AtomLinkRelationAttributeName).Value != TestAtomConstants.AtomSelfRelationAttributeValue) .Single()) }, new { // specify a published date via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Published = DateTimeOffset.Parse(testPublished)), Xml = @"<published xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testPublished + @"</published>", Extractor = fragmentExtractor(TestAtomConstants.AtomPublishedElementName) }, new { // specify rights via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Rights = testRights), Xml = @"<rights type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testRights.Text + @"</rights>", Extractor = fragmentExtractor(TestAtomConstants.AtomRightsElementName) }, new { // specify a source via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = testSource), Xml = string.Join( "$(NL)", @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @" <id>" + testSourceId + "</id>", @" <title type=""text"">" + testTitle.Text + @"</title>", @" <subtitle type=""text"">" + testSubtitle.Text + @"</subtitle>", @" <updated>" + testUpdated + @"</updated>", @" <link rel=""" + testLinkRelation + @""" type = """ + testLinkMediaType + @""" title=""" + testLinkTitle + @""" href=""" + testLinkHref + @""" hreflang=""" + testLinkHrefLang + @""" length=""" + testLinkLength + @"""/>", @" <category term=""" + testCategoryTerm + @""" scheme=""" + testCategoryScheme + @""" label=""" + testCategoryLabel + @""" />", @" <logo>" + testLogo + @"</logo>", @" <rights type=""text"">" + testRights.Text + @"</rights>", @" <contributor>", @" <name>" + testContributorName + @"</name>", @" <uri>" + testContributorUri + @"</uri>", @" <email>" + testContributorEmail + @"</email>", @" </contributor>", @" <generator uri=""" + testGeneratorUri + @""" version=""" +testGeneratorVersion + @""">" + testGeneratorName + @"</generator>", @" <icon>" + testIcon + @"</icon>", @" <author>", @" <name>" + testAuthorName + @"</name>", @" <uri>" + testAuthorUri + @"</uri>", @" <email>" + testAuthorEmail + @"</email>", @" </author>", @"</source>"), Extractor = fragmentExtractor(TestAtomConstants.AtomSourceElementName) }, new { // specify default feed metadata as source CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Source = new AtomFeedMetadata()), Xml = string.Join( "$(NL)", @"<source xmlns=""" + TestAtomConstants.AtomNamespace + @""">", @" <id />", @" <title />", @" <updated />", @"</source>"), Extractor = new Func<XElement, XElement>(result => { var source = fragmentExtractor(TestAtomConstants.AtomSourceElementName)(result); // Remove the value of updates as it can't be reliably predicted source.Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomUpdatedElementName).Nodes().Remove(); return source; }) }, new { // specify a summary via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Summary = testSummary), Xml = @"<summary type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testSummary.Text + @"</summary>", Extractor = fragmentExtractor(TestAtomConstants.AtomSummaryElementName) }, new { // specify a title via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Title = testTitle), Xml = @"<title type=""text"" xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testTitle.Text + @"</title>", Extractor = fragmentExtractor(TestAtomConstants.AtomTitleElementName) }, new { // specify an updated date via metadata CustomizeMetadata = new Action<AtomEntryMetadata>(metadata => metadata.Updated = DateTimeOffset.Parse(testUpdated)), Xml = @"<updated xmlns=""" + TestAtomConstants.AtomNamespace + @""">" + testUpdated + @"</updated>", Extractor = fragmentExtractor(TestAtomConstants.AtomUpdatedElementName) }, }; // Convert test cases to test descriptions IEnumerable<Func<ODataEntry>> entryCreators = new Func<ODataEntry>[] { () => ObjectModelUtils.CreateDefaultEntry(), () => ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(), }; var testDescriptors = testCases.SelectMany(testCase => entryCreators.Select(entryCreator => { ODataEntry entry = entryCreator(); AtomEntryMetadata metadata = entry.Atom(); this.Assert.IsNotNull(metadata, "Expected default entry metadata on a default entry."); testCase.CustomizeMetadata(metadata); return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.Xml, FragmentExtractor = testCase.Extractor }); })); this.CombinatorialEngineProvider.RunCombinations( testDescriptors.PayloadCases(WriterPayloads.EntryPayloads), this.WriterTestConfigurationProvider.AtomFormatConfigurations, (testDescriptor, testConfiguration) => { testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger); }); }
/// <summary> /// Converts the Object Model representation of Atom metadata for feeds into appropriate PayloadElement annotations /// </summary> /// <param name="feedMetadata">the Atom feed metadata, in Object Model representation, to convert</param> /// <param name="feed">the EntitySetInstance to annotate</param> private static void ConvertAtomFeedMetadata(AtomFeedMetadata feedMetadata, EntitySetInstance feed) { ExceptionUtilities.CheckArgumentNotNull(feedMetadata, "feedMetadata"); ExceptionUtilities.CheckArgumentNotNull(feed, "feed"); foreach (var author in feedMetadata.Authors) { feed.AtomAuthor(author.Name, author.Uri == null ? null : author.Uri.OriginalString, author.Email); } foreach (var category in feedMetadata.Categories) { feed.AtomCategory(category.Term, category.Scheme, category.Label); } foreach (var contributor in feedMetadata.Contributors) { feed.AtomContributor(contributor.Name, contributor.Uri == null ? null : contributor.Uri.OriginalString, contributor.Email); } if (feedMetadata.Generator != null) { feed.AtomGenerator(feedMetadata.Generator.Name, feedMetadata.Generator.Uri == null ? null : feedMetadata.Generator.Uri.OriginalString, feedMetadata.Generator.Version); } if (feedMetadata.Icon != null) { feed.AtomIcon(feedMetadata.Icon.OriginalString); } foreach (var link in feedMetadata.Links) { string linkLength = link.Length.HasValue ? link.Length.Value.ToString() : null; feed.AtomLink(link.Href == null ? null : link.Href.OriginalString, link.Relation, link.MediaType, link.HrefLang, link.Title, linkLength); } if (feedMetadata.Logo != null) { feed.AtomLogo(feedMetadata.Logo.OriginalString); } if (feedMetadata.Rights != null) { feed.AtomRights(feedMetadata.Rights.Text, ToString(feedMetadata.Rights.Kind)); } if (feedMetadata.SelfLink != null) { AtomLinkMetadata selfLink = feedMetadata.SelfLink; ExceptionUtilities.Assert(selfLink.Relation == TestAtomConstants.AtomSelfRelationAttributeValue, "The self link ATOM metadata must have the rel set to 'self'."); string selfLinkLength = selfLink.Length.HasValue ? selfLink.Length.Value.ToString() : null; feed.AtomSelfLink(selfLink.Href == null ? null : selfLink.Href.OriginalString, selfLink.MediaType, selfLink.HrefLang, selfLink.Title, selfLinkLength); } if (feedMetadata.NextPageLink != null) { AtomLinkMetadata nextPageLink = feedMetadata.NextPageLink; ExceptionUtilities.Assert(nextPageLink.Relation == TestAtomConstants.AtomNextRelationAttributeValue, "The next page link ATOM metadata must have the rel set to 'next'."); string nextPageLinkLength = nextPageLink.Length.HasValue ? nextPageLink.Length.Value.ToString() : null; feed.AtomNextPageLink(nextPageLink.Href == null ? null : nextPageLink.Href.OriginalString, nextPageLink.MediaType, nextPageLink.HrefLang, nextPageLink.Title, nextPageLinkLength); } if (feedMetadata.SourceId != null) { // This should only occur when the metadata comes from the source element of an entry // and we are annotating a temporary feed instance feed.AtomId(UriUtils.UriToString(feedMetadata.SourceId)); } if (feedMetadata.Subtitle != null) { feed.AtomSubtitle(feedMetadata.Subtitle.Text, ToString(feedMetadata.Subtitle.Kind)); } if (feedMetadata.Title != null) { feed.AtomTitle(feedMetadata.Title.Text, ToString(feedMetadata.Title.Kind)); } if (feedMetadata.Updated.HasValue) { feed.AtomUpdated(ToString(feedMetadata.Updated)); } }
/// <summary> /// Visits an ATOM feed metadata. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to visit.</param> protected virtual void VisitAtomFeedMetadata(AtomFeedMetadata atomFeedMetadata) { IEnumerable<AtomPersonMetadata> authors = atomFeedMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { this.VisitAtomMetadata(author); } } IEnumerable<AtomCategoryMetadata> categories = atomFeedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { this.VisitAtomMetadata(category); } } IEnumerable<AtomPersonMetadata> contributors = atomFeedMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { this.VisitAtomMetadata(contributor); } } this.VisitAtomMetadata(atomFeedMetadata.Generator); IEnumerable<AtomLinkMetadata> links = atomFeedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { this.VisitAtomMetadata(link); } } this.VisitAtomMetadata(atomFeedMetadata.Rights); this.VisitAtomMetadata(atomFeedMetadata.SelfLink); this.VisitAtomMetadata(atomFeedMetadata.Subtitle); this.VisitAtomMetadata(atomFeedMetadata.Title); }
/// <summary> /// Creates a an ODataFeed instance with default values for 'Id' and 'Updated' /// that can be used and modified in tests. /// </summary> /// <returns>The created ODataFeed instance.</returns> public static ODataFeed CreateDefaultFeedWithAtomMetadata() { ODataFeed feed = new ODataFeed() { Id = DefaultFeedId, SerializationInfo = MySerializationInfo }; AtomFeedMetadata metadata = new AtomFeedMetadata() { Updated = DateTimeOffset.Parse(DefaultFeedUpdated) }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return feed; }
internal void WriteEntryMetadata(AtomEntryMetadata entryMetadata, string updatedTime) { Debug.Assert(!string.IsNullOrEmpty(updatedTime), "!string.IsNullOrEmpty(updatedTime)"); #if DEBUG DateTimeOffset tempDateTimeOffset; Debug.Assert(DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset), "DateTimeOffset.TryParse(updatedTime, out tempDateTimeOffset)"); #endif // TODO: implement the rule around authors (an entry has to have an author directly or in the <entry:source> unless the feed has an author). // currently we make all entries have an author. if (entryMetadata == null) { // write all required metadata elements with default content // <atom:title></atom:title> this.WriteEmptyElement( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace); // <atom:updated>dateTimeOffset</atom:updated> // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload. this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updatedTime); this.WriteEmptyAuthor(); } else { // <atom:title>text</atom:title> // NOTE: writes an empty element even if no title was specified since the title is required this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, entryMetadata.Title); AtomTextConstruct summary = entryMetadata.Summary; if (summary != null) { // <atom:summary>text</atom:summary> this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSummaryElementName, AtomConstants.AtomNamespace, summary); } string published = entryMetadata.Published.HasValue ? ODataAtomConvert.ToAtomString(entryMetadata.Published.Value) : null; if (published != null) { // <atom:published>dateTimeOffset</atom:published> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomPublishedElementName, AtomConstants.AtomNamespace, published); } // <atom:updated>date</atom:updated> string updated = entryMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(entryMetadata.Updated.Value) : null; // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload. updated = updated ?? updatedTime; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updated); bool wroteAuthor = false; IEnumerable <AtomPersonMetadata> authors = entryMetadata.Authors; if (authors != null) { foreach (AtomPersonMetadata author in authors) { if (author == null) { throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_AuthorMetadataMustNotContainNull); } // <atom:author>author data</atom:author> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(author); this.XmlWriter.WriteEndElement(); wroteAuthor = true; } } if (!wroteAuthor) { // write empty authors since they are required this.WriteEmptyAuthor(); } IEnumerable <AtomPersonMetadata> contributors = entryMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { if (contributor == null) { throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_ContributorMetadataMustNotContainNull); } // <atom:contributor>contributor data</atom:contributor> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(contributor); this.XmlWriter.WriteEndElement(); } } IEnumerable <AtomLinkMetadata> links = entryMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { if (link == null) { throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_LinkMetadataMustNotContainNull); } // <atom:link>...</atom:link> this.WriteAtomLink(link, null /*edit-link-etag*/); } } IEnumerable <AtomCategoryMetadata> categories = entryMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { if (category == null) { throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_CategoryMetadataMustNotContainNull); } // <atom:category term="..." scheme="..." label="..."></atom:category> this.WriteCategory(category); } } if (entryMetadata.Rights != null) { // <atom:rights>rights</atom:rights> this.WriteTextConstruct( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, entryMetadata.Rights); } AtomFeedMetadata source = entryMetadata.Source; if (source != null) { // <atom:source> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSourceElementName, AtomConstants.AtomNamespace); bool authorWritten; this.SourceMetadataSerializer.WriteFeedMetadata(source, null /* feed */, updatedTime, out authorWritten); // </atom:source> this.XmlWriter.WriteEndElement(); } } }
internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten) { Debug.Assert(feedMetadata != null, "Feed metadata must not be null!"); // <atom:id>text</atom:id> // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself; // if used in the context of an <atom:source> element it is stored in metadata Uri id = feed == null ? feedMetadata.SourceId : feed.Id; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, id == null ? null : UriUtils.UriToString(id)); // <atom:title>text</atom:title> // NOTE: write an empty element if no title is specified since the element is required this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title); if (feedMetadata.Subtitle != null) { // <atom:subtitle>text</atom:subtitle> this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload. string updated = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updated); AtomLinkMetadata selfLinkMetadata = feedMetadata.SelfLink; if (selfLinkMetadata != null) { AtomLinkMetadata mergedSelfLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( selfLinkMetadata, AtomConstants.AtomSelfRelationAttributeValue, null /* href */, null /* title */, null /* media type */); this.WriteAtomLink(mergedSelfLinkMetadata, null /*etag*/); } IEnumerable<AtomLinkMetadata> links = feedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { // DeltaLink is written from ODataFeed, so it shouldn't be written again from AtomFeedMetadata. if (link.Relation != AtomConstants.AtomDeltaRelationAttributeValue) { // <atom:link>...</atom:link> this.WriteAtomLink(link, null /* etag */); } } } IEnumerable<AtomCategoryMetadata> categories = feedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { // <atom:category term="..." scheme="..." label="..."></atom:category> this.WriteCategory(category); } } Uri logo = feedMetadata.Logo; if (logo != null) { // <atom:logo>Uri</atom:logo> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLogoElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(logo)); } if (feedMetadata.Rights != null) { // <atom:rights>rights</atom:rights> this.WriteTextConstruct( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, feedMetadata.Rights); } IEnumerable<AtomPersonMetadata> contributors = feedMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { // <atom:contributor>contributor data</atom:contributor> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(contributor); this.XmlWriter.WriteEndElement(); } } AtomGeneratorMetadata generator = feedMetadata.Generator; if (generator != null) { // <atom:generator uri="..." version="...">name</atom:generator> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace); if (generator.Uri != null) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, this.UriToUrlAttributeValue(generator.Uri)); } if (!string.IsNullOrEmpty(generator.Version)) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version); } ODataAtomWriterUtils.WriteString(this.XmlWriter, generator.Name); this.XmlWriter.WriteEndElement(); } Uri icon = feedMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(icon)); } IEnumerable<AtomPersonMetadata> authors = feedMetadata.Authors; authorWritten = false; if (authors != null) { foreach (AtomPersonMetadata author in authors) { // <atom:author>author data</atom:author> authorWritten = true; this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(author); this.XmlWriter.WriteEndElement(); } } }
internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten) { Debug.Assert(feedMetadata != null, "Feed metadata must not be null!"); // <atom:id>text</atom:id> // NOTE: this is the Id of the feed. For a regular feed this is stored on the feed itself; // if used in the context of an <atom:source> element it is stored in metadata Uri id = feed == null ? feedMetadata.SourceId : feed.Id; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIdElementName, AtomConstants.AtomNamespace, id == null ? null : UriUtils.UriToString(id)); // <atom:title>text</atom:title> // NOTE: write an empty element if no title is specified since the element is required this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, feedMetadata.Title); if (feedMetadata.Subtitle != null) { // <atom:subtitle>text</atom:subtitle> this.WriteTextConstruct(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomSubtitleElementName, AtomConstants.AtomNamespace, feedMetadata.Subtitle); } // <atom:updated>date</atom:updated> // NOTE: the <updated> element is required and if not specified we use a single 'default/current' date/time for the whole payload. string updated = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime; this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomUpdatedElementName, AtomConstants.AtomNamespace, updated); AtomLinkMetadata selfLinkMetadata = feedMetadata.SelfLink; if (selfLinkMetadata != null) { AtomLinkMetadata mergedSelfLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( selfLinkMetadata, AtomConstants.AtomSelfRelationAttributeValue, null /* href */, null /* title */, null /* media type */); this.WriteAtomLink(mergedSelfLinkMetadata, null /*etag*/); } IEnumerable <AtomLinkMetadata> links = feedMetadata.Links; if (links != null) { foreach (AtomLinkMetadata link in links) { // DeltaLink is written from ODataFeed, so it shouldn't be written again from AtomFeedMetadata. if (link.Relation != AtomConstants.AtomDeltaRelationAttributeValue) { // <atom:link>...</atom:link> this.WriteAtomLink(link, null /* etag */); } } } IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories; if (categories != null) { foreach (AtomCategoryMetadata category in categories) { // <atom:category term="..." scheme="..." label="..."></atom:category> this.WriteCategory(category); } } Uri logo = feedMetadata.Logo; if (logo != null) { // <atom:logo>Uri</atom:logo> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomLogoElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(logo)); } if (feedMetadata.Rights != null) { // <atom:rights>rights</atom:rights> this.WriteTextConstruct( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomRightsElementName, AtomConstants.AtomNamespace, feedMetadata.Rights); } IEnumerable <AtomPersonMetadata> contributors = feedMetadata.Contributors; if (contributors != null) { foreach (AtomPersonMetadata contributor in contributors) { // <atom:contributor>contributor data</atom:contributor> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomContributorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(contributor); this.XmlWriter.WriteEndElement(); } } AtomGeneratorMetadata generator = feedMetadata.Generator; if (generator != null) { // <atom:generator uri="..." version="...">name</atom:generator> this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomGeneratorElementName, AtomConstants.AtomNamespace); if (generator.Uri != null) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorUriAttributeName, this.UriToUrlAttributeValue(generator.Uri)); } if (!string.IsNullOrEmpty(generator.Version)) { this.XmlWriter.WriteAttributeString(AtomConstants.AtomGeneratorVersionAttributeName, generator.Version); } ODataAtomWriterUtils.WriteString(this.XmlWriter, generator.Name); this.XmlWriter.WriteEndElement(); } Uri icon = feedMetadata.Icon; if (icon != null) { // <atom:icon>Uri</atom:icon> this.WriteElementWithTextContent( AtomConstants.AtomNamespacePrefix, AtomConstants.AtomIconElementName, AtomConstants.AtomNamespace, this.UriToUrlAttributeValue(icon)); } IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors; authorWritten = false; if (authors != null) { foreach (AtomPersonMetadata author in authors) { // <atom:author>author data</atom:author> authorWritten = true; this.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomAuthorElementName, AtomConstants.AtomNamespace); this.WritePersonMetadata(author); this.XmlWriter.WriteEndElement(); } } }
private static AtomFeedMetadata CreateFeedMetadata(IEnumerable<XmlTreeAnnotation> children, ODataFeed feed) { AtomFeedMetadata metadata = null; foreach (XmlTreeAnnotation epmTree in children) { if (epmTree.NamespaceName == TestAtomConstants.AtomNamespace) { if (metadata == null) { metadata = new AtomFeedMetadata(); } string localName = epmTree.LocalName; if (localName == TestAtomConstants.AtomAuthorElementName) { AtomPersonMetadata author = CreateAuthorMetadata(epmTree.Children); List<AtomPersonMetadata> authors; if (metadata.Authors == null) { authors = new List<AtomPersonMetadata>(); metadata.Authors = authors; } else { authors = (List<AtomPersonMetadata>)metadata.Authors; } authors.Add(author); } else if (localName == TestAtomConstants.AtomCategoryElementName) { AtomCategoryMetadata category = CreateCategoryMetadata(epmTree.Children); List<AtomCategoryMetadata> categories; if (metadata.Categories == null) { categories = new List<AtomCategoryMetadata>(); metadata.Categories = categories; } else { categories = (List<AtomCategoryMetadata>)metadata.Categories; } categories.Add(category); } else if (localName == TestAtomConstants.AtomContributorElementName) { AtomPersonMetadata contributor = CreateAuthorMetadata(epmTree.Children); List<AtomPersonMetadata> contributors; if (metadata.Contributors == null) { contributors = new List<AtomPersonMetadata>(); metadata.Contributors = contributors; } else { contributors = (List<AtomPersonMetadata>)metadata.Contributors; } contributors.Add(contributor); } else if (localName == TestAtomConstants.AtomGeneratorElementName) { metadata.Generator = CreateGeneratorMetadata(epmTree); } else if (localName == TestAtomConstants.AtomIconElementName) { metadata.Icon = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue); } else if (localName == TestAtomConstants.AtomIdElementName) { Debug.Assert(!epmTree.IsAttribute); Uri id = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue, UriKind.Absolute); if (feed == null) { // we are creating the metadata for an entry's 'source' metadata; // we don't have a feed to store the Id on so it has to go into metadata metadata.SourceId = id; } else { feed.Id = id; } } else if (localName == TestAtomConstants.AtomLinkElementName) { AtomLinkMetadata link = CreateLinkMetadata(epmTree.Children); List<AtomLinkMetadata> links; if (metadata.Links == null) { links = new List<AtomLinkMetadata>(); metadata.Links = links; } else { links = (List<AtomLinkMetadata>)metadata.Contributors; } links.Add(link); } else if (localName == TestAtomConstants.AtomLogoElementName) { metadata.Logo = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue); } else if (localName == TestAtomConstants.AtomRightsElementName) { AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Rights = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomSubtitleElementName) { AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Subtitle = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomTitleElementName) { AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children); metadata.Title = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue }; } else if (localName == TestAtomConstants.AtomUpdatedElementName) { Debug.Assert(!epmTree.IsAttribute); metadata.Updated = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue); } else { throw new NotSupportedException("Unsupported atom metadata found!"); } } } return metadata; }
public void PayloadOrderTest() { ODataFeedAndEntrySerializationInfo info = new ODataFeedAndEntrySerializationInfo() { NavigationSourceEntityTypeName = "Null", NavigationSourceName = "MySet", ExpectedTypeName = "Null" }; AtomFeedMetadata defaultAtomFeedMetadata = new AtomFeedMetadata() { Authors = new[] { new AtomPersonMetadata { Name = "Bart" } }, Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }; IEnumerable<PayloadOrderTestCase> testCases = new[] { new PayloadOrderTestCase { DebugDescription = "Just ID", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info } .WithAnnotation(defaultAtomFeedMetadata), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <author>", " <name>Bart</name>", " </author>{0}", "</feed>"), }, new PayloadOrderTestCase { DebugDescription = "Just ID, and another ID at the end, the one from the starts is used", Feed = new ODataFeed() { SerializationInfo = info } .WithAnnotation(new WriteFeedCallbacksAnnotation { BeforeWriteStartCallback = (feed) => { feed.Id = new Uri("http://MyFeedId"); }, BeforeWriteEndCallback = (feed) => { feed.Id = new Uri("http://AnotherFeedId"); } }) .WithAnnotation(defaultAtomFeedMetadata), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <author>", " <name>Bart</name>", " </author>{0}", "</feed>"), }, new PayloadOrderTestCase { DebugDescription = "ID and no author metadata and no entities, the default author should be written.", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info } .WithAnnotation(new AtomFeedMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>{0}", " <author>", " <name />", " </author>", "</feed>"), NoEntitiesOnly = true }, new PayloadOrderTestCase { DebugDescription = "ID and no author metadata but with entities, no author is written.", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info } .WithAnnotation(new AtomFeedMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>{0}", "</feed>"), WithEntitiesOnly = true }, new PayloadOrderTestCase { DebugDescription = "ID, count at the end - the count is ignored", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), SerializationInfo = info } .WithAnnotation(new WriteFeedCallbacksAnnotation { BeforeWriteStartCallback = (feed) => { feed.Count = null; }, BeforeWriteEndCallback = (feed) => { feed.Count = 42; } }) .WithAnnotation(defaultAtomFeedMetadata), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <author>", " <name>Bart</name>", " </author>{0}", "</feed>"), }, new PayloadOrderTestCase { DebugDescription = "ID, count and next link", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), Count = 42, NextPageLink = new Uri("http://odata.org/nextlink"), SerializationInfo = info } .WithAnnotation(defaultAtomFeedMetadata), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <count xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">42</count>", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <author>", " <name>Bart</name>", " </author>{0}", " <link rel=\"next\" href=\"http://odata.org/nextlink\" />", "</feed>"), }, new PayloadOrderTestCase { DebugDescription = "ID, count and next link and some ATOM metadata, metadata goes before entries but author is the last of them.", Feed = new ODataFeed() { Id = new Uri("http://MyFeedId"), Count = 42, NextPageLink = new Uri("http://odata.org/nextlink"), SerializationInfo = info } .WithAnnotation(new AtomFeedMetadata() { Authors = new[] { new AtomPersonMetadata { Name = "Bart" } }, Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime, Logo = new Uri("http://odata.org/logo") }), Xml = string.Join( "$(NL)", "<feed xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <count xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\">42</count>", " <id>http://MyFeedId</id>", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <logo>http://odata.org/logo</logo>", " <author>", " <name>Bart</name>", " </author>{0}", " <link rel=\"next\" href=\"http://odata.org/nextlink\" />", "</feed>"), }, }; IEnumerable<PayloadWriterTestDescriptor<ODataItem>> testDescriptors = testCases.Where(testCase => !testCase.WithEntitiesOnly).Select(testCase => new PayloadWriterTestDescriptor<ODataItem>( this.Settings, new ODataItem[] { testCase.Feed }, tc => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = string.Format(CultureInfo.InvariantCulture, testCase.Xml, string.Empty), FragmentExtractor = (element) => element }) { DebugDescription = testCase.DebugDescription, }); testDescriptors = testDescriptors.Concat(testCases.Where(testCase => !testCase.NoEntitiesOnly).Select(testCase => new PayloadWriterTestDescriptor<ODataItem>( this.Settings, new ODataItem[] { testCase.Feed, new ODataEntry { } .WithAnnotation(new AtomEntryMetadata { Updated = ObjectModelUtils.DefaultEntryUpdatedDateTime }), }, tc => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = string.Format(CultureInfo.InvariantCulture, testCase.Xml, string.Join( "$(NL)", "<entry xmlns=\"" + TestAtomConstants.AtomNamespace + "\">", " <id />", " <title />", " <updated>2010-10-12T17:13:00Z</updated>", " <author>", " <name />", " </author>", " <content type=\"application/xml\" />", "</entry>")), FragmentExtractor = (element) => element }) { DebugDescription = testCase.DebugDescription + "- with entry", })); this.CombinatorialEngineProvider.RunCombinations( testDescriptors, this.WriterTestConfigurationProvider.AtomFormatConfigurationsWithIndent .Where(tc => !tc.IsRequest), (testDescriptor, testConfiguration) => { testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger); }); }
/// <summary> /// Visits an ATOM feed metadata. /// </summary> /// <param name="atomFeedMetadata">The feed metadata to visit.</param> protected override void VisitAtomFeedMetadata(AtomFeedMetadata atomFeedMetadata) { this.ValidateUri(atomFeedMetadata.Icon); this.ValidateUri(atomFeedMetadata.Logo); base.VisitAtomFeedMetadata(atomFeedMetadata); }
public void BaseUriErrorTest() { Uri baseUri = new Uri("http://odata.org"); Uri testUri = new Uri("http://odata.org/relative"); IEnumerable<Func<Uri, BaseUriErrorTestCase>> testCaseFuncs = new Func<Uri, BaseUriErrorTestCase>[] { relativeUri => new BaseUriErrorTestCase { // next page link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); feed.NextPageLink = relativeUri; return new [] { feed }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // entry read link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); entry.ReadLink = relativeUri; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // entry edit link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); entry.EditLink = relativeUri; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // media resource (default stream) read link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataStreamReferenceValue mediaResource = new ODataStreamReferenceValue(); mediaResource.ContentType = "image/jpg"; mediaResource.ReadLink = relativeUri; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); entry.MediaResource = mediaResource; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // media resource (default stream) edit link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataStreamReferenceValue mediaResource = new ODataStreamReferenceValue(); mediaResource.ContentType = "image/jpg"; // required mediaResource.ReadLink = testUri; // required mediaResource.EditLink = relativeUri; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); entry.MediaResource = mediaResource; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // link Url ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataNavigationLink link = ObjectModelUtils.CreateDefaultCollectionLink(); link.Url = relativeUri; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); return new ODataItem[] { entry, link }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // association link Url ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataNavigationLink link = ObjectModelUtils.CreateDefaultSingletonLink(); link.AssociationLinkUrl = relativeUri; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); return new ODataItem[] { entry, link }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // named stream read link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataStreamReferenceValue namedStream = new ODataStreamReferenceValue() { ContentType = "image/jpg", ReadLink = relativeUri, }; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); ODataProperty property = new ODataProperty() { Name = "NamedStream", Value = namedStream }; entry.Properties = new[] { property }; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // named stream edit link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataStreamReferenceValue namedStream = new ODataStreamReferenceValue() { ContentType = "image/jpg", ReadLink = testUri, EditLink = relativeUri }; ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); ODataProperty property = new ODataProperty() { Name = "NamedStream", Value = namedStream }; entry.Properties = new[] { property }; return new [] { entry }; }), Formats = new [] { ODataFormat.Atom, ODataFormat.Json } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed generator Uri ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Generator = new AtomGeneratorMetadata() { Uri = relativeUri } }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed logo ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Logo = relativeUri }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed icon ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Icon = relativeUri }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed author ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Authors = new [] { new AtomPersonMetadata() { Uri = relativeUri } } }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed contributor ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Contributors = new [] { new AtomPersonMetadata() { Uri = relativeUri } } }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: feed link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataFeed feed = ObjectModelUtils.CreateDefaultFeed(); AtomFeedMetadata metadata = new AtomFeedMetadata() { Links = new [] { new AtomLinkMetadata() { Href = relativeUri } } }; feed.SetAnnotation<AtomFeedMetadata>(metadata); return new [] { feed }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: entry author ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); AtomEntryMetadata metadata = new AtomEntryMetadata() { Authors = new [] { new AtomPersonMetadata() { Uri = relativeUri } } }; entry.SetAnnotation<AtomEntryMetadata>(metadata); return new [] { entry }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: entry contributor ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); AtomEntryMetadata metadata = new AtomEntryMetadata() { Contributors = new [] { new AtomPersonMetadata() { Uri = relativeUri } } }; entry.SetAnnotation<AtomEntryMetadata>(metadata); return new [] { entry }; }), Formats = new [] { ODataFormat.Atom } }, relativeUri => new BaseUriErrorTestCase { // Atom metadata: entry link ItemFunc = new Func<IEnumerable<ODataItem>>(() => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntry(); AtomEntryMetadata metadata = new AtomEntryMetadata() { Links = new [] { new AtomLinkMetadata() { Href = relativeUri } } }; entry.SetAnnotation<AtomEntryMetadata>(metadata); return new [] { entry }; }), Formats = new [] { ODataFormat.Atom } }, }; // ToDo: Fix places where we've lost JsonVerbose coverage to add JsonLight Uri testRelativeUri = baseUri.MakeRelativeUri(testUri); Uri invalidRelativeUri = new Uri("../invalid/relative/uri", UriKind.Relative); this.CombinatorialEngineProvider.RunCombinations( testCaseFuncs, this.WriterTestConfigurationProvider.ExplicitFormatConfigurations.Where(c => c.IsRequest == false && c.Format == ODataFormat.Atom), new Uri[] { testRelativeUri, invalidRelativeUri }, new bool[] { false, true }, (testCaseFunc, testConfiguration, uri, implementUrlResolver) => { var testCase = testCaseFunc(uri); var testDescriptor = new { Descriptor = new PayloadWriterTestDescriptor<ODataItem>( this.Settings, testCase.ItemFunc(), testConfig => new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = ODataExpectedExceptions.ODataException("ODataWriter_RelativeUriUsedWithoutBaseUriSpecified", uri.OriginalString) }), Formats = testCase.Formats }; if (testDescriptor.Formats.Contains(testConfiguration.Format)) { PayloadWriterTestDescriptor<ODataItem> payloadTestDescriptor = testDescriptor.Descriptor; TestUrlResolver urlResolver = null; if (implementUrlResolver) { payloadTestDescriptor = new PayloadWriterTestDescriptor<ODataItem>(payloadTestDescriptor); urlResolver = new TestUrlResolver(); payloadTestDescriptor.UrlResolver = urlResolver; } testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); TestWriterUtils.WriteAndVerifyODataPayload(payloadTestDescriptor, testConfiguration, this.Assert, this.Logger); if (implementUrlResolver) { this.Assert.AreEqual(1, urlResolver.Calls.Where(call => call.Value.OriginalString == uri.OriginalString).Count(), "The resolver should be called exactly once for each URL."); } } }); }