/// <summary> /// Writes a stream property to the ATOM payload /// </summary> /// <param name="streamProperty">The stream property to create the payload for.</param> /// <param name="owningType">The <see cref="IEdmEntityType"/> instance for which the stream property defined on.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param> internal void WriteStreamProperty( ODataProperty streamProperty, IEdmEntityType owningType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties) { Debug.Assert(streamProperty != null, "Stream property must not be null."); Debug.Assert(streamProperty.Value != null, "The media resource of the stream property must not be null."); WriterValidationUtils.ValidatePropertyNotNull(streamProperty); string propertyName = streamProperty.Name; if (projectedProperties.ShouldSkipProperty(propertyName)) { return; } WriterValidationUtils.ValidatePropertyName(propertyName); duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(streamProperty); IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(streamProperty.Name, owningType); WriterValidationUtils.ValidateStreamReferenceProperty(streamProperty, edmProperty, this.Version, this.WritingResponse); ODataStreamReferenceValue streamReferenceValue = (ODataStreamReferenceValue)streamProperty.Value; WriterValidationUtils.ValidateStreamReferenceValue(streamReferenceValue, false /*isDefaultStream*/); if (owningType != null && owningType.IsOpen && edmProperty == null) { ValidationUtils.ValidateOpenPropertyValue(streamProperty.Name, streamReferenceValue); } AtomStreamReferenceMetadata streamReferenceMetadata = streamReferenceValue.GetAnnotation <AtomStreamReferenceMetadata>(); string contentType = streamReferenceValue.ContentType; string linkTitle = streamProperty.Name; Uri readLink = streamReferenceValue.ReadLink; if (readLink != null) { string readLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, false); AtomLinkMetadata readLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.SelfLink; AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(readLinkMetadata, readLinkRelation, readLink, linkTitle, contentType); this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, null /* etag */); } Uri editLink = streamReferenceValue.EditLink; if (editLink != null) { string editLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, true); AtomLinkMetadata editLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink; AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(editLinkMetadata, editLinkRelation, editLink, linkTitle, contentType); this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, streamReferenceValue.ETag); } }
/// <summary> /// Writes the edit-media link for an entry. /// </summary> /// <param name="mediaResource">The media resource representing the MR of the entry to write.</param> internal void WriteEntryMediaEditLink(ODataStreamReferenceValue mediaResource) { Debug.Assert(mediaResource != null, "mediaResource != null"); Uri mediaEditLink = mediaResource.EditLink; Debug.Assert(mediaEditLink != null || mediaResource.ETag == null, "The default stream edit link and etag should have been validated by now."); if (mediaEditLink != null) { AtomStreamReferenceMetadata streamReferenceMetadata = mediaResource.GetAnnotation <AtomStreamReferenceMetadata>(); AtomLinkMetadata mediaEditMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink; AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata( mediaEditMetadata, AtomConstants.AtomEditMediaRelationAttributeValue, mediaEditLink, null /* title */, null /* mediaType */); this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, mediaResource.ETag); } }
/// <summary> /// Reads a stream property link in an atom:entry. /// </summary> /// <param name="entryState">The reader entry state for the entry being read.</param> /// <param name="streamPropertyName">The name of the stream property that is being read.</param> /// <param name="linkRelation">The rel attribute value for the link.</param> /// <param name="linkHRef">The href attribute value for the link (or null if the href attribute was not present).</param> /// <param name="editLink">true if we are reading an edit link; otherwise false.</param> /// <returns>true if the stream property link was read; otherwise false.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element atom:link - The atom:link element to read. /// Post-Condition: Any - The node after the atom:link element if the link was read by this method. /// XmlNodeType.Element atom:link - The atom:link element to read if the link was not read by this method. /// </remarks> private bool ReadStreamPropertyLinkInEntry(IODataAtomReaderEntryState entryState, string streamPropertyName, string linkRelation, string linkHRef, bool editLink) { if (!this.ReadingResponse) { // Ignore stream properties in requests as they cannot appear there. // Reader versioning: do not recognize stream properties if MPV < V3, but read them even in <V3 payload if MPV >= V3. return false; } // Fail on stream property with empty name, no backward compat reasons, we can fail. if (streamPropertyName.Length == 0) { throw new ODataException(ODataErrorStrings.ODataAtomEntryAndFeedDeserializer_StreamPropertyWithEmptyName); } ODataStreamReferenceValue streamReferenceValue = this.GetNewOrExistingStreamPropertyValue(entryState, streamPropertyName); Debug.Assert(streamReferenceValue != null, "streamReferenceValue != null"); AtomStreamReferenceMetadata atomStreamMetadata = null; if (this.ReadAtomMetadata) { // First, check if there is an existing metadata annotation on the stream reference value. atomStreamMetadata = streamReferenceValue.GetAnnotation<AtomStreamReferenceMetadata>(); // If not, create a new metadata annotation. if (atomStreamMetadata == null) { atomStreamMetadata = new AtomStreamReferenceMetadata(); streamReferenceValue.SetAnnotation(atomStreamMetadata); } } // set the edit-link or the read-link // We allow missing hrefs on atom:link elements Uri href = linkHRef == null ? null : this.ProcessUriFromPayload(linkHRef, this.XmlReader.XmlBaseUri); if (editLink) { // edit-link // We fail if we find two edit links for the same stream property. // WCF DS client behavior is to fail if we find two edit or read links for the same stream property. if (streamReferenceValue.EditLink != null) { throw new ODataException(ODataErrorStrings.ODataAtomEntryAndFeedDeserializer_StreamPropertyWithMultipleEditLinks(streamPropertyName)); } streamReferenceValue.EditLink = href; if (this.ReadAtomMetadata) { atomStreamMetadata.EditLink = this.EntryMetadataDeserializer.ReadAtomLinkElementInEntryContent(linkRelation, linkHRef); } } else { // read-link // We fail if we find two read links for the same stream property. // WCF DS client behavior is to fail if we find two edit or read links for the same stream property. if (streamReferenceValue.ReadLink != null) { throw new ODataException(ODataErrorStrings.ODataAtomEntryAndFeedDeserializer_StreamPropertyWithMultipleReadLinks(streamPropertyName)); } streamReferenceValue.ReadLink = href; if (this.ReadAtomMetadata) { atomStreamMetadata.SelfLink = this.EntryMetadataDeserializer.ReadAtomLinkElementInEntryContent(linkRelation, linkHRef); } } // set the ContentType string contentType = this.XmlReader.GetAttribute(this.AtomTypeAttributeName, this.EmptyNamespace); if (contentType != null && streamReferenceValue.ContentType != null) { // If we find two different content types, we fail since we have only one property to store it in (and it doesn't makes sense for a single stream // property to have two different content types). if (!HttpUtils.CompareMediaTypeNames(contentType, streamReferenceValue.ContentType)) { throw new ODataException(ODataErrorStrings.ODataAtomEntryAndFeedDeserializer_StreamPropertyWithMultipleContentTypes(streamPropertyName)); } } streamReferenceValue.ContentType = contentType; // set the ETag if (editLink) { string etag = this.XmlReader.GetAttribute(this.ODataETagAttributeName, this.XmlReader.ODataMetadataNamespace); streamReferenceValue.ETag = etag; } // [Astoria-ODataLib-Integration] Should we skip the atom:link element for named streams. // Skip the entire Atom:link element content. The client does not expect any content to be there. Also the client // does not write link elements when creating requests and the server does not really need to care about these. this.XmlReader.Skip(); return true; }
/// <summary> /// Get the stream reference value for media resource (the default stream of an entity). /// </summary> /// <param name="entityToSerialize">Entity that is currently being serialized.</param> /// <param name="title">The title for the element being written.</param> /// <returns> /// An instance of ODataStreamReferenceValue containing the metadata about the media resource. /// </returns> private ODataStreamReferenceValue GetMediaResource(EntityToSerialize entityToSerialize, string title) { Debug.Assert(entityToSerialize.Entity != null, "element != null"); Debug.Assert(entityToSerialize.ResourceType != null && entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type != null && type.ResourceTypeKind == ResourceTypeKind.EntityType"); Debug.Assert(!string.IsNullOrEmpty(title), "!string.IsNullOrEmpty(title)"); ODataStreamReferenceValue mediaResource = null; // Handle MLE if (entityToSerialize.ResourceType.IsMediaLinkEntry) { string mediaETag; Uri readStreamUri; string mediaContentType; Debug.Assert(entityToSerialize.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "type.ResourceTypeKind == ResourceTypeKind.EntityType"); this.Service.StreamProvider.GetStreamDescription(entityToSerialize.Entity, null /*null for MLE*/, this.Service.OperationContext, out mediaETag, out readStreamUri, out mediaContentType); Debug.Assert(WebUtil.IsETagValueValid(mediaETag, true), "WebUtil.IsETagValueValid(mediaETag, true)"); Debug.Assert(!string.IsNullOrEmpty(mediaContentType), "!string.IsNullOrEmpty(mediaContentType)"); mediaResource = new ODataStreamReferenceValue(); // build the stream's edit link lazily to avoid creating the entity's edit link if it is not needed. SimpleLazy<Uri> lazyStreamEditLink = new SimpleLazy<Uri>(() => RequestUriProcessor.AppendEscapedSegment(entityToSerialize.SerializedKey.RelativeEditLink, XmlConstants.UriValueSegment)); this.PayloadMetadataPropertyManager.SetEditLink(mediaResource, () => lazyStreamEditLink.Value); this.PayloadMetadataPropertyManager.SetContentType(mediaResource, mediaContentType); // If the stream provider did not provider a read link, then we should use the edit link as the read link. this.PayloadMetadataPropertyManager.SetReadLink(mediaResource, () => readStreamUri ?? lazyStreamEditLink.Value); #pragma warning disable 618 if (this.contentFormat == ODataFormat.Atom) #pragma warning restore 618 { AtomStreamReferenceMetadata mediaResourceAtom = new AtomStreamReferenceMetadata() { EditLink = new AtomLinkMetadata { Title = title } }; mediaResource.SetAnnotation(mediaResourceAtom); } if (!string.IsNullOrEmpty(mediaETag)) { this.PayloadMetadataPropertyManager.SetETag(mediaResource, mediaETag); } } return mediaResource; }
/// <summary> /// Visits an ATOM stream reference metadata. /// </summary> /// <param name="atomStreamReferenceMetadata">The stream reference metadata to visit.</param> protected virtual void VisitAtomStreamReferenceMetadata(AtomStreamReferenceMetadata atomStreamReferenceMetadata) { this.VisitAtomMetadata(atomStreamReferenceMetadata.EditLink); this.VisitAtomMetadata(atomStreamReferenceMetadata.SelfLink); }
public void NamedStreamReadAndEditLinkMetadataWriterTest() { Func<XElement, XElement> fragmentExtractor = (e) => e.Elements(TestAtomConstants.AtomXNamespace + "link").Last(); var allTestCases = linkMetadataTestCases.ConcatSingle(incorrectMediaTypeLinkMetadataTestCases).ConcatSingle(incorrectTitleLinkMetadataTestCases); var readLinkTestDescriptors = allTestCases.Select(testCase => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(); ODataStreamReferenceValue streamReferenceValue = new ODataStreamReferenceValue() { ReadLink = new Uri(readLinkHref), ContentType = linkMediaType, }; AtomStreamReferenceMetadata streamReferenceMetadata = new AtomStreamReferenceMetadata() { SelfLink = testCase.LinkMetadata("http://docs.oasis-open.org/odata/ns/mediaresource/Stream", readLinkHref) }; streamReferenceValue.SetAnnotation<AtomStreamReferenceMetadata>(streamReferenceMetadata); entry.Properties = new ODataProperty[] { new ODataProperty { Name = "Id", Value = 1 }, new ODataProperty { Name = "Stream", Value = streamReferenceValue } }; return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.ExpectedXml == null ? null : testCase.ExpectedXml("http://docs.oasis-open.org/odata/ns/mediaresource/Stream", readLinkHref, "Stream", linkMediaType), ExpectedException2 = testCase.ExpectedException == null ? null : testCase.ExpectedException("http://docs.oasis-open.org/odata/ns/mediaresource/Stream", readLinkHref), FragmentExtractor = fragmentExtractor }); }); var editLinkTestDescriptors = allTestCases.Select(testCase => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(); ODataStreamReferenceValue streamReferenceValue = new ODataStreamReferenceValue() { ReadLink = new Uri(readLinkHref), EditLink = new Uri(editLinkHref), ContentType = linkMediaType, }; AtomStreamReferenceMetadata streamReferenceMetadata = new AtomStreamReferenceMetadata() { EditLink = testCase.LinkMetadata("http://docs.oasis-open.org/odata/ns/edit-media/Stream", editLinkHref) }; streamReferenceValue.SetAnnotation<AtomStreamReferenceMetadata>(streamReferenceMetadata); entry.Properties = new ODataProperty[] { new ODataProperty { Name = "Id", Value = 1 }, new ODataProperty { Name = "Stream", Value = streamReferenceValue } }; return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.ExpectedXml == null ? null : testCase.ExpectedXml("http://docs.oasis-open.org/odata/ns/edit-media/Stream", editLinkHref, "Stream", linkMediaType), ExpectedException2 = testCase.ExpectedException == null ? null : testCase.ExpectedException("http://docs.oasis-open.org/odata/ns/edit-media/Stream", editLinkHref), FragmentExtractor = fragmentExtractor }); }); var testDescriptors = readLinkTestDescriptors.Concat(editLinkTestDescriptors); this.CombinatorialEngineProvider.RunCombinations( testDescriptors.PayloadCases(WriterPayloads.EntryPayloads), this.WriterTestConfigurationProvider.AtomFormatConfigurations .Where(tc => !tc.IsRequest), (testDescriptor, testConfiguration) => { testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); TestWriterUtils.WriteAndVerifyODataPayload(testDescriptor, testConfiguration, this.Assert, this.Logger); }); }
public void DefaultStreamEditLinkMetadataWriterTest() { Func<XElement, XElement> fragmentExtractor = (e) => e.Elements(TestAtomConstants.AtomXNamespace + "link").Last(); // NOTE: no self-link test cases since the self link is represented as the <content> element and not customizable through link metadata var testDescriptors = linkMetadataTestCases.Select(testCase => { ODataEntry entry = ObjectModelUtils.CreateDefaultEntryWithAtomMetadata(); ODataStreamReferenceValue streamReferenceValue = new ODataStreamReferenceValue() { ReadLink = new Uri(readLinkHref), EditLink = new Uri(editLinkHref), ContentType = linkMediaType, }; AtomStreamReferenceMetadata streamReferenceMetadata = new AtomStreamReferenceMetadata() { EditLink = testCase.LinkMetadata("edit-media", editLinkHref) }; streamReferenceValue.SetAnnotation<AtomStreamReferenceMetadata>(streamReferenceMetadata); entry.MediaResource = streamReferenceValue; return new PayloadWriterTestDescriptor<ODataItem>(this.Settings, entry, testConfiguration => new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { Xml = testCase.ExpectedXml == null ? null : testCase.ExpectedXml("edit-media", editLinkHref, null, null), ExpectedException2 = testCase.ExpectedException == null ? null : testCase.ExpectedException("edit-media", editLinkHref), FragmentExtractor = fragmentExtractor }); }); 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); }); }