public void ShouldBeAbleToSetTheFeedSerializationInfo()
 {
     ODataFeed feed = new ODataFeed();
     ODataFeedAndEntrySerializationInfo serializationInfo = new ODataFeedAndEntrySerializationInfo { NavigationSourceName = "Set", NavigationSourceEntityTypeName = "ns.base", ExpectedTypeName = "ns.expected" };
     feed.SetSerializationInfo(serializationInfo);
     feed.SerializationInfo.Should().BeSameAs(serializationInfo);
 }
        public void WriteStartShouldIgnoreDeltaLinkWhenWritingResponseTopLevelFeed()
        {
            string expectedPayload =
                 @"<?xml version=""1.0"" encoding=""utf-8""?>"
                + @"<feed xmlns=""http://www.w3.org/2005/Atom"" xmlns:d=""http://docs.oasis-open.org/odata/ns/data"" xmlns:m=""http://docs.oasis-open.org/odata/ns/metadata"" xmlns:georss=""http://www.georss.org/georss"" xmlns:gml=""http://www.opengis.net/gml"" m:context=""http://www.example.com/$metadata#TestEntitySet"">"
                    + "<id>http://host/TestEntitySet</id>"
                    + "<title />";

            Action<ODataWriter> deltaLinkAtWriteStart = (odataWriter) =>
            {
                var feedToWrite = new ODataFeed { Id = new Uri("http://host/TestEntitySet", UriKind.Absolute), DeltaLink = new Uri("http://host/deltaLink", UriKind.Absolute) };
                odataWriter.WriteStart(feedToWrite);
            };

            WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Atom, expectedPayload, request: false, createFeedWriter: true);
        }
        public void WriteStartShouldIgnoreDeltaLinkWhenWritingRequestTopLevelFeed()
        {
            string expectedPayload =
            "{" +
                "\"@odata.context\":\"http://www.example.com/$metadata#TestEntitySet\"," +
                "\"value\":[]" +
            "}";

            Action<ODataWriter> deltaLinkAtWriteStart = (odataWriter) =>
            {
                var feedToWrite = new ODataFeed { DeltaLink = new Uri("http://host/deltaLink", UriKind.Absolute) };
                odataWriter.WriteStart(feedToWrite);
                odataWriter.WriteEnd();
            };

            WriteAnnotationsAndValidatePayload(deltaLinkAtWriteStart, ODataFormat.Json, expectedPayload, request: true, createFeedWriter: true);
        }
        public void CreateFeed_NoMetadata()
        {
            var feed = new ODataFeed<Item>();
            feed.Feed = new List<Folder>()
            {
                GetFolder()
            };
            (feed.Feed.First()).MetadataUrl =
                "https://labs.sf-api.com/sf/v3/$metadata#ShareFile.Api.Models.Folder";
            ((feed.Feed.First() as Folder).Children.First()).MetadataUrl =
                "https://labs.sf-api.com/sf/v3/$metadata#ShareFile.Api.Models.File";
            feed.MetadataUrl = "";

            var serializer = GetSerializer();

            StringWriter writer = new StringWriter();
            serializer.Serialize(writer, feed);

            var jsonReader = new JsonTextReader(new StringReader(writer.ToString()));

            var item = serializer.Deserialize<ODataFeed<Item>>(jsonReader);
            item.Should().NotBeNull();
            item.Feed.First().GetType().Should().Be(typeof(Folder));
        }
        private void ApplyFeedInNavigationProperty(IEdmNavigationProperty navigationProperty, object entityResource, ODataFeed feed, ODataDeserializerContext readContext)
        {
            ODataFeedAnnotation feedAnnotation = feed.GetAnnotation <ODataFeedAnnotation>();

            Contract.Assert(feedAnnotation != null, "Each feed we create should gave annotation on it.");

            ODataEntryDeserializer deserializer = DeserializerProvider.GetODataDeserializer(navigationProperty.Type);
            object value = deserializer.ReadInline(feed, readContext);

            if (readContext.IsPatchMode)
            {
                throw Error.InvalidOperation(SRResources.CannotPatchNavigationProperties, navigationProperty.Name, navigationProperty.DeclaringEntityType().FullName());
            }

            SetCollectionProperty(entityResource, navigationProperty.Name, isDelta: false, value: value);
        }
        /// <summary>
        /// Reads one operation for the feed being read.
        /// </summary>
        /// <param name="feed">The feed to read.</param>
        /// <param name="metadataReferencePropertyName">The name of the metadata reference property being read.</param>
        /// <param name="insideArray">true if the operation value is inside an array, i.e. multiple targets for the operation; false otherwise.</param>
        private void ReadSingleOperationValue(ODataFeed feed, string metadataReferencePropertyName, bool insideArray)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(!string.IsNullOrEmpty(metadataReferencePropertyName), "!string.IsNullOrEmpty(metadataReferencePropertyName)");
            Debug.Assert(ODataJsonLightUtils.IsMetadataReferenceProperty(metadataReferencePropertyName), "ODataJsonLightReaderUtils.IsMetadataReferenceProperty(metadataReferencePropertyName)");

            if (this.JsonReader.NodeType != JsonNodeType.StartObject)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonOperationsDeserializerUtils_OperationsPropertyMustHaveObjectValue(metadataReferencePropertyName, this.JsonReader.NodeType));
            }

            // read over the start-object node of the metadata object for the operations
            this.JsonReader.ReadStartObject();

            var operation = this.CreateODataOperationAndAddToFeed(feed, metadataReferencePropertyName);

            // Ignore the unrecognized operation.
            if (operation == null)
            {
                while (this.JsonReader.NodeType == JsonNodeType.Property)
                {
                    this.JsonReader.ReadPropertyName();
                    this.JsonReader.SkipValue();
                }

                this.JsonReader.ReadEndObject();
                return;
            }

            Debug.Assert(operation.Metadata != null, "operation.Metadata != null");

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                string operationPropertyName = ODataAnnotationNames.RemoveAnnotationPrefix(this.JsonReader.ReadPropertyName());
                switch (operationPropertyName)
                {
                    case JsonConstants.ODataOperationTitleName:
                        if (operation.Title != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_MultipleOptionalPropertiesInOperation(operationPropertyName, metadataReferencePropertyName));
                        }

                        string titleString = this.JsonReader.ReadStringValue(JsonConstants.ODataOperationTitleName);
                        ODataJsonLightValidationUtils.ValidateOperationPropertyValueIsNotNull(titleString, operationPropertyName, metadataReferencePropertyName);
                        operation.Title = titleString;
                        break;

                    case JsonConstants.ODataOperationTargetName:
                        if (operation.Target != null)
                        {
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_MultipleOptionalPropertiesInOperation(operationPropertyName, metadataReferencePropertyName));
                        }

                        string targetString = this.JsonReader.ReadStringValue(JsonConstants.ODataOperationTargetName);
                        ODataJsonLightValidationUtils.ValidateOperationPropertyValueIsNotNull(targetString, operationPropertyName, metadataReferencePropertyName);
                        operation.Target = this.ProcessUriFromPayload(targetString);
                        break;

                    default:
                        // skip over all unknown properties and read the next property or 
                        // the end of the metadata for the current propertyName
                        this.JsonReader.SkipValue();
                        break;
                }
            }

            if (operation.Target == null && insideArray)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_OperationMissingTargetProperty(metadataReferencePropertyName));
            }

            // read the end-object node of the target / title pair
            this.JsonReader.ReadEndObject();
        }
        /// <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)
        {
            DebugUtils.CheckNoExternalCallers();
            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(!string.IsNullOrEmpty(feed.Id), "The feed Id should have been validated by now.");
                this.WriteElementWithTextContent(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomIdElementName,
                    AtomConstants.AtomNamespace,
                    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>
        /// Writes the odata.count annotation for a feed if it has not been written yet (and the count is specified on the feed).
        /// </summary>
        /// <param name="feed">The feed to write the count for.</param>
        /// <param name="propertyName">The name of the expanded nav property or null for a top-level feed.</param>
        private void WriteFeedCount(ODataFeed feed, string propertyName)
        {
            Debug.Assert(feed != null, "feed != null");

            // If we haven't written the count yet and it's available, write it.
            long? count = feed.Count;
            if (count.HasValue)
            {
                if (propertyName == null)
                {
                    this.odataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataCount);
                }
                else
                {
                    this.odataAnnotationWriter.WritePropertyAnnotationName(propertyName, ODataAnnotationNames.ODataCount);
                }

                this.jsonWriter.WriteValue(count.Value);
            }
        }
 /// <summary>
 /// Constructor to create a new feed scope.
 /// </summary>
 /// <param name="feed">The feed for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 internal JsonLightFeedScope(ODataFeed feed, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
     : base(feed, navigationSource, entityType, skipWriting, selectedProperties, odataUri)
 {
 }
 void IODataSerializationFeature.Apply(ODataFeed feed, ODataSerializationFeatureContext context)
 {
 }
 public AsyncOperationScheduledException(ODataFeed<AsyncOperation> asyncOperation)
 {
     ScheduledOperations = asyncOperation;
 }
Example #12
0
        public void ExpandedLinkWithNullNavigationTests()
        {
            ODataNavigationLink expandedEntryLink = ObjectModelUtils.CreateDefaultCollectionLink();

            expandedEntryLink.IsCollection = false;

            ODataNavigationLink expandedEntryLink2 = ObjectModelUtils.CreateDefaultCollectionLink();

            expandedEntryLink2.IsCollection = false;
            expandedEntryLink2.Name         = expandedEntryLink2.Name + "2";

            ODataEntry defaultEntry = ObjectModelUtils.CreateDefaultEntry();
            ODataFeed  defaultFeed  = ObjectModelUtils.CreateDefaultFeed();
            ODataEntry nullEntry    = ObjectModelUtils.ODataNullEntry;

            PayloadWriterTestDescriptor.WriterTestExpectedResultCallback successCallback = (testConfiguration) =>
            {
                if (testConfiguration.Format == ODataFormat.Atom)
                {
                    return(new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        Xml = new XElement(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataInlineElementName).ToString(),
                        FragmentExtractor = (result) =>
                        {
                            return result.Elements(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomLinkElementName)
                            .First(e => e.Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataInlineElementName) != null)
                            .Element(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.ODataInlineElementName);
                        }
                    });
                }
                else
                {
                    return(new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        Json = "null", //new JsonPrimitiveValue(null).ToText(testConfiguration.MessageWriterSettings.Indent),
                        FragmentExtractor = (result) =>
                        {
                            return JsonUtils.UnwrapTopLevelValue(testConfiguration, result).Object().Properties.First(p => p.Name == ObjectModelUtils.DefaultLinkName).Value;
                        }
                    });
                }
            };

            Func <ExpectedException, PayloadWriterTestDescriptor.WriterTestExpectedResultCallback> errorCallback = (expectedException) =>
            {
                return((testConfiguration) =>
                {
                    return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                    {
                        ExpectedException2 = expectedException,
                    };
                });
            };

            var testCases = new PayloadWriterTestDescriptor <ODataItem>[]
            {
                // navigation to a null entry
                new PayloadWriterTestDescriptor <ODataItem>(
                    this.Settings,
                    new ODataItem[] { defaultEntry, expandedEntryLink, nullEntry },
                    successCallback),

                // navigation to a null entry twice
                new PayloadWriterTestDescriptor <ODataItem>(
                    this.Settings,
                    new ODataItem[] { defaultEntry, expandedEntryLink, nullEntry, null, null, expandedEntryLink2, nullEntry, null },
                    successCallback),

                // top level null entry.
                new PayloadWriterTestDescriptor <ODataItem>(
                    this.Settings,
                    new ODataItem[] { nullEntry },
                    errorCallback(new ExpectedException(typeof(ArgumentNullException)))),

                // navigation to a null entry twice in expanded link
                // this actually throws ArgumentNullException when WriteStart() for the second nullEntry is called since
                // the state has been changed from NavigationLink to ExpandedLink after the first one.
                // TODO: check if ArgumentNullException needs to change the WriterState.
                new PayloadWriterTestDescriptor <ODataItem>(
                    this.Settings,
                    new ODataItem[] { defaultEntry, expandedEntryLink, nullEntry, null, nullEntry },
                    errorCallback(new ExpectedException(typeof(ArgumentNullException)))),

                // Null entry inside a feed, same as above this throws ArgumentNullException but state is not put to error state.
                new PayloadWriterTestDescriptor <ODataItem>(
                    this.Settings,
                    new ODataItem[] { defaultFeed, nullEntry },
                    errorCallback(new ExpectedException(typeof(ArgumentNullException)))),
            };

            // TODO: Fix places where we've lost JsonVerbose coverage to add JsonLight
            this.CombinatorialEngineProvider.RunCombinations(
                testCases,
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent.Where(tc => tc.Format == ODataFormat.Atom),
                (testCase, testConfig) =>
            {
                testConfig = testConfig.Clone();
                testConfig.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                TestWriterUtils.WriteAndVerifyODataPayload(testCase, testConfig, this.Assert, this.Logger);
            });
        }
Example #13
0
        public void ExpandedLinkWithMultiplicityTests()
        {
            ODataNavigationLink expandedEntryLink = ObjectModelUtils.CreateDefaultCollectionLink();

            expandedEntryLink.IsCollection = false;

            ODataNavigationLink expandedFeedLink = ObjectModelUtils.CreateDefaultCollectionLink();

            expandedFeedLink.IsCollection = true;

            ODataEntry defaultEntry = ObjectModelUtils.CreateDefaultEntry();
            ODataFeed  defaultFeed  = ObjectModelUtils.CreateDefaultFeed();
            ODataEntityReferenceLink defaultEntityReferenceLink = ObjectModelUtils.CreateDefaultEntityReferenceLink();

            ODataEntry officeEntry           = ObjectModelUtils.CreateDefaultEntry("TestModel.OfficeType");
            ODataEntry officeWithNumberEntry = ObjectModelUtils.CreateDefaultEntry("TestModel.OfficeWithNumberType");
            ODataEntry cityEntry             = ObjectModelUtils.CreateDefaultEntry("TestModel.CityType");

            // CityHall is a nav prop with multiplicity '*' of type 'TestModel.OfficeType'
            ODataNavigationLink cityHallLinkIsCollectionNull  = ObjectModelUtils.CreateDefaultCollectionLink("CityHall", /*isCollection*/ null);
            ODataNavigationLink cityHallLinkIsCollectionTrue  = ObjectModelUtils.CreateDefaultCollectionLink("CityHall", /*isCollection*/ true);
            ODataNavigationLink cityHallLinkIsCollectionFalse = ObjectModelUtils.CreateDefaultCollectionLink("CityHall", /*isCollection*/ false);

            // PoliceStation is a nav prop with multiplicity '1' of type 'TestModel.OfficeType'
            ODataNavigationLink policeStationLinkIsCollectionNull  = ObjectModelUtils.CreateDefaultCollectionLink("PoliceStation", /*isCollection*/ null);
            ODataNavigationLink policeStationLinkIsCollectionTrue  = ObjectModelUtils.CreateDefaultCollectionLink("PoliceStation", /*isCollection*/ true);
            ODataNavigationLink policeStationLinkIsCollectionFalse = ObjectModelUtils.CreateDefaultCollectionLink("PoliceStation", /*isCollection*/ false);

            ExpectedException expandedEntryLinkWithFeedContentError  = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedContent", "http://odata.org/link");
            ExpectedException expandedFeedLinkWithEntryContentError  = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryContent", "http://odata.org/link");
            ExpectedException expandedFeedLinkWithEntryMetadataError = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryMetadata", "http://odata.org/link");

            ExpectedException expandedEntryLinkWithFeedMetadataErrorResponse        = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedMetadata", "http://odata.org/link");
            ExpectedException expandedFeedLinkPayloadWithEntryMetadataErrorRequest  = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkWithFeedPayloadAndEntryMetadata", "http://odata.org/link");
            ExpectedException expandedFeedLinkPayloadWithEntryMetadataErrorResponse = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionTrueWithEntryMetadata", "http://odata.org/link");
            ExpectedException expandedEntryLinkPayloadWithFeedMetadataErrorResponse = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkIsCollectionFalseWithFeedMetadata", "http://odata.org/link");
            ExpectedException expandedEntryLinkPayloadWithFeedMetadataError         = ODataExpectedExceptions.ODataException("WriterValidationUtils_ExpandedLinkWithEntryPayloadAndFeedMetadata", "http://odata.org/link");
            ExpectedException multipleItemsInExpandedLinkError   = ODataExpectedExceptions.ODataException("ODataWriterCore_MultipleItemsInNavigationLinkContent", "http://odata.org/link");
            ExpectedException entityReferenceLinkInResponseError = ODataExpectedExceptions.ODataException("ODataWriterCore_EntityReferenceLinkInResponse");

            IEdmModel model = Microsoft.Test.OData.Utils.Metadata.TestModels.BuildTestModel();

            var testCases = new ExpandedLinkMultiplicityTestCase[]
            {
                #region IsCollection flag does not match payload
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link with IsCollection is 'false' and feed payload
                    Items         = new ODataItem[] { defaultEntry, expandedEntryLink, defaultFeed },
                    ExpectedError = tc => expandedEntryLinkWithFeedContentError,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link with IsCollection is 'true' and entry payload
                    Items         = new ODataItem[] { defaultEntry, expandedFeedLink, defaultEntry },
                    ExpectedError = tc => expandedFeedLinkWithEntryContentError,
                },
                #endregion IsCollection flag does not match payload
                #region IsCollection == null; check compatibility of entity types of navigation property and entity in expanded link
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of singleton type without IsCollection value and an entry of a non-matching entity type;
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionNull, cityEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Atom || !tc.IsRequest
                            ? ODataExpectedExceptions.ODataException("WriterValidationUtils_EntryTypeInExpandedLinkNotCompatibleWithNavigationPropertyType", "TestModel.CityType", "TestModel.OfficeType")
                            : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "PoliceStation"),
                    Model = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of singleton type without IsCollection value and an entry of a matching entity type; no error expected.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionNull, officeEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Json && !tc.IsRequest ? null : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "PoliceStation"),
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of singleton type without IsCollection and an entry of a derived entity type; no error expected.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionNull, officeWithNumberEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Json && !tc.IsRequest ? null : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "PoliceStation"),
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of collection type without IsCollection value and an entry of a non-matching entity type;
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionNull, defaultFeed, cityEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Json && !tc.IsRequest
                            ? ODataExpectedExceptions.ODataException("WriterValidationUtils_EntryTypeInExpandedLinkNotCompatibleWithNavigationPropertyType", "TestModel.CityType", "TestModel.OfficeType")
                            : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "CityHall"),
                    Model = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of collection type without IsCollection value and an entry of a matching entity type; no error expected.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionNull, defaultFeed, officeEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Json && !tc.IsRequest
                            ? null
                            : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "CityHall"),
                    Model = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Expanded link of collection type without IsCollection and an entry of a derived entity type; no error expected.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionNull, defaultFeed, officeWithNumberEntry },
                    ExpectedError = tc => tc.Format == ODataFormat.Json && !tc.IsRequest
                            ? null
                            : ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "CityHall"),
                    Model = model,
                },
                #endregion IsCollection == null; check compatibility of entity types of navigation property and entity in expanded link
                #region Expanded link with entry content
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == false, singleton nav prop; should not fail.
                    Items = new ODataItem[] { cityEntry, policeStationLinkIsCollectionFalse, officeEntry },
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == true, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionTrue, officeEntry },
                    ExpectedError = tc => expandedFeedLinkWithEntryContentError,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == false, collection nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionFalse, officeEntry },
                    ExpectedError = tc => tc.IsRequest ? expandedEntryLinkPayloadWithFeedMetadataError : expandedEntryLinkPayloadWithFeedMetadataErrorResponse,
                    Model         = model
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == true, collection nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionTrue, officeEntry },
                    ExpectedError = tc => expandedFeedLinkWithEntryContentError,
                    Model         = model
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == null, singleton nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionNull, officeEntry },
                    ExpectedError = tc => tc.IsRequest || tc.Format == ODataFormat.Atom
                            ? ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "PoliceStation")
                            : null,
                    Model = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Entry content, IsCollection == null, collection nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionNull, officeEntry },
                    ExpectedError = tc => expandedEntryLinkPayloadWithFeedMetadataError,
                    Model         = model,
                },
                #endregion Expanded collection link with entry content
                #region Expanded link with feed content
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == false, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionFalse, defaultFeed, officeEntry },
                    ExpectedError = tc => expandedEntryLinkWithFeedContentError,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == true, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionTrue, defaultFeed, officeEntry },
                    ExpectedError = tc => tc.IsRequest ? expandedFeedLinkPayloadWithEntryMetadataErrorRequest : expandedFeedLinkPayloadWithEntryMetadataErrorResponse,
                    Model         = model
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == false, collection nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionFalse, defaultFeed, officeEntry },
                    ExpectedError = tc => tc.IsRequest ? expandedEntryLinkWithFeedContentError : expandedEntryLinkWithFeedMetadataErrorResponse,
                    Model         = model
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == true, collection nav prop; should not fail.
                    Items = new ODataItem[] { cityEntry, cityHallLinkIsCollectionTrue, defaultFeed, officeEntry },
                    Model = model
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == null, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionNull, defaultFeed, officeEntry },
                    ExpectedError = tc => expandedFeedLinkPayloadWithEntryMetadataErrorRequest,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Feed content, IsCollection == null, collection nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionNull, defaultFeed, officeEntry },
                    ExpectedError = tc => tc.IsRequest || tc.Format == ODataFormat.Atom
                            ? ODataExpectedExceptions.ODataException("WriterValidationUtils_NavigationLinkMustSpecifyIsCollection", "CityHall")
                            : null,
                    Model = model,
                },
                #endregion Expanded collection link with entry content
                #region Expanded link with entity reference link content
                new ExpandedLinkMultiplicityTestCase
                {
                    // Single ERL (entity reference link) content, IsCollection == false, singleton nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionFalse, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? null : entityReferenceLinkInResponseError
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Multiple ERL (entity reference link) content, IsCollection == false, singleton nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionFalse, defaultEntityReferenceLink, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? multipleItemsInExpandedLinkError : entityReferenceLinkInResponseError,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Single ERL content, IsCollection == true, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionTrue, defaultEntityReferenceLink },
                    ExpectedError = tc => expandedFeedLinkWithEntryMetadataError,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Multiple ERL content, IsCollection == true, singleton nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, policeStationLinkIsCollectionTrue, defaultEntityReferenceLink, defaultEntityReferenceLink },
                    ExpectedError = tc => expandedFeedLinkWithEntryMetadataError,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Single ERL content, IsCollection == false, collection nav prop; should not fail (metadata mismatch explicitly allowed).
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionFalse, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? null : expandedEntryLinkWithFeedMetadataErrorResponse,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Multiple ERL content, IsCollection == false, collection nav prop; should fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionFalse, defaultEntityReferenceLink, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? multipleItemsInExpandedLinkError : expandedEntryLinkWithFeedMetadataErrorResponse,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Single ERL content, IsCollection == true, collection nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionTrue, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? null : entityReferenceLinkInResponseError,
                    Model         = model,
                },
                new ExpandedLinkMultiplicityTestCase
                {
                    // Multiple ERL content, IsCollection == true, collection nav prop; should not fail.
                    Items         = new ODataItem[] { cityEntry, cityHallLinkIsCollectionTrue, defaultEntityReferenceLink, defaultEntityReferenceLink },
                    ExpectedError = tc => tc.IsRequest ? null : entityReferenceLinkInResponseError,
                    Model         = model,
                },

                //// NOTE: Not testing the cases where IsCollection == null here since ERL payloads are only allowed in
                ////       requests where IsCollection is required (in ATOM and JSON)
                #endregion Expanded link with entity reference link content
            };

            // TODO: Fix places where we've lost JsonVerbose coverage to add JsonLight
            this.CombinatorialEngineProvider.RunCombinations(
                testCases,
                this.WriterTestConfigurationProvider.ExplicitFormatConfigurations.Where(tc => tc.Format == ODataFormat.Atom),
                (testCase, testConfiguration) =>
            {
                testConfiguration = testConfiguration.Clone();
                testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                using (var memoryStream = new TestStream())
                    using (var messageWriter = TestWriterUtils.CreateMessageWriter(memoryStream, testConfiguration, this.Assert, null, testCase.Model))
                    {
                        ODataWriter writer = messageWriter.CreateODataWriter(isFeed: false);
                        TestExceptionUtils.ExpectedException(
                            this.Assert,
                            () => TestWriterUtils.WritePayload(messageWriter, writer, true, testCase.Items),
                            testCase.ExpectedError == null ? null : testCase.ExpectedError(testConfiguration),
                            this.ExceptionVerifier);
                    }
            });
        }
        /// <summary>
        /// Creates the materializer link with a feed.
        /// </summary>
        /// <param name="link">The link.</param>
        /// <param name="feed">The feed.</param>
        /// <returns>The materializer link.</returns>
        public static MaterializerNavigationLink CreateLink(ODataNavigationLink link, ODataFeed feed)
        {
            Debug.Assert(link.GetAnnotation <MaterializerNavigationLink>() == null, "there should be no MaterializerNavigationLink annotation on the feed link yet");
            MaterializerNavigationLink materializedNavigationLink = new MaterializerNavigationLink(link, feed);

            link.SetAnnotation <MaterializerNavigationLink>(materializedNavigationLink);
            return(materializedNavigationLink);
        }
Example #15
0
 public ODataFeedTests()
 {
     this.odataFeed = new ODataFeed();
 }
Example #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ReadingFeedArgs" /> class.
 /// </summary>
 /// <param name="feed">The feed.</param>
 public ReadingFeedArgs(ODataFeed feed)
 {
     Util.CheckArgumentNull(feed, "feed");
     this.Feed = feed;
 }
Example #17
0
        /// <summary>
        /// Start writing a feed.
        /// </summary>
        /// <param name="feed">The feed to write.</param>
        protected override void StartFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(
                this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.HasValue || this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            // Verify non-empty ID
            // We require non-null, non-empty IDs on feeds since it is required by ATOM.
            if (feed.Id == null)
            {
                throw new ODataException(OData.Core.Strings.ODataAtomWriter_FeedsMustHaveNonEmptyId);
            }

            this.CheckAndWriteParentNavigationLinkStartForInlineElement();

            // <atom:feed>
            this.atomOutputContext.XmlWriter.WriteStartElement(AtomConstants.AtomNamespacePrefix, AtomConstants.AtomFeedElementName, AtomConstants.AtomNamespace);

            if (this.IsTopLevel)
            {
                this.atomEntryAndFeedSerializer.WriteBaseUriAndDefaultNamespaceAttributes();

                // metadata:context
                this.atomEntryAndFeedSerializer.TryWriteFeedContextUri(this.CurrentFeedScope.GetOrCreateTypeContext(this.atomOutputContext.Model, this.atomOutputContext.WritingResponse));

                if (feed.Count.HasValue)
                {
                    this.atomEntryAndFeedSerializer.WriteCount(feed.Count.Value);
                }
            }
            
            bool authorWritten;
            this.atomEntryAndFeedSerializer.WriteFeedMetadata(feed, this.updatedTime, out authorWritten);
            this.CurrentFeedScope.AuthorWritten = authorWritten;
            this.WriteFeedInstanceAnnotations(feed, this.CurrentFeedScope);
        }
 /// <summary>
 /// Visits a feed item.
 /// </summary>
 /// <param name="feed">The feed to visit.</param>
 protected override void VisitFeed(ODataFeed feed)
 {
     this.VisitAtomMetadata(feed.GetAnnotation <AtomFeedMetadata>());
     base.VisitFeed(feed);
 }
 /// <summary>
 /// Provide additional serialization information to the <see cref="ODataWriter"/> for <paramref name="feed"/>.
 /// </summary>
 /// <param name="feed">The instance to set the serialization info.</param>
 /// <param name="serializationInfo">The serialization info to set.</param>
 public static void SetSerializationInfo(this ODataFeed feed, ODataFeedAndEntrySerializationInfo serializationInfo)
 {
     ExceptionUtils.CheckArgumentNotNull(feed, "feed");
     feed.SerializationInfo = serializationInfo;
 }
        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();
                }
            }
        }
        /// <summary>
        /// Finish writing a feed.
        /// </summary>
        /// <param name="feed">The feed to write.</param>
        protected override void EndFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            if (this.ParentNavigationLink == null && this.writingParameter)
            {
                // End the array which holds the entries in the feed.
                this.jsonWriter.EndArrayScope();
            }
            else if (this.ParentNavigationLink == null)
            {
                // End the array which holds the entries in the feed.
                this.jsonWriter.EndArrayScope();

                // Write custom instance annotations
                this.jsonLightEntryAndFeedSerializer.InstanceAnnotationWriter.WriteInstanceAnnotations(feed.InstanceAnnotations, this.CurrentFeedScope.InstanceAnnotationWriteTracker);

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, /*propertyName*/null);

                    // Write the delta link if it's available.
                    this.WriteFeedDeltaLink(feed);
                }

                // Close the object wrapper.
                this.jsonWriter.EndObjectScope();
            }
            else
            {
                Debug.Assert(
                    this.ParentNavigationLink != null && this.ParentNavigationLink.IsCollection.HasValue && this.ParentNavigationLink.IsCollection.Value,
                    "We should have verified that feeds can only be written into IsCollection = true links in requests.");
                string propertyName = this.ParentNavigationLink.Name;

                this.ValidateNoDeltaLinkForExpandedFeed(feed);
                this.ValidateNoCustomInstanceAnnotationsForExpandedFeed(feed);

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // End the array which holds the entries in the feed.
                    // NOTE: in requests we will only write the EndArray of a feed 
                    //       when we hit the navigation link end since a navigation link
                    //       can contain multiple feeds that get collapesed into a single array value.
                    this.jsonWriter.EndArrayScope();

                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, propertyName);
                }
            }
        }
Example #22
0
 public override System.Threading.Tasks.Task WriteStartAsync(ODataFeed feed)
 {
     throw new NotImplementedException();
 }
        /// <summary>
        /// Writes the odata.deltaLink annotation for a feed if it has not been written yet (and the delta link is specified on the feed).
        /// </summary>
        /// <param name="feed">The feed to write the delta link for.</param>
        private void WriteFeedDeltaLink(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            // If we haven't written the delta link yet and it's available, write it.
            Uri deltaLink = feed.DeltaLink;
            if (deltaLink != null && !this.CurrentFeedScope.DeltaLinkWritten)
            {
                this.odataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataDeltaLink);
                this.jsonWriter.WriteValue(this.jsonLightEntryAndFeedSerializer.UriToString(deltaLink));
                this.CurrentFeedScope.DeltaLinkWritten = true;
            }
        }
        /// <summary>
        /// Start writing a feed.
        /// </summary>
        /// <param name="feed">The feed to write.</param>
        protected override void StartFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            IJsonWriter jsonWriter = this.jsonLightOutputContext.JsonWriter;

            if (this.ParentNavigationLink == null)
            {
                // Top-level feed.
                // "{"
                jsonWriter.StartObjectScope();

                // @odata.context
                this.jsonLightEntryAndFeedSerializer.WriteFeedContextUri(this.CurrentFeedScope.GetOrCreateTypeContext(this.jsonLightOutputContext.Model, this.jsonLightOutputContext.WritingResponse));

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // Write the inline count if it's available.
                    this.WriteFeedCount(feed, /*propertyName*/ null);

                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, /*propertyName*/ null);

                    // Write the delta link if it's available.
                    this.WriteFeedDeltaLink(feed);
                }

                // Write custom instance annotations
                this.jsonLightEntryAndFeedSerializer.InstanceAnnotationWriter.WriteInstanceAnnotations(feed.InstanceAnnotations, this.CurrentFeedScope.InstanceAnnotationWriteTracker);

                // "value":
                jsonWriter.WriteValuePropertyName();

                // Start array which will hold the entries in the feed.
                jsonWriter.StartArrayScope();
            }
            else
            {
                // Expanded feed.
                Debug.Assert(
                    this.ParentNavigationLink != null && this.ParentNavigationLink.IsCollection.HasValue && this.ParentNavigationLink.IsCollection.Value,
                    "We should have verified that feeds can only be written into IsCollection = true links in requests.");
                string propertyName = this.ParentNavigationLink.Name;

                this.ValidateNoDeltaLinkForExpandedFeed(feed);
                this.ValidateNoCustomInstanceAnnotationsForExpandedFeed(feed);

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // Write the inline count if it's available.
                    this.WriteFeedCount(feed, propertyName);

                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, propertyName);

                    // And then write the property name to start the value.
                    jsonWriter.WriteName(propertyName);

                    // Start array which will hold the entries in the feed.
                    jsonWriter.StartArrayScope();
                }
                else
                {
                    JsonLightNavigationLinkScope navigationLinkScope = (JsonLightNavigationLinkScope)this.ParentNavigationLinkScope;
                    if (!navigationLinkScope.FeedWritten)
                    {
                        // Close the entity reference link array (if written)
                        if (navigationLinkScope.EntityReferenceLinkWritten)
                        {
                            jsonWriter.EndArrayScope();
                        }

                        // And then write the property name to start the value.
                        jsonWriter.WriteName(propertyName);

                        // Start array which will hold the entries in the feed.
                        jsonWriter.StartArrayScope();

                        navigationLinkScope.FeedWritten = true;
                    }
                }
            }
        }
        public void WritingFeedWithFunctionAndAction()
        {
            ODataFeed feed = new ODataFeed();
            feed.AddAction(new ODataAction { Metadata = new Uri("http://example.org/odata.svc/$metadata#Action"), Target = new Uri("http://example.org/odata.svc/DoAction"), Title = "ActionTitle" });
            feed.AddFunction(new ODataFunction() { Metadata = new Uri("http://example.org/odata.svc/$metadata#Function"), Target = new Uri("http://example.org/odata.svc/DoFunction"), Title = "FunctionTitle" });

            ODataItem[] itemsToWrite = new ODataItem[]
            {
                feed,
                this.entryWithOnlyData1,
            };

            string result = this.GetWriterOutputForContentTypeAndKnobValue("application/json;odata.metadata=minimal", true, itemsToWrite, Model, EntitySet, EntityType);

            const string expectedPayload = "{\"" +
                                                "@odata.context\":\"http://example.org/odata.svc/$metadata#EntitySet\"," +
                                                 "\"#Action\":{" +
                                                        "\"title\":\"ActionTitle\"," +
                                                        "\"target\":\"http://example.org/odata.svc/DoAction\"" +
                                                    "}," +
                                                    "\"#Function\":{" +
                                                        "\"title\":\"FunctionTitle\"," +
                                                        "\"target\":\"http://example.org/odata.svc/DoFunction\"" +
                                                    "}," +
                                                "\"value\":[" +
                                                    "{" +
                                                         "\"ID\":101,\"Name\":\"Alice\"" +
                                                    "}" +
                                                    "]" +
                                            "}";
            result.Should().Be(expectedPayload);
        }
 /// <summary>
 /// Create a new feed scope.
 /// </summary>
 /// <param name="feed">The feed for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 /// <returns>The newly create scope.</returns>
 protected override FeedScope CreateFeedScope(ODataFeed feed, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
 {
     return(new JsonLightFeedScope(feed, navigationSource, entityType, skipWriting, selectedProperties, odataUri));
 }
        /// <summary>
        /// Read the metadata reference property value for the feed being read.
        /// </summary>
        /// <param name="feed">The feed to read.</param>
        /// <param name="metadataReferencePropertyName">The name of the metadata reference property being read.</param>
        private void ReadMetadataReferencePropertyValue(ODataFeed feed, string metadataReferencePropertyName)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(!string.IsNullOrEmpty(metadataReferencePropertyName), "!string.IsNullOrEmpty(metadataReferencePropertyName)");
            Debug.Assert(metadataReferencePropertyName.IndexOf(ODataConstants.ContextUriFragmentIndicator) > -1, "metadataReferencePropertyName.IndexOf(JsonLightConstants.ContextUriFragmentIndicator) > -1");
            this.JsonReader.AssertNotBuffering();

            this.ValidateCanReadMetadataReferenceProperty();

            // Validate that the property name is a valid absolute URI or a valid URI fragment.
            ODataJsonLightValidationUtils.ValidateMetadataReferencePropertyName(this.ContextUriParseResult.MetadataDocumentUri, metadataReferencePropertyName);

            bool insideArray = false;
            if (this.JsonReader.NodeType == JsonNodeType.StartArray)
            {
                this.JsonReader.ReadStartArray();
                insideArray = true;
            }

            do
            {
                this.ReadSingleOperationValue(feed, metadataReferencePropertyName, insideArray);
            }
            while (insideArray && this.JsonReader.NodeType != JsonNodeType.EndArray);

            if (insideArray)
            {
                this.JsonReader.ReadEndArray();
            }

            this.JsonReader.AssertNotBuffering();
            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);
        }
 /// <summary>
 /// Constructor to create a new feed scope.
 /// </summary>
 /// <param name="feed">The feed for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 internal JsonLightFeedScope(ODataFeed feed, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
     : base(feed, navigationSource, entityType, skipWriting, selectedProperties, odataUri)
 {
 }
 protected override void EndFeed(ODataFeed feed)
 {
     throw new NotImplementedException();
 }
        internal void WriteFeedMetadata(AtomFeedMetadata feedMetadata, ODataFeed feed, string updatedTime, out bool authorWritten)
        {
            string textContent = (feed == null) ? feedMetadata.SourceId : feed.Id;

            base.WriteElementWithTextContent("", "id", "http://www.w3.org/2005/Atom", textContent);
            base.WriteTextConstruct("", "title", "http://www.w3.org/2005/Atom", feedMetadata.Title);
            if (feedMetadata.Subtitle != null)
            {
                base.WriteTextConstruct("", "subtitle", "http://www.w3.org/2005/Atom", feedMetadata.Subtitle);
            }
            string str2 = feedMetadata.Updated.HasValue ? ODataAtomConvert.ToAtomString(feedMetadata.Updated.Value) : updatedTime;

            base.WriteElementWithTextContent("", "updated", "http://www.w3.org/2005/Atom", str2);
            AtomLinkMetadata selfLink = feedMetadata.SelfLink;

            if (selfLink != null)
            {
                AtomLinkMetadata linkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(selfLink, "self", null, null, null);
                base.WriteAtomLink(linkMetadata, null);
            }
            IEnumerable <AtomLinkMetadata> links = feedMetadata.Links;

            if (links != null)
            {
                foreach (AtomLinkMetadata metadata3 in links)
                {
                    base.WriteAtomLink(metadata3, null);
                }
            }
            IEnumerable <AtomCategoryMetadata> categories = feedMetadata.Categories;

            if (categories != null)
            {
                foreach (AtomCategoryMetadata metadata4 in categories)
                {
                    base.WriteCategory(metadata4);
                }
            }
            Uri logo = feedMetadata.Logo;

            if (logo != null)
            {
                base.WriteElementWithTextContent("", "logo", "http://www.w3.org/2005/Atom", base.UriToUrlAttributeValue(logo));
            }
            if (feedMetadata.Rights != null)
            {
                base.WriteTextConstruct("", "rights", "http://www.w3.org/2005/Atom", feedMetadata.Rights);
            }
            IEnumerable <AtomPersonMetadata> contributors = feedMetadata.Contributors;

            if (contributors != null)
            {
                foreach (AtomPersonMetadata metadata5 in contributors)
                {
                    base.XmlWriter.WriteStartElement("", "contributor", "http://www.w3.org/2005/Atom");
                    base.WritePersonMetadata(metadata5);
                    base.XmlWriter.WriteEndElement();
                }
            }
            AtomGeneratorMetadata generator = feedMetadata.Generator;

            if (generator != null)
            {
                base.XmlWriter.WriteStartElement("", "generator", "http://www.w3.org/2005/Atom");
                if (generator.Uri != null)
                {
                    base.XmlWriter.WriteAttributeString("uri", base.UriToUrlAttributeValue(generator.Uri));
                }
                if (!string.IsNullOrEmpty(generator.Version))
                {
                    base.XmlWriter.WriteAttributeString("version", generator.Version);
                }
                ODataAtomWriterUtils.WriteString(base.XmlWriter, generator.Name);
                base.XmlWriter.WriteEndElement();
            }
            Uri icon = feedMetadata.Icon;

            if (icon != null)
            {
                base.WriteElementWithTextContent("", "icon", "http://www.w3.org/2005/Atom", base.UriToUrlAttributeValue(icon));
            }
            IEnumerable <AtomPersonMetadata> authors = feedMetadata.Authors;

            authorWritten = false;
            if (authors != null)
            {
                foreach (AtomPersonMetadata metadata7 in authors)
                {
                    authorWritten = true;
                    base.XmlWriter.WriteStartElement("", "author", "http://www.w3.org/2005/Atom");
                    base.WritePersonMetadata(metadata7);
                    base.XmlWriter.WriteEndElement();
                }
            }
        }
        public void QueryWithOrderBy()
        {
            Dictionary <string, int> testCases = new Dictionary <string, int>()
            {
                { "Customers?$orderby=PersonID", -1 },                       //-1 means the current PersonID is smaller than the next one.
                { "Customers?$orderby=PersonID mul @factor&@factor=-1", 1 }, //-1 means the current PersonID is bigger than the next one.
                { "Customers?$orderby=PersonID mul @factor desc&@factor=-1", -1 },
            };

            ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings()
            {
                BaseUri = ServiceBaseUri
            };

            foreach (var testCase in testCases)
            {
                foreach (var mimeType in mimeTypes)
                {
                    var requestMessage = new HttpWebRequestMessage(new Uri(ServiceBaseUri.AbsoluteUri + testCase.Key, UriKind.Absolute));
                    requestMessage.SetHeader("Accept", mimeType);
                    var responseMessage = requestMessage.GetResponse();
                    Assert.AreEqual(200, responseMessage.StatusCode);

                    if (!mimeType.Contains(MimeTypes.ODataParameterNoMetadata))
                    {
                        ODataEntry entry1 = null;
                        ODataEntry entry2 = null;
                        ODataFeed  feed   = null;
                        using (var messageReader = new ODataMessageReader(responseMessage, readerSettings, Model))
                        {
                            var reader = messageReader.CreateODataFeedReader();

                            while (reader.Read())
                            {
                                if (reader.State == ODataReaderState.EntryEnd)
                                {
                                    if (null == entry1)
                                    {
                                        entry1 = reader.Item as ODataEntry;
                                    }
                                    else
                                    {
                                        entry2 = reader.Item as ODataEntry;
                                    }
                                }
                                else if (reader.State == ODataReaderState.FeedEnd)
                                {
                                    feed = reader.Item as ODataFeed;
                                }
                            }
                        }
                        Assert.IsNotNull(feed);

                        Assert.AreEqual(testCase.Value,
                                        String.Compare(
                                            entry1.Properties.Single(p => p.Name == "PersonID").Value.ToString(),
                                            entry2.Properties.Single(p => p.Name == "PersonID").Value.ToString()));
                    }
                }
            }
        }
Example #32
0
 /// <summary>
 /// Writes the collection of <see cref="ODataInstanceAnnotation"/> for the given <paramref name="feed"/> to the ATOM payload.
 /// </summary>
 /// <param name="feed">The feed to write the <see cref="ODataInstanceAnnotation"/> for.</param>
 /// <param name="currentFeedScope">The current feed scope.</param>
 private void WriteFeedInstanceAnnotations(ODataFeed feed, AtomFeedScope currentFeedScope)
 {
     if (this.IsTopLevel)
     {
         this.WriteInstanceAnnotations(feed.InstanceAnnotations, currentFeedScope.InstanceAnnotationWriteTracker);
     }
     else
     {
         if (feed.InstanceAnnotations.Count > 0)
         {
             throw new ODataException(OData.Core.Strings.ODataJsonLightWriter_InstanceAnnotationNotSupportedOnExpandedFeed);
         }
     }
 }
        internal static ODataItem ReadEntryOrFeed(ODataReader odataReader, ODataDeserializerContext readContext)
        {
            ODataItem         topLevelItem = null;
            Stack <ODataItem> itemsStack   = new Stack <ODataItem>();

            while (odataReader.Read())
            {
                switch (odataReader.State)
                {
                case ODataReaderState.EntryStart:
                    ODataEntry           entry           = (ODataEntry)odataReader.Item;
                    ODataEntryAnnotation entryAnnotation = null;
                    if (entry != null)
                    {
                        entryAnnotation = new ODataEntryAnnotation();
                        entry.SetAnnotation(entryAnnotation);
                    }

                    if (itemsStack.Count == 0)
                    {
                        Contract.Assert(entry != null, "The top-level entry can never be null.");
                        topLevelItem = entry;
                    }
                    else
                    {
                        ODataItem parentItem = itemsStack.Peek();
                        ODataFeed parentFeed = parentItem as ODataFeed;
                        if (parentFeed != null)
                        {
                            ODataFeedAnnotation parentFeedAnnotation = parentFeed.GetAnnotation <ODataFeedAnnotation>();
                            Contract.Assert(parentFeedAnnotation != null, "Every feed we added to the stack should have the feed annotation on it.");
                            parentFeedAnnotation.Add(entry);
                        }
                        else
                        {
                            ODataNavigationLink           parentNavigationLink           = (ODataNavigationLink)parentItem;
                            ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                            Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                            Contract.Assert(parentNavigationLink.IsCollection == false, "Only singleton navigation properties can contain entry as their child.");
                            Contract.Assert(parentNavigationLinkAnnotation.Count == 0, "Each navigation property can contain only one entry as its direct child.");
                            parentNavigationLinkAnnotation.Add(entry);
                        }
                    }
                    itemsStack.Push(entry);
                    RecurseEnter(readContext);
                    break;

                case ODataReaderState.EntryEnd:
                    Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The entry which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    RecurseLeave(readContext);
                    break;

                case ODataReaderState.NavigationLinkStart:
                    ODataNavigationLink navigationLink = (ODataNavigationLink)odataReader.Item;
                    Contract.Assert(navigationLink != null, "Navigation link should never be null.");

                    navigationLink.SetAnnotation(new ODataNavigationLinkAnnotation());
                    Contract.Assert(itemsStack.Count > 0, "Navigation link can't appear as top-level item.");
                    {
                        ODataEntry           parentEntry           = (ODataEntry)itemsStack.Peek();
                        ODataEntryAnnotation parentEntryAnnotation = parentEntry.GetAnnotation <ODataEntryAnnotation>();
                        Contract.Assert(parentEntryAnnotation != null, "Every entry we added to the stack should have the entry annotation on it.");
                        parentEntryAnnotation.Add(navigationLink);
                    }

                    itemsStack.Push(navigationLink);
                    RecurseEnter(readContext);
                    break;

                case ODataReaderState.NavigationLinkEnd:
                    Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The navigation link which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    RecurseLeave(readContext);
                    break;

                case ODataReaderState.FeedStart:
                    ODataFeed feed = (ODataFeed)odataReader.Item;
                    Contract.Assert(feed != null, "Feed should never be null.");

                    feed.SetAnnotation(new ODataFeedAnnotation());
                    if (itemsStack.Count > 0)
                    {
                        ODataNavigationLink parentNavigationLink = (ODataNavigationLink)itemsStack.Peek();
                        Contract.Assert(parentNavigationLink != null, "this has to be an inner feed. inner feeds always have a navigation link.");
                        ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                        Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                        Contract.Assert(parentNavigationLink.IsCollection == true, "Only collection navigation properties can contain feed as their child.");
                        parentNavigationLinkAnnotation.Add(feed);
                    }
                    else
                    {
                        topLevelItem = feed;
                    }

                    itemsStack.Push(feed);
                    RecurseEnter(readContext);
                    break;

                case ODataReaderState.FeedEnd:
                    Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The feed which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    RecurseLeave(readContext);
                    break;

                case ODataReaderState.EntityReferenceLink:
                    ODataEntityReferenceLink entityReferenceLink = (ODataEntityReferenceLink)odataReader.Item;
                    Contract.Assert(entityReferenceLink != null, "Entity reference link should never be null.");

                    Contract.Assert(itemsStack.Count > 0, "Entity reference link should never be reported as top-level item.");
                    {
                        ODataNavigationLink           parentNavigationLink           = (ODataNavigationLink)itemsStack.Peek();
                        ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                        Contract.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                        parentNavigationLinkAnnotation.Add(entityReferenceLink);
                    }

                    break;

                default:
                    Contract.Assert(false, "We should never get here, it means the ODataReader reported a wrong state.");
                    break;
                }
            }

            Contract.Assert(odataReader.State == ODataReaderState.Completed, "We should have consumed all of the input by now.");
            Contract.Assert(topLevelItem != null, "A top level entry or feed should have been read by now.");
            return(topLevelItem);
        }
        /// <summary>
        /// Writes the delta link for a feed.
        /// </summary>
        /// <param name="feed">The feed to write the delta link for.</param>
        internal void WriteFeedDeltaLink(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            Uri deltaLink = feed.DeltaLink;
            if (deltaLink != null)
            {
                // <atom:link rel="http://docs.oasis-open.org/odata/ns/delta" href="delta-link" />
                this.WriteFeedLink(
                    feed, 
                    AtomConstants.AtomDeltaRelationAttributeValue, 
                    deltaLink, 
                    (feedMetadata) => feedMetadata == null ? null : feedMetadata.Links.FirstOrDefault(link => link.Relation == AtomConstants.AtomDeltaRelationAttributeValue));
            }
        }
Example #35
0
        /// <summary>
        /// Finish writing a feed.
        /// </summary>
        /// <param name="feed">The feed to write.</param>
        protected override void EndFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(
                this.ParentNavigationLink == null || this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            AtomFeedScope currentFeedScope = this.CurrentFeedScope;
            if (!currentFeedScope.AuthorWritten && currentFeedScope.EntryCount == 0)
            {
                // Write an empty author if there were no entries, since the feed must have an author if the entries don't have one as per ATOM spec
                this.atomEntryAndFeedSerializer.WriteFeedDefaultAuthor();
            }

            this.WriteFeedInstanceAnnotations(feed, currentFeedScope);
            this.atomEntryAndFeedSerializer.WriteFeedNextPageLink(feed);

            // Write delta link only in case of writing response for a top level feed.
            if (this.IsTopLevel)
            {
                if (this.atomOutputContext.WritingResponse)
                {
                    this.atomEntryAndFeedSerializer.WriteFeedDeltaLink(feed);
                }
            }
            else
            {
                this.ValidateNoDeltaLinkForExpandedFeed(feed);
            }

            // </atom:feed>
            this.atomOutputContext.XmlWriter.WriteEndElement();

            this.CheckAndWriteParentNavigationLinkEndForInlineElement();
        }
Example #36
0
 public void InitTest()
 {
     this.odataFeed = new ODataFeed();
 }
        /// <summary>
        /// Writes the next page link for a feed.
        /// </summary>
        /// <param name="feed">The feed to write the next page link for.</param>
        internal void WriteFeedNextPageLink(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            Uri nextPageLink = feed.NextPageLink;
            if (nextPageLink != null)
            {
                // <atom:link rel="next" href="next-page-link" />
                this.WriteFeedLink(
                    feed,
                    AtomConstants.AtomNextRelationAttributeValue,
                    nextPageLink, 
                    (feedMetadata) => feedMetadata == null ? null : feedMetadata.NextPageLink);
            }
        }
Example #38
0
        /// <summary>
        /// Reads an entry from the <paramref name="odataReader"/> and all it's children including expanded entries and feeds.
        /// </summary>
        /// <param name="odataReader">The ODataReader to read from.</param>
        /// <param name="topLevelSegmentInfo">The segment info for the top-level entry to read.</param>
        /// <returns>The <see cref="ODataEntry"/> with annotations which store the navigation links and their expanded values.</returns>
        private ODataEntry ReadEntry(ODataReader odataReader, SegmentInfo topLevelSegmentInfo)
        {
            Debug.Assert(odataReader != null, "odataReader != null");
            Debug.Assert(odataReader.State == ODataReaderState.Start, "The ODataReader must not have been used yet.");
            Debug.Assert(topLevelSegmentInfo != null, "topLevelSegmentInfo != null");

            ODataEntry        topLevelEntry = null;
            Stack <ODataItem> itemsStack    = new Stack <ODataItem>();

            while (odataReader.Read())
            {
                // Don't let the item stack grow larger than we can process later. Also lets us to fail and report the recursion depth limit error before ODL does.
                if (itemsStack.Count >= RecursionLimit)
                {
                    throw DataServiceException.CreateDeepRecursion(RecursionLimit);
                }

                switch (odataReader.State)
                {
                case ODataReaderState.EntryStart:
                    ODataEntry           entry           = (ODataEntry)odataReader.Item;
                    ODataEntryAnnotation entryAnnotation = null;
                    if (entry != null)
                    {
                        entryAnnotation = new ODataEntryAnnotation();
                        entry.SetAnnotation(entryAnnotation);
                    }

                    if (itemsStack.Count == 0)
                    {
                        Debug.Assert(entry != null, "The top-level entry can never be null.");
                        topLevelEntry = entry;

                        // For top-level entry, create the entry resource here.
                        // This is needed since the creation of the resource may fail (especially if this is an update in which case
                        // we evaluate the URL query, which may return no results and thus we would fail with 404)
                        // and we need to report that failure rather then potential other failures caused by the properties in the entry in question.
                        this.CreateEntityResource(topLevelSegmentInfo, entry, entryAnnotation, /*topLevel*/ true);
                    }
                    else
                    {
                        ODataItem parentItem = itemsStack.Peek();
                        ODataFeed parentFeed = parentItem as ODataFeed;
                        if (parentFeed != null)
                        {
                            ODataFeedAnnotation parentFeedAnnotation = parentFeed.GetAnnotation <ODataFeedAnnotation>();
                            Debug.Assert(parentFeedAnnotation != null, "Every feed we added to the stack should have the feed annotation on it.");
                            parentFeedAnnotation.Add(entry);
                        }
                        else
                        {
                            ODataNavigationLink           parentNavigationLink           = (ODataNavigationLink)parentItem;
                            ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                            Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                            Debug.Assert(parentNavigationLink.IsCollection == false, "Only singleton navigation properties can contain entry as their child.");
                            Debug.Assert(parentNavigationLinkAnnotation.Count == 0, "Each navigation property can contain only one entry as its direct child.");
                            parentNavigationLinkAnnotation.Add(entry);
                        }
                    }

                    itemsStack.Push(entry);
                    break;

                case ODataReaderState.EntryEnd:
                    Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The entry which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.NavigationLinkStart:
                    ODataNavigationLink navigationLink = (ODataNavigationLink)odataReader.Item;
                    Debug.Assert(navigationLink != null, "Navigation link should never be null.");

                    navigationLink.SetAnnotation(new ODataNavigationLinkAnnotation());
                    Debug.Assert(itemsStack.Count > 0, "Navigation link can't appear as top-level item.");
                    {
                        ODataEntry           parentEntry           = (ODataEntry)itemsStack.Peek();
                        ODataEntryAnnotation parentEntryAnnotation = parentEntry.GetAnnotation <ODataEntryAnnotation>();
                        Debug.Assert(parentEntryAnnotation != null, "Every entry we added to the stack should have the navigation link annotation on it.");
                        parentEntryAnnotation.Add(navigationLink);
                    }

                    itemsStack.Push(navigationLink);
                    break;

                case ODataReaderState.NavigationLinkEnd:
                    Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The navigation link which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.FeedStart:
                    ODataFeed feed = (ODataFeed)odataReader.Item;
                    Debug.Assert(feed != null, "Feed should never be null.");

                    feed.SetAnnotation(new ODataFeedAnnotation());
                    Debug.Assert(itemsStack.Count > 0, "Since we always start reading entry, we should never get a feed as the top-level item.");
                    {
                        ODataNavigationLink           parentNavigationLink           = (ODataNavigationLink)itemsStack.Peek();
                        ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                        Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                        Debug.Assert(parentNavigationLink.IsCollection == true, "Only collection navigation properties can contain feed as their child.");
                        parentNavigationLinkAnnotation.Add(feed);
                    }

                    itemsStack.Push(feed);
                    break;

                case ODataReaderState.FeedEnd:
                    Debug.Assert(itemsStack.Count > 0 && itemsStack.Peek() == odataReader.Item, "The feed which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.EntityReferenceLink:
                    ODataEntityReferenceLink entityReferenceLink = (ODataEntityReferenceLink)odataReader.Item;
                    Debug.Assert(entityReferenceLink != null, "Entity reference link should never be null.");

                    Debug.Assert(itemsStack.Count > 0, "Entity reference link should never be reported as top-level item.");
                    {
                        ODataNavigationLink           parentNavigationLink           = (ODataNavigationLink)itemsStack.Peek();
                        ODataNavigationLinkAnnotation parentNavigationLinkAnnotation = parentNavigationLink.GetAnnotation <ODataNavigationLinkAnnotation>();
                        Debug.Assert(parentNavigationLinkAnnotation != null, "Every navigation link we added to the stack should have the navigation link annotation on it.");

                        parentNavigationLinkAnnotation.Add(entityReferenceLink);
                    }

                    break;

                default:
                    Debug.Assert(false, "We should never get here, it means the ODataReader reported a wrong state.");
                    break;
                }
            }

            Debug.Assert(odataReader.State == ODataReaderState.Completed, "We should have consumed all of the input by now.");
            Debug.Assert(topLevelEntry != null, "A top level entry should have been read by now.");
            return(topLevelEntry);
        }
 /// <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);
 }
Example #40
0
        private void WriteFeed(object graph, ODataWriter writer, ODataSerializerContext writeContext)
        {
            IEnumerable enumerable = graph as IEnumerable; // Data to serialize

            if (enumerable != null)
            {
                ODataFeed feed = new ODataFeed();

                if (writeContext.EntitySet != null)
                {
                    IEntitySetLinkBuilder linkBuilder = SerializerProvider.EdmModel.GetEntitySetLinkBuilder(writeContext.EntitySet);
                    Uri feedSelfLink = linkBuilder.BuildFeedSelfLink(new FeedContext(writeContext.EntitySet, writeContext.UrlHelper, graph));
                    if (feedSelfLink != null)
                    {
                        feed.SetAnnotation(new AtomFeedMetadata()
                        {
                            SelfLink = new AtomLinkMetadata()
                            {
                                Relation = SelfLinkRelation, Href = feedSelfLink
                            }
                        });
                    }
                }

                // TODO: Bug 467590: remove the hardcoded feed id. Get support for it from the model builder ?
                feed.Id = FeedNamespace + _edmCollectionType.FullName();

                // If we have more OData format specific information apply it now.
                ODataResult odataFeedAnnotations = graph as ODataResult;
                if (odataFeedAnnotations != null)
                {
                    feed.Count        = odataFeedAnnotations.Count;
                    feed.NextPageLink = odataFeedAnnotations.NextPageLink;
                }
                else
                {
                    object             nextPageLinkPropertyValue;
                    HttpRequestMessage request = writeContext.Request;
                    if (request != null && request.Properties.TryGetValue(ODataQueryOptions.NextPageLinkPropertyKey, out nextPageLinkPropertyValue))
                    {
                        Uri nextPageLink = nextPageLinkPropertyValue as Uri;
                        if (nextPageLink != null)
                        {
                            feed.NextPageLink = nextPageLink;
                        }
                    }
                }

                writer.WriteStart(feed);

                foreach (object entry in enumerable)
                {
                    if (entry == null)
                    {
                        throw Error.NotSupported(SRResources.NullElementInCollection);
                    }

                    ODataSerializer entrySerializer = SerializerProvider.GetODataPayloadSerializer(entry.GetType());
                    if (entrySerializer == null)
                    {
                        throw Error.NotSupported(SRResources.TypeCannotBeSerialized, entry.GetType(), typeof(ODataMediaTypeFormatter).Name);
                    }

                    Contract.Assert(entrySerializer.ODataPayloadKind == ODataPayloadKind.Entry);

                    entrySerializer.WriteObjectInline(entry, writer, writeContext);
                }

                writer.WriteEnd();
            }
        }
        /// <summary>
        /// Start writing a feed.
        /// </summary>
        /// <param name="feed">The feed to write.</param>
        protected override void StartFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");

            if (this.ParentNavigationLink == null && this.writingParameter)
            {
                // Start array which will hold the entries in the feed.
                this.jsonWriter.StartArrayScope();
            }
            else if (this.ParentNavigationLink == null)
            {
                // Top-level feed.
                // "{"
                this.jsonWriter.StartObjectScope();

                // @odata.context
                this.jsonLightEntryAndFeedSerializer.WriteFeedContextUri(this.CurrentFeedScope.GetOrCreateTypeContext(this.jsonLightOutputContext.Model, this.jsonLightOutputContext.WritingResponse));

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // write "odata.actions" metadata
                    IEnumerable<ODataAction> actions = feed.Actions;
                    if (actions != null)
                    {
                        this.jsonLightEntryAndFeedSerializer.WriteOperations(actions.Cast<ODataOperation>(), /*isAction*/ true);
                    }

                    // write "odata.functions" metadata
                    IEnumerable<ODataFunction> functions = feed.Functions;
                    if (functions != null)
                    {
                        this.jsonLightEntryAndFeedSerializer.WriteOperations(functions.Cast<ODataOperation>(), /*isAction*/ false);
                    }

                    // Write the inline count if it's available.
                    this.WriteFeedCount(feed, /*propertyName*/null);

                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, /*propertyName*/null);

                    // Write the delta link if it's available.
                    this.WriteFeedDeltaLink(feed);
                }

                // Write custom instance annotations
                this.jsonLightEntryAndFeedSerializer.InstanceAnnotationWriter.WriteInstanceAnnotations(feed.InstanceAnnotations, this.CurrentFeedScope.InstanceAnnotationWriteTracker);

                // "value":
                this.jsonWriter.WriteValuePropertyName();

                // Start array which will hold the entries in the feed.
                this.jsonWriter.StartArrayScope();
            }
            else
            {
                // Expanded feed.
                Debug.Assert(
                    this.ParentNavigationLink != null && this.ParentNavigationLink.IsCollection.HasValue && this.ParentNavigationLink.IsCollection.Value,
                    "We should have verified that feeds can only be written into IsCollection = true links in requests.");
                string propertyName = this.ParentNavigationLink.Name;

                this.ValidateNoDeltaLinkForExpandedFeed(feed);
                this.ValidateNoCustomInstanceAnnotationsForExpandedFeed(feed);

                if (this.jsonLightOutputContext.WritingResponse)
                {
                    // Write the inline count if it's available.
                    this.WriteFeedCount(feed, propertyName);

                    // Write the next link if it's available.
                    this.WriteFeedNextLink(feed, propertyName);

                    // And then write the property name to start the value. 
                    this.jsonWriter.WriteName(propertyName);

                    // Start array which will hold the entries in the feed.
                    this.jsonWriter.StartArrayScope();
                }
                else
                {
                    JsonLightNavigationLinkScope navigationLinkScope = (JsonLightNavigationLinkScope)this.ParentNavigationLinkScope;
                    if (!navigationLinkScope.FeedWritten)
                    {
                        // Close the entity reference link array (if written)
                        if (navigationLinkScope.EntityReferenceLinkWritten)
                        {
                            this.jsonWriter.EndArrayScope();
                        }

                        // And then write the property name to start the value. 
                        this.jsonWriter.WriteName(propertyName);

                        // Start array which will hold the entries in the feed.
                        this.jsonWriter.StartArrayScope();

                        navigationLinkScope.FeedWritten = true;
                    }
                }
            }
        }
        private string WriteAndVerifyOrderFeed(StreamResponseMessage responseMessage, ODataWriter odataWriter,
                                               bool hasModel, string mimeType)
        {
            var orderFeed = new ODataFeed()
            {
                Id           = new Uri(this.ServiceUri + "Order"),
                NextPageLink = new Uri(this.ServiceUri + "Order?$skiptoken=-9"),
            };

            if (!hasModel)
            {
                orderFeed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo()
                {
                    NavigationSourceName = "Order", NavigationSourceEntityTypeName = NameSpace + "Order"
                });
            }

            odataWriter.WriteStart(orderFeed);

            var orderEntry1 = WritePayloadHelper.CreateOrderEntry1(hasModel);

            odataWriter.WriteStart(orderEntry1);

            var orderEntry1Navigation1 = new ODataNavigationLink()
            {
                Name         = "Customer",
                IsCollection = false,
                Url          = new Uri(this.ServiceUri + "Order(-10)/Customer")
            };

            odataWriter.WriteStart(orderEntry1Navigation1);
            odataWriter.WriteEnd();

            var orderEntry1Navigation2 = new ODataNavigationLink()
            {
                Name         = "Login",
                IsCollection = false,
                Url          = new Uri(this.ServiceUri + "Order(-10)/Login")
            };

            odataWriter.WriteStart(orderEntry1Navigation2);
            odataWriter.WriteEnd();

            // Finish writing orderEntry1.
            odataWriter.WriteEnd();

            var orderEntry2 = WritePayloadHelper.CreateOrderEntry2(hasModel);

            odataWriter.WriteStart(orderEntry2);

            var orderEntry2Navigation1 = new ODataNavigationLink()
            {
                Name         = "Customer",
                IsCollection = false,
                Url          = new Uri(this.ServiceUri + "Order(-9)/Customer")
            };

            odataWriter.WriteStart(orderEntry2Navigation1);
            odataWriter.WriteEnd();

            var orderEntry2Navigation2 = new ODataNavigationLink()
            {
                Name         = "Login",
                IsCollection = false,
                Url          = new Uri(this.ServiceUri + "Order(-9)/Login")
            };

            odataWriter.WriteStart(orderEntry2Navigation2);
            odataWriter.WriteEnd();

            // Finish writing orderEntry2.
            odataWriter.WriteEnd();

            // Finish writing the feed.
            odataWriter.WriteEnd();

            // Some very basic verification for the payload.
            bool verifyFeedCalled         = false;
            bool verifyEntryCalled        = false;
            bool verifyNavigationCalled   = false;
            Action <ODataFeed> verifyFeed = (feed) =>
            {
                Assert.IsNotNull(feed.NextPageLink, "feed.NextPageLink");
                verifyFeedCalled = true;
            };
            Action <ODataEntry> verifyEntry = (entry) =>
            {
                Assert.AreEqual(3, entry.Properties.Count(), "entry.Properties.Count");
                verifyEntryCalled = true;
            };
            Action <ODataNavigationLink> verifyNavigation = (navigation) =>
            {
                Assert.IsTrue(navigation.Name == "Customer" || navigation.Name == "Login", "navigation.Name");
                verifyNavigationCalled = true;
            };

            Stream stream = responseMessage.GetStream();

            if (!mimeType.Contains(MimeTypes.ODataParameterNoMetadata))
            {
                stream.Seek(0, SeekOrigin.Begin);
                WritePayloadHelper.ReadAndVerifyFeedEntryMessage(true, responseMessage, WritePayloadHelper.OrderSet, WritePayloadHelper.OrderType, verifyFeed,
                                                                 verifyEntry, verifyNavigation);
                Assert.IsTrue(verifyFeedCalled && verifyEntryCalled && verifyNavigationCalled,
                              "Verification action not called.");
            }

            return(WritePayloadHelper.ReadStreamContent(stream));
        }
 /// <summary>
 /// Create a new feed scope.
 /// </summary>
 /// <param name="feed">The feed for the new scope.</param>
 /// <param name="navigationSource">The navigation source we are going to write entities for.</param>
 /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param>
 /// <param name="skipWriting">true if the content of the scope to create should not be written.</param>
 /// <param name="selectedProperties">The selected properties of this scope.</param>
 /// <param name="odataUri">The ODataUri info of this scope.</param>
 /// <returns>The newly create scope.</returns>
 protected override FeedScope CreateFeedScope(ODataFeed feed, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
 {
     return new JsonLightFeedScope(feed, navigationSource, entityType, skipWriting, selectedProperties, odataUri);
 }
        private string WriteAndVerifyExpandedCustomerEntry(StreamResponseMessage responseMessage,
                                                           ODataWriter odataWriter, bool hasModel, string mimeType)
        {
            ODataEntry customerEntry = WritePayloadHelper.CreateCustomerEntry(hasModel);

            odataWriter.WriteStart(customerEntry);

            // write non-expanded navigations
            foreach (var navigation in WritePayloadHelper.CreateCustomerNavigationLinks())
            {
                odataWriter.WriteStart(navigation);
                odataWriter.WriteEnd();
            }

            // write expanded navigation
            var expandedNavigation = new ODataNavigationLink()
            {
                Name         = "Logins",
                IsCollection = true,
                Url          = new Uri(this.ServiceUri + "Customer(-9)/Logins")
            };

            odataWriter.WriteStart(expandedNavigation);

            var loginFeed = new ODataFeed()
            {
                Id = new Uri(this.ServiceUri + "Customer(-9)/Logins")
            };

            if (!hasModel)
            {
                loginFeed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo()
                {
                    NavigationSourceName = "Login", NavigationSourceEntityTypeName = NameSpace + "Login"
                });
            }

            odataWriter.WriteStart(loginFeed);

            var loginEntry = WritePayloadHelper.CreateLoginEntry(hasModel);

            odataWriter.WriteStart(loginEntry);

            foreach (var navigation in WritePayloadHelper.CreateLoginNavigationLinks())
            {
                odataWriter.WriteStart(navigation);
                odataWriter.WriteEnd();
            }

            // Finish writing loginEntry.
            odataWriter.WriteEnd();

            // Finish writing the loginFeed.
            odataWriter.WriteEnd();

            // Finish writing expandedNavigation.
            odataWriter.WriteEnd();

            // Finish writing customerEntry.
            odataWriter.WriteEnd();

            // Some very basic verification for the payload.
            bool verifyFeedCalled         = false;
            int  verifyEntryCalled        = 0;
            bool verifyNavigationCalled   = false;
            Action <ODataFeed> verifyFeed = (feed) =>
            {
                verifyFeedCalled = true;
            };

            Action <ODataEntry> verifyEntry = (entry) =>
            {
                if (entry.TypeName.Contains("Customer"))
                {
                    Assert.AreEqual(7, entry.Properties.Count());
                }

                if (entry.TypeName.Contains("Login"))
                {
                    Assert.AreEqual(2, entry.Properties.Count());
                }

                verifyEntryCalled++;
            };

            Action <ODataNavigationLink> verifyNavigation = (navigation) =>
            {
                Assert.IsNotNull(navigation.Name);
                verifyNavigationCalled = true;
            };

            Stream stream = responseMessage.GetStream();

            if (!mimeType.Contains(MimeTypes.ODataParameterNoMetadata))
            {
                stream.Seek(0, SeekOrigin.Begin);
                WritePayloadHelper.ReadAndVerifyFeedEntryMessage(false, responseMessage, WritePayloadHelper.CustomerSet, WritePayloadHelper.CustomerType,
                                                                 verifyFeed, verifyEntry, verifyNavigation);
                Assert.IsTrue(verifyFeedCalled && verifyEntryCalled == 2 && verifyNavigationCalled,
                              "Verification action not called.");
            }

            return(WritePayloadHelper.ReadStreamContent(stream));
        }
        /// <summary>
        /// Writes the odata.nextLink annotation for a feed if it has not been written yet (and the next link is specified on the feed).
        /// </summary>
        /// <param name="feed">The feed to write the next link for.</param>
        /// <param name="propertyName">The name of the expanded nav property or null for a top-level feed.</param>
        private void WriteFeedNextLink(ODataFeed feed, string propertyName)
        {
            Debug.Assert(feed != null, "feed != null");

            // If we haven't written the next link yet and it's available, write it.
            Uri nextPageLink = feed.NextPageLink;
            if (nextPageLink != null && !this.CurrentFeedScope.NextPageLinkWritten)
            {
                if (propertyName == null)
                {
                    this.odataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataNextLink);
                }
                else
                {
                    this.odataAnnotationWriter.WritePropertyAnnotationName(propertyName, ODataAnnotationNames.ODataNextLink);
                }

                this.jsonWriter.WriteValue(this.jsonLightEntryAndFeedSerializer.UriToString(nextPageLink));
                this.CurrentFeedScope.NextPageLinkWritten = true;
            }
        }
        private string WriteAndVerifyPersonFeed(StreamResponseMessage responseMessage, ODataWriter odataWriter,
                                                bool hasModel, string mimeType)
        {
            var personFeed = new ODataFeed()
            {
                Id        = new Uri(this.ServiceUri + "Person"),
                DeltaLink = new Uri(this.ServiceUri + "Person")
            };

            if (!hasModel)
            {
                personFeed.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo()
                {
                    NavigationSourceName = "Person", NavigationSourceEntityTypeName = NameSpace + "Person"
                });
            }

            odataWriter.WriteStart(personFeed);

            ODataEntry personEntry = WritePayloadHelper.CreatePersonEntry(hasModel);

            odataWriter.WriteStart(personEntry);

            var personNavigation = new ODataNavigationLink()
            {
                Name         = "PersonMetadata",
                IsCollection = true,
                Url          = new Uri("Person(-5)/PersonMetadata", UriKind.Relative)
            };

            odataWriter.WriteStart(personNavigation);
            odataWriter.WriteEnd();

            // Finish writing personEntry.
            odataWriter.WriteEnd();

            ODataEntry employeeEntry = WritePayloadHelper.CreateEmployeeEntry(hasModel);

            odataWriter.WriteStart(employeeEntry);

            var employeeNavigation1 = new ODataNavigationLink()
            {
                Name         = "PersonMetadata",
                IsCollection = true,
                Url          = new Uri("Person(-3)/" + NameSpace + "Employee" + "/PersonMetadata", UriKind.Relative)
            };

            odataWriter.WriteStart(employeeNavigation1);
            odataWriter.WriteEnd();

            var employeeNavigation2 = new ODataNavigationLink()
            {
                Name         = "Manager",
                IsCollection = false,
                Url          = new Uri("Person(-3)/" + NameSpace + "Employee" + "/Manager", UriKind.Relative)
            };

            odataWriter.WriteStart(employeeNavigation2);
            odataWriter.WriteEnd();

            // Finish writing employeeEntry.
            odataWriter.WriteEnd();

            ODataEntry specialEmployeeEntry = WritePayloadHelper.CreateSpecialEmployeeEntry(hasModel);

            odataWriter.WriteStart(specialEmployeeEntry);

            var specialEmployeeNavigation1 = new ODataNavigationLink()
            {
                Name         = "PersonMetadata",
                IsCollection = true,
                Url          = new Uri("Person(-10)/" + NameSpace + "SpecialEmployee" + "/PersonMetadata", UriKind.Relative)
            };

            odataWriter.WriteStart(specialEmployeeNavigation1);
            odataWriter.WriteEnd();

            var specialEmployeeNavigation2 = new ODataNavigationLink()
            {
                Name         = "Manager",
                IsCollection = false,
                Url          = new Uri("Person(-10)/" + NameSpace + "SpecialEmployee" + "/Manager", UriKind.Relative)
            };

            odataWriter.WriteStart(specialEmployeeNavigation2);
            odataWriter.WriteEnd();

            var specialEmployeeNavigation3 = new ODataNavigationLink()
            {
                Name         = "Car",
                IsCollection = false,
                Url          = new Uri("Person(-10)/" + NameSpace + "SpecialEmployee" + "/Manager", UriKind.Relative)
            };

            odataWriter.WriteStart(specialEmployeeNavigation3);
            odataWriter.WriteEnd();

            // Finish writing specialEmployeeEntry.
            odataWriter.WriteEnd();

            // Finish writing the feed.
            odataWriter.WriteEnd();

            // Some very basic verification for the payload.
            bool verifyFeedCalled         = false;
            bool verifyEntryCalled        = false;
            bool verifyNavigationCalled   = false;
            Action <ODataFeed> verifyFeed = (feed) =>
            {
                if (mimeType != MimeTypes.ApplicationAtomXml)
                {
                    Assert.IsTrue(feed.DeltaLink.AbsoluteUri.Contains("Person"));
                }
                verifyFeedCalled = true;
            };
            Action <ODataEntry> verifyEntry = (entry) =>
            {
                Assert.IsTrue(entry.EditLink.AbsoluteUri.EndsWith("Person(-5)") ||
                              entry.EditLink.AbsoluteUri.EndsWith("Person(-3)/" + NameSpace + "Employee") ||
                              entry.EditLink.AbsoluteUri.EndsWith("Person(-10)/" + NameSpace + "SpecialEmployee"));
                verifyEntryCalled = true;
            };
            Action <ODataNavigationLink> verifyNavigation = (navigation) =>
            {
                Assert.IsTrue(navigation.Name == "PersonMetadata" || navigation.Name == "Manager" || navigation.Name == "Car");
                verifyNavigationCalled = true;
            };

            Stream stream = responseMessage.GetStream();

            if (!mimeType.Contains(MimeTypes.ODataParameterNoMetadata))
            {
                stream.Seek(0, SeekOrigin.Begin);
                WritePayloadHelper.ReadAndVerifyFeedEntryMessage(true, responseMessage, WritePayloadHelper.PersonSet, WritePayloadHelper.PersonType, verifyFeed,
                                                                 verifyEntry, verifyNavigation);
                Assert.IsTrue(verifyFeedCalled && verifyEntryCalled && verifyNavigationCalled,
                              "Verification action not called.");
            }

            return(WritePayloadHelper.ReadStreamContent(stream));
        }
        private void ValidateNoCustomInstanceAnnotationsForExpandedFeed(ODataFeed feed)
        {
            Debug.Assert(feed != null, "feed != null");
            Debug.Assert(
                this.ParentNavigationLink != null && this.ParentNavigationLink.IsCollection.HasValue && this.ParentNavigationLink.IsCollection.Value == true,
                "This should only be called when writing an expanded feed.");

            if (feed.InstanceAnnotations.Count > 0)
            {
                throw new ODataException(OData.Core.Strings.ODataJsonLightWriter_InstanceAnnotationNotSupportedOnExpandedFeed);
            }
        }
        /// <summary>
        /// Reads an ODataFeed or an ODataItem from the reader.
        /// </summary>
        /// <param name="reader">The OData reader to read from.</param>
        /// <returns>The read feed or entry.</returns>
        public static ODataItemBase ReadEntryOrFeed(ODataReader reader)
        {
            if (reader == null)
            {
                throw Error.ArgumentNull("odataReader");
            }

            ODataItemBase         topLevelItem = null;
            Stack <ODataItemBase> itemsStack   = new Stack <ODataItemBase>();

            while (reader.Read())
            {
                switch (reader.State)
                {
                case ODataReaderState.EntryStart:
                    ODataEntry entry = (ODataEntry)reader.Item;
                    ODataEntryWithNavigationLinks entryWrapper = null;
                    if (entry != null)
                    {
                        entryWrapper = new ODataEntryWithNavigationLinks(entry);
                    }

                    if (itemsStack.Count == 0)
                    {
                        Contract.Assert(entry != null, "The top-level entry can never be null.");
                        topLevelItem = entryWrapper;
                    }
                    else
                    {
                        ODataItemBase        parentItem = itemsStack.Peek();
                        ODataFeedWithEntries parentFeed = parentItem as ODataFeedWithEntries;
                        if (parentFeed != null)
                        {
                            parentFeed.Entries.Add(entryWrapper);
                        }
                        else
                        {
                            ODataNavigationLinkWithItems parentNavigationLink = (ODataNavigationLinkWithItems)parentItem;
                            Contract.Assert(parentNavigationLink.NavigationLink.IsCollection == false, "Only singleton navigation properties can contain entry as their child.");
                            Contract.Assert(parentNavigationLink.NestedItems.Count == 0, "Each navigation property can contain only one entry as its direct child.");
                            parentNavigationLink.NestedItems.Add(entryWrapper);
                        }
                    }
                    itemsStack.Push(entryWrapper);
                    break;

                case ODataReaderState.EntryEnd:
                    Contract.Assert(itemsStack.Count > 0 && (reader.Item == null || itemsStack.Peek().Item == reader.Item), "The entry which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.NavigationLinkStart:
                    ODataNavigationLink navigationLink = (ODataNavigationLink)reader.Item;
                    Contract.Assert(navigationLink != null, "Navigation link should never be null.");

                    ODataNavigationLinkWithItems navigationLinkWrapper = new ODataNavigationLinkWithItems(navigationLink);
                    Contract.Assert(itemsStack.Count > 0, "Navigation link can't appear as top-level item.");
                    {
                        ODataEntryWithNavigationLinks parentEntry = (ODataEntryWithNavigationLinks)itemsStack.Peek();
                        parentEntry.NavigationLinks.Add(navigationLinkWrapper);
                    }

                    itemsStack.Push(navigationLinkWrapper);
                    break;

                case ODataReaderState.NavigationLinkEnd:
                    Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek().Item == reader.Item, "The navigation link which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.FeedStart:
                    ODataFeed feed = (ODataFeed)reader.Item;
                    Contract.Assert(feed != null, "Feed should never be null.");

                    ODataFeedWithEntries feedWrapper = new ODataFeedWithEntries(feed);
                    if (itemsStack.Count > 0)
                    {
                        ODataNavigationLinkWithItems parentNavigationLink = (ODataNavigationLinkWithItems)itemsStack.Peek();
                        Contract.Assert(parentNavigationLink != null, "this has to be an inner feed. inner feeds always have a navigation link.");
                        Contract.Assert(parentNavigationLink.NavigationLink.IsCollection == true, "Only collection navigation properties can contain feed as their child.");
                        parentNavigationLink.NestedItems.Add(feedWrapper);
                    }
                    else
                    {
                        topLevelItem = feedWrapper;
                    }

                    itemsStack.Push(feedWrapper);
                    break;

                case ODataReaderState.FeedEnd:
                    Contract.Assert(itemsStack.Count > 0 && itemsStack.Peek().Item == reader.Item, "The feed which is ending should be on the top of the items stack.");
                    itemsStack.Pop();
                    break;

                case ODataReaderState.EntityReferenceLink:
                    ODataEntityReferenceLink entityReferenceLink = (ODataEntityReferenceLink)reader.Item;
                    Contract.Assert(entityReferenceLink != null, "Entity reference link should never be null.");
                    ODataEntityReferenceLinkBase entityReferenceLinkWrapper = new ODataEntityReferenceLinkBase(entityReferenceLink);

                    Contract.Assert(itemsStack.Count > 0, "Entity reference link should never be reported as top-level item.");
                    {
                        ODataNavigationLinkWithItems parentNavigationLink = (ODataNavigationLinkWithItems)itemsStack.Peek();
                        parentNavigationLink.NestedItems.Add(entityReferenceLinkWrapper);
                    }

                    break;

                default:
                    Contract.Assert(false, "We should never get here, it means the ODataReader reported a wrong state.");
                    break;
                }
            }

            Contract.Assert(reader.State == ODataReaderState.Completed, "We should have consumed all of the input by now.");
            Contract.Assert(topLevelItem != null, "A top level entry or feed should have been read by now.");
            return(topLevelItem);
        }
 protected HttpResponseMessage GenerateAsyncOperationScheduled()
 {
     var operations = new ODataFeed<AsyncOperation>();
     operations.Feed = new List<AsyncOperation>
     {
         new AsyncOperation
         {
             BatchID = GetId(10),
             BatchSourceID = GetId(),
             BatchProgress = 0,
             BatchState = AsyncOperationState.Scheduled
         }
     };
     return new HttpResponseMessage(HttpStatusCode.Accepted)
     {
         Content = new StringContent(JsonConvert.SerializeObject(operations), Encoding.UTF8, "application/json")
     };
 }
Example #50
0
        private void Read(Lazy <ODataReader> lazyReader)
        {
            foreach (var state in this.expectedStates)
            {
                lazyReader.Value.Read();
                ExceptionUtilities.Assert(lazyReader.Value.State == state, "Expected %1, Found %2", state, lazyReader.Value.State);
                switch (state)
                {
                case ODataReaderState.FeedStart:
                    if (readItems.Count > 0)
                    {
                        ODataNavigationLink navLink = (ODataNavigationLink)readItems.Peek();
                        var annotation = navLink.GetAnnotation <ODataNavigationLinkExpandedItemObjectModelAnnotation>();
                        if (annotation == null)
                        {
                            annotation = new ODataNavigationLinkExpandedItemObjectModelAnnotation();
                            navLink.SetAnnotation(annotation);
                        }

                        annotation.ExpandedItem = lazyReader.Value.Item;
                    }

                    readItems.Push(lazyReader.Value.Item);
                    break;

                case ODataReaderState.EntryStart:
                    var currentEntry = (ODataEntry)lazyReader.Value.Item;
                    if (readItems.Count > 0)
                    {
                        ODataFeed feed = readItems.Peek() as ODataFeed;
                        if (feed != null)
                        {
                            var annotation = feed.GetAnnotation <ODataFeedEntriesObjectModelAnnotation>();
                            if (annotation == null)
                            {
                                annotation = new ODataFeedEntriesObjectModelAnnotation();
                                feed.SetAnnotation(annotation);
                            }
                            annotation.Add((ODataEntry)lazyReader.Value.Item);
                        }
                        else
                        {
                            ODataNavigationLink navLink = (ODataNavigationLink)readItems.Peek();
                            var annotation = navLink.GetAnnotation <ODataNavigationLinkExpandedItemObjectModelAnnotation>();
                            if (annotation == null)
                            {
                                annotation = new ODataNavigationLinkExpandedItemObjectModelAnnotation();
                                navLink.SetAnnotation(annotation);
                            }

                            annotation.ExpandedItem = currentEntry;
                        }
                    }

                    readItems.Push(currentEntry);
                    break;

                case ODataReaderState.NavigationLinkStart:
                    ODataEntry entry = (ODataEntry)readItems.Peek();
                    var        navLinksAnnotation = entry.GetAnnotation <ODataEntryNavigationLinksObjectModelAnnotation>();
                    if (navLinksAnnotation == null)
                    {
                        navLinksAnnotation = new ODataEntryNavigationLinksObjectModelAnnotation();
                        entry.SetAnnotation(navLinksAnnotation);
                    }

                    navLinksAnnotation.Add((ODataNavigationLink)lazyReader.Value.Item, entry.Properties.Count() + navLinksAnnotation.Count);
                    readItems.Push(lazyReader.Value.Item);
                    break;

                case ODataReaderState.EntryEnd:
                case ODataReaderState.FeedEnd:
                case ODataReaderState.NavigationLinkEnd:
                    if (readItems.Count() > 1)
                    {
                        readItems.Pop();
                    }
                    break;
                }
            }

            this.expectedStates.Clear();
        }
        public void WritingNestedInlinecountTest()
        {
            ODataFeed feed = new ODataFeed { Count = 1 };

            ODataItem[] itemsToWrite = new ODataItem[]
            {
                new ODataFeed(), 
                this.entryWithOnlyData1,
                this.containedCollectionNavLink,
                feed
            };

            string resourcePath = "EntitySet";
            string result = this.GetWriterOutputForContentTypeAndKnobValue("application/json;odata.metadata=minimal", true, itemsToWrite, Model, EntitySet, EntityType, null, null, resourcePath);

            string expectedPayload = "{" +
                                            "\"@odata.context\":\"http://example.org/odata.svc/$metadata#EntitySet\"," +
                                            "\"value\":[" +
                                                "{" +
                                                    "\"ID\":101,\"Name\":\"Alice\"," +
                                                    "\"[email protected]\":\"http://example.org/odata.svc/$metadata#EntitySet(101)/ContainedCollectionNavProp\"," +
                                                    "\"[email protected]\":\"http://example.org/odata.svc/navigation\"," +
                                                    "\"[email protected]\":1," +
                                                    "\"ContainedCollectionNavProp\":[]" +
                                                "}" +
                                            "]" +
                                        "}";
            result.Should().Be(expectedPayload);
        }
Example #52
0
        private void WriteTopLevelFeed(ODataMessageWriterTestWrapper messageWriter, ODataMessageReaderTestWrapper messageReader, ODataFeed feed)
        {
            var feedWriter = messageWriter.CreateODataFeedWriter();
            Lazy <ODataReader> lazyReader = new Lazy <ODataReader>(() => messageReader.CreateODataFeedReader());

            this.WriteFeed(feedWriter, lazyReader, feed);
        }
        /// <summary>
        /// Creates a new instance of ODataAction or ODataFunction for the <paramref name="metadataReferencePropertyName"/>.
        /// </summary>
        /// <param name="feed">The feed to add the action or function .</param>
        /// <param name="metadataReferencePropertyName">The name of the metadata reference property being read.</param>
        /// <returns>A new instance of ODataAction or ODataFunction for the <paramref name="metadataReferencePropertyName"/>.</returns>
        private ODataOperation CreateODataOperationAndAddToFeed(ODataFeed feed, string metadataReferencePropertyName)
        {
            string fullyQualifiedOperationName = ODataJsonLightUtils.GetUriFragmentFromMetadataReferencePropertyName(this.ContextUriParseResult.MetadataDocumentUri, metadataReferencePropertyName);
            IEdmOperation firstActionOrFunction = this.JsonLightInputContext.Model.ResolveOperations(fullyQualifiedOperationName).FirstOrDefault();

            bool isAction;

            if (firstActionOrFunction == null)
            {
                // Ignore the unknown function/action.
                return null;
            }

            var operation = ODataJsonLightUtils.CreateODataOperation(this.ContextUriParseResult.MetadataDocumentUri, metadataReferencePropertyName, firstActionOrFunction, out isAction);

            if (isAction)
            {
                feed.AddAction((ODataAction)operation);
            }
            else
            {
                feed.AddFunction((ODataFunction)operation);
            }

            return operation;
        }
Example #54
0
 /// <summary>
 /// Initializes a new instance of <see cref="ODataFeedWithEntries"/>.
 /// </summary>
 /// <param name="item">The wrapped item.</param>
 public ODataFeedWithEntries(ODataFeed item)
     : base(item)
 {
     Entries = new List <ODataEntryWithNavigationLinks>();
 }
        /// <summary>
        /// Reads expanded feed navigation link.
        /// </summary>
        /// <param name="entryState">The state of the reader for entry to read.</param>
        /// <param name="navigationProperty">The navigation property for which to read the expanded link.</param>
        /// <returns>The navigation link info for the expanded link read.</returns>
        /// <remarks>
        /// This method doesn't move the reader.
        /// </remarks>
        private static ODataJsonLightReaderNavigationLinkInfo ReadExpandedFeedNavigationLink(IODataJsonLightReaderEntryState entryState, IEdmNavigationProperty navigationProperty)
        {
            Debug.Assert(entryState != null, "entryState != null");
            Debug.Assert(navigationProperty != null, "navigationProperty != null");

            ODataNavigationLink navigationLink = new ODataNavigationLink()
            {
                Name = navigationProperty.Name,
                IsCollection = true
            };

            ODataFeed expandedFeed = new ODataFeed();

            Dictionary<string, object> propertyAnnotations = entryState.DuplicatePropertyNamesChecker.GetODataPropertyAnnotations(navigationLink.Name);
            if (propertyAnnotations != null)
            {
                foreach (KeyValuePair<string, object> propertyAnnotation in propertyAnnotations)
                {
                    switch (propertyAnnotation.Key)
                    {
                        case ODataAnnotationNames.ODataNavigationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.navigationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.Url = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataAssociationLinkUrl:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.associationLinkUrl annotation should have been parsed as a non-null Uri.");
                            navigationLink.AssociationLinkUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataNextLink:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.nextLink annotation should have been parsed as a non-null Uri.");
                            expandedFeed.NextPageLink = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataCount:
                            Debug.Assert(propertyAnnotation.Value is long && propertyAnnotation.Value != null, "The odata.count annotation should have been parsed as a non-null long.");
                            expandedFeed.Count = (long?)propertyAnnotation.Value;
                            break;
                        case ODataAnnotationNames.ODataContext:
                            Debug.Assert(propertyAnnotation.Value is Uri && propertyAnnotation.Value != null, "The odata.context annotation should have been parsed as a non-null Uri.");
                            navigationLink.ContextUrl = (Uri)propertyAnnotation.Value;
                            break;

                        case ODataAnnotationNames.ODataDeltaLink:   // Delta links are not supported on expanded feeds.
                        default:
                            throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_UnexpectedExpandedCollectionNavigationLinkPropertyAnnotation(navigationLink.Name, propertyAnnotation.Key));
                    }
                }
            }

            return ODataJsonLightReaderNavigationLinkInfo.CreateExpandedFeedLinkInfo(navigationLink, navigationProperty, expandedFeed);
        }
        /// <summary>
        /// Create the <see cref="ODataFeed"/> to be written for the given feed instance.
        /// </summary>
        /// <param name="feedInstance">The instance representing the feed being written.</param>
        /// <param name="feedType">The EDM type of the feed being written.</param>
        /// <param name="writeContext">The serializer context.</param>
        /// <returns>The created <see cref="ODataFeed"/> object.</returns>
        public virtual ODataFeed CreateODataFeed(IEnumerable feedInstance, IEdmCollectionTypeReference feedType,
                                                 ODataSerializerContext writeContext)
        {
            ODataFeed feed = new ODataFeed();

            if (writeContext.EntitySet != null)
            {
                IEdmModel model = writeContext.Model;
                EntitySetLinkBuilderAnnotation linkBuilder = model.GetEntitySetLinkBuilder(writeContext.EntitySet);
                FeedContext feedContext = new FeedContext
                {
                    Request        = writeContext.Request,
                    RequestContext = writeContext.RequestContext,
                    EntitySet      = writeContext.EntitySet,
                    Url            = writeContext.Url,
                    FeedInstance   = feedInstance
                };

                Uri feedSelfLink = linkBuilder.BuildFeedSelfLink(feedContext);
                if (feedSelfLink != null)
                {
                    feed.SetAnnotation(new AtomFeedMetadata()
                    {
                        SelfLink = new AtomLinkMetadata()
                        {
                            Relation = "self", Href = feedSelfLink
                        }
                    });
                }
            }

            // TODO: Bug 467590: remove the hardcoded feed id. Get support for it from the model builder ?
            feed.Id = "http://schemas.datacontract.org/2004/07/" + feedType.FullName();

            if (writeContext.ExpandedEntity == null)
            {
                // If we have more OData format specific information apply it now, only if we are the root feed.
                PageResult odataFeedAnnotations = feedInstance as PageResult;
                if (odataFeedAnnotations != null)
                {
                    feed.Count        = odataFeedAnnotations.Count;
                    feed.NextPageLink = odataFeedAnnotations.NextPageLink;
                }
                else if (writeContext.Request != null)
                {
                    feed.NextPageLink = writeContext.Request.ODataProperties().NextLink;

                    long?inlineCount = writeContext.Request.ODataProperties().TotalCount;
                    if (inlineCount.HasValue)
                    {
                        feed.Count = inlineCount.Value;
                    }
                }
            }
            else
            {
                // nested feed
                ITruncatedCollection truncatedCollection = feedInstance as ITruncatedCollection;
                if (truncatedCollection != null && truncatedCollection.IsTruncated)
                {
                    feed.NextPageLink = GetNestedNextPageLink(writeContext, truncatedCollection.PageSize);
                }
            }

            return(feed);
        }
        /// <summary>
        /// Writes the next page link for a feed.
        /// </summary>
        /// <param name="feed">The feed to write the next page link for.</param>
        internal void WriteFeedNextPageLink(ODataFeed feed)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(feed != null, "feed != null");

            Uri nextPageLink = feed.NextPageLink;
            if (nextPageLink != null)
            {
                // <atom:link rel="next" href="next-page-link" />
                AtomFeedMetadata feedMetadata = feed.GetAnnotation<AtomFeedMetadata>();
                AtomLinkMetadata mergedLink = ODataAtomWriterMetadataUtils.MergeLinkMetadata(
                    feedMetadata == null ? null : feedMetadata.NextPageLink,
                    AtomConstants.AtomNextRelationAttributeValue,
                    nextPageLink,
                    null, /*title*/
                    null /*mediaType*/);
                this.atomFeedMetadataSerializer.WriteAtomLink(mergedLink, null);
            }
        }
 public override void WriteStart(ODataFeed feed)
 {
 }
 protected override ODataWriterCore.FeedScope CreateFeedScope(ODataFeed feed, IEdmNavigationSource navigationSource, IEdmEntityType entityType, bool skipWriting, SelectedPropertiesNode selectedProperties, ODataUri odataUri)
 {
     throw new NotImplementedException();
 }
 public override Task WriteStartAsync(ODataFeed feed)
 {
     throw new InternalTestFailureException("Should not hit this code");
 }