public void ReadLinkShouldNotBeOmittedWhenNotIdenticalToEditLink()
 {
     DateTimeOffset updatedTime = DateTimeOffset.UtcNow;
     var entry = new ODataEntry
     {
         Id = new Uri("http://test.org/EntitySet('1')"),
         EditLink = new Uri("http://test.org/EntitySet('1')/edit"),
         ReadLink = new Uri("http://test.org/EntitySet('1')/read")
     };
     entry.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime });
     string actual = this.WriteAtomEntry(entry);
     string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
         "<entry 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://temp.org/$metadata#EntitySet/$entity\">" +
             "<id>http://test.org/EntitySet('1')</id>" +
             "<link rel=\"edit\" href=\"http://test.org/EntitySet('1')/edit\" />" +
             "<link rel=\"self\" href=\"http://test.org/EntitySet('1')/read\" />" +
             "<title />" +
             "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
             "<author>" +
                 "<name />" +
             "</author>" +
             "<content type=\"application/xml\" />" +
         "</entry>";
     Assert.AreEqual(expected, actual);
 }
Example #2
0
        /// <summary>
        /// Creates a new ODataEntry from the specified entity set, instance, and type.
        /// </summary>
        /// <param name="entitySet">Entity set for the new entry.</param>
        /// <param name="value">Entity instance for the new entry.</param>
        /// <param name="entityType">Entity type for the new entry.</param>
        /// <returns>New ODataEntry with the specified entity set and type, property values from the specified instance.</returns>
        internal static ODataEntry CreateODataEntry(IEdmEntitySet entitySet, IEdmStructuredValue value, IEdmEntityType entityType)
        {
            var entry = new ODataEntry();
            entry.SetAnnotation(new ODataTypeAnnotation(entitySet, entityType));
            entry.Properties = value.PropertyValues.Select(p =>
            {
                object propertyValue;
                if (p.Value.ValueKind == EdmValueKind.Null)
                {
                    propertyValue = null;
                }
                else if (p.Value is IEdmPrimitiveValue)
                {
                    propertyValue = ((IEdmPrimitiveValue)p.Value).ToClrValue();
                }
                else
                {
                    Assert.Fail("Test only currently supports creating ODataEntry from IEdmPrimitiveValue instances.");
                    return null;
                }

                return new ODataProperty() { Name = p.Name, Value = propertyValue };
            });

            return entry;
        }
        private static ODataEntry CreateEntryWithKeyAsSegmentConvention(bool addAnnotation, bool? useKeyAsSegment)
        {
            var model = new EdmModel();
            var container = new EdmEntityContainer("Fake", "Container");
            model.AddElement(container);
            if (addAnnotation)
            {
                model.AddVocabularyAnnotation(new EdmAnnotation(container, UrlConventionsConstants.ConventionTerm, UrlConventionsConstants.KeyAsSegmentAnnotationValue));                
            }
            
            EdmEntityType entityType = new EdmEntityType("Fake", "FakeType");
            entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
            model.AddElement(entityType);

            var entitySet = new EdmEntitySet(container, "FakeSet", entityType);
            container.AddElement(entitySet);

            var metadataContext = new ODataMetadataContext(
                true,
                ODataReaderBehavior.DefaultBehavior.OperationsBoundToEntityTypeMustBeContainerQualified,
                new EdmTypeReaderResolver(model, ODataReaderBehavior.DefaultBehavior),
                model,
                new Uri("http://temp.org/$metadata"),
                null /*requestUri*/);

            var thing = new ODataEntry {Properties = new[] {new ODataProperty {Name = "Id", Value = 1}}};
            thing.SetAnnotation(new ODataTypeAnnotation(entitySet, entityType));
            thing.MetadataBuilder = metadataContext.GetEntityMetadataBuilderForReader(new TestJsonLightReaderEntryState { Entry = thing, SelectedProperties = new SelectedPropertiesNode("*")}, useKeyAsSegment);
            return thing;
        }
        internal void SetTypeName(ODataEntry entry, string entitySetBaseTypeName, string entryTypeName)
        {
            Debug.Assert(entry != null, "entry != null");

            // We should always write this since for derived types, ODL needs to know the typename.
            entry.TypeName = entryTypeName;

            if (this.interpreter.ShouldIncludeEntryTypeName(entitySetBaseTypeName, entryTypeName))
            {
                entry.SetAnnotation(new SerializationTypeNameAnnotation() { TypeName = entry.TypeName });
            }
            else
            {
                // When we should not write the typename, setting the serialization type name to null
                // so that ODL does not write the type on the wire.
                entry.SetAnnotation(new SerializationTypeNameAnnotation() { TypeName = null });
            }
        }
        internal static IEdmValue CreateStructuredEdmValue(ODataEntry entry, IEdmEntitySet entitySet, IEdmEntityTypeReference entityType)
        {
            if (entitySet != null)
            {
                object typeAnnotation = ReflectionUtils.CreateInstance(
                    odataTypeAnnotationType,
                    new Type[] { typeof(IEdmEntitySet), typeof(IEdmEntityTypeReference) },
                    entitySet, entityType);
                entry.SetAnnotation(typeAnnotation);
            }

            return (IEdmValue)ReflectionUtils.CreateInstance(
                odataEdmStructuredValueType, 
                new Type[] { typeof(ODataEntry) },
                entry);
        }
        /// <summary>
        /// If an entity type name is found in the payload this method is called to apply it to the current scope.
        /// This method should be called even if the type name was not found in which case a null should be passed in.
        /// The method validates that some type will be available as the current entity type after it returns (if we are parsing using metadata).
        /// </summary>
        /// <param name="entityTypeNameFromPayload">The entity type name found in the payload or null if no type was specified in the payload.</param>
        protected void ApplyEntityTypeNameFromPayload(string entityTypeNameFromPayload)
        {
            Debug.Assert(
                this.scopes.Count > 0 && this.scopes.Peek().Item is ODataEntry,
                "Entity type can be applied only when in entry scope.");

            SerializationTypeNameAnnotation serializationTypeNameAnnotation;
            EdmTypeKind             targetTypeKind;
            IEdmEntityTypeReference targetEntityTypeReference =
                (IEdmEntityTypeReference)ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                    EdmTypeKind.Entity,
                    /*defaultPrimitivePayloadType*/ null,
                    this.CurrentEntityType.ToTypeReference(),
                    entityTypeNameFromPayload,
                    this.inputContext.Model,
                    this.inputContext.MessageReaderSettings,
                    this.inputContext.Version,
                    () => EdmTypeKind.Entity,
                    out targetTypeKind,
                    out serializationTypeNameAnnotation);

            IEdmEntityType targetEntityType = null;
            ODataEntry     entry            = this.CurrentEntry;

            if (targetEntityTypeReference != null)
            {
                targetEntityType = targetEntityTypeReference.EntityDefinition();
                entry.TypeName   = targetEntityType.ODataFullName();

                if (serializationTypeNameAnnotation != null)
                {
                    entry.SetAnnotation(serializationTypeNameAnnotation);
                }
            }
            else if (entityTypeNameFromPayload != null)
            {
                entry.TypeName = entityTypeNameFromPayload;
            }

            // Set the current entity type since the type from payload might be more derived than
            // the expected one.
            this.CurrentEntityType = targetEntityType;
        }
        public void FlagsEnumAsEntityProperty_EmptyStrAsValue_NullAsTypeName()
        {
            DateTimeOffset updatedTime = DateTimeOffset.UtcNow;
            Func<ODataEntry> entryClone = () =>
            {
                var tmp = new ODataEntry
                {
                    TypeName = "NS.MyEntityType",
                    Properties = new[]
                        {
                            new ODataProperty
                            {
                                Name = "ColorFlags",
                                Value = new ODataEnumValue("")
                            }
                        },
                    SerializationInfo = MySerializationInfo
                };
                tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime });
                return tmp;
            };

            // model-request
            string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<entry 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://odata.org/test/$metadata#MySet/$entity\">" +
                    "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                        "<id />" +
                        "<title />" +
                        "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
                        "<author><name /></author>" +
                        "<content type=\"application/xml\">" +
                        "<m:properties>" +
                            "<d:ColorFlags m:type=\"#NS.ColorFlags\"></d:ColorFlags>" +
                        "</m:properties>" +
                    "</content>" +
                "</entry>";
            this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // model-reseponse
            this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // NoModel-request
            expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<entry 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://odata.org/test/$metadata#MySet/$entity\">" +
                    "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                        "<id />" +
                        "<title />" +
                        "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
                        "<author><name /></author>" +
                        "<content type=\"application/xml\">" +
                        "<m:properties>" +
                            "<d:ColorFlags></d:ColorFlags>" +
                        "</m:properties>" +
                    "</content>" +
                "</entry>";
            this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // NoModel-response
            this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);
        }
Example #8
0
        /// <summary>
        /// Creates an ODataEntry for the given EntityDescriptor and fills in its ODataLib metadata.
        /// </summary>
        /// <param name="entityDescriptor">The entity descriptor.</param>
        /// <param name="serverTypeName">Name of the server type.</param>
        /// <param name="entityType">The client-side entity type.</param>
        /// <param name="clientFormat">The current client format.</param>
        /// <returns>An odata entry with its metadata filled in.</returns>
        internal static ODataEntry CreateODataEntry(EntityDescriptor entityDescriptor, string serverTypeName, ClientTypeAnnotation entityType, DataServiceClientFormat clientFormat)
        {
            ODataEntry entry = new ODataEntry();

            // If the client type name is different from the server type name, then add SerializationTypeNameAnnotation
            // which tells ODataLib to write the type name in the annotation in the payload.
            if (entityType.ElementTypeName != serverTypeName)
            {
                entry.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName });
            }

            // We always need to write the client type name, since this is the type name used by ODataLib
            // to resolve the entity type using EdmModel.FindSchemaElement.
            entry.TypeName = entityType.ElementTypeName;

            // Continue to send the entry's ID in update payloads in Atom for compatibility with V1-V3,
            // but for JSON-Light we do not want the extra information on the wire.
            if (clientFormat.UsingAtom && EntityStates.Modified == entityDescriptor.State)
            {
                // <id>http://host/service/entityset(key)</id>
                entry.Id = entityDescriptor.GetLatestIdentity();
            }

            if (entityDescriptor.IsMediaLinkEntry || entityType.IsMediaLinkEntry)
            {
                // Since we are already enabled EnableWcfDataServicesClientBehavior in the writer settings,
                // setting the MediaResource value will tell ODataLib to write MLE payload, irrespective of
                // what the metadata says.
                entry.MediaResource = new ODataStreamReferenceValue();
            }

            return entry;
        }
        public void FlagsEnumAsCollectionElement_StrAsValue_StrAsTypeName()
        {
            DateTimeOffset updatedTime = DateTimeOffset.UtcNow;
            Func<ODataEntry> entryClone = () =>
            {
                var tmp = new ODataEntry
                {
                    TypeName = "NS.MyEntityType",
                    Properties = new[]
                    {
                        new ODataProperty{Name = "FloatId", Value = new ODataPrimitiveValue(12.3D)},       
                        new ODataProperty{Name = "Color", Value = new ODataEnumValue(Color.Green.ToString(), "NS.Color")},
                        new ODataProperty
                        {
                            Name = "MyCollectionType",
                            Value = new ODataCollectionValue { Items = new[] {  new ODataEnumValue(Color.Red.ToString(),"NS.EnumUndefinedTypename"), new ODataEnumValue(Color.Green.ToString(),"NS.EnumUndefinedTypename")} }
                        }
                    },
                    SerializationInfo = MySerializationInfo
                };
                tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime });
                return tmp;
            };

            // model-request
            string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<entry 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://odata.org/test/$metadata#MySet/$entity\">" +
                    "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                    "<id />" +
                    "<title />" +
                    "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
                    "<author>" +
                        "<name />" +
                    "</author>" +
                    "<content type=\"application/xml\">" +
                        "<m:properties>" +
                            "<d:FloatId m:type=\"Double\">12.3</d:FloatId>" +
                            "<d:Color m:type=\"#NS.Color\">Green</d:Color>" +
                            "<d:MyCollectionType m:type=\"#Collection(NS.ColorFlags)\">" +
                                "<m:element m:type=\"#NS.EnumUndefinedTypename\">Red</m:element>" +
                                "<m:element m:type=\"#NS.EnumUndefinedTypename\">Green</m:element>" +
                            "</d:MyCollectionType>" +
                        "</m:properties>" +
                    "</content>" +
                "</entry>";
            this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // model-reseponse
            this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // NoModel-request
            expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<entry 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://odata.org/test/$metadata#MySet/$entity\">" +
                    "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                    "<id />" +
                    "<title />" +
                    "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
                    "<author>" +
                        "<name />" +
                    "</author>" +
                    "<content type=\"application/xml\">" +
                        "<m:properties>" +
                            "<d:FloatId m:type=\"Double\">12.3</d:FloatId>" +
                            "<d:Color m:type=\"#NS.Color\">Green</d:Color>" +
                            "<d:MyCollectionType>" +
                                "<m:element m:type=\"#NS.EnumUndefinedTypename\">Red</m:element>" +
                                "<m:element m:type=\"#NS.EnumUndefinedTypename\">Green</m:element>" +
                            "</d:MyCollectionType>" +
                        "</m:properties>" +
                    "</content>" +
                "</entry>";
            this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // NoModel-response
            this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);
        }
        public void FlagsEnumAsEntityProperty_NullAsValue_ButNonNullable_GetNullError()
        {
            DateTimeOffset updatedTime = DateTimeOffset.UtcNow;
            Func<ODataEntry> entryClone = () =>
            {
                var tmp = new ODataEntry
                {
                    TypeName = "NS.MyEntityType",
                    Properties = new[]
                        {
                            new ODataProperty
                            {
                                Name = "ColorFlags",
                                Value = null
                            }
                        },
                    SerializationInfo = MySerializationInfo
                };

                tmp.SetAnnotation(new AtomEntryMetadata() { Updated = updatedTime });
                return tmp;
            };

            string fullName = this.entityType.FindProperty("ColorFlags").Type.FullName();

            // model-request
            Action action = () => this.WriteRequestWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: "");
            action.ShouldThrow<ODataException>().WithMessage(Strings.WriterValidationUtils_NonNullablePropertiesMustNotHaveNullValue("ColorFlags", fullName));

            // model-reseponse
            action = () => this.WriteResponseWithModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: "");
            action.ShouldThrow<ODataException>().WithMessage(Strings.WriterValidationUtils_NonNullablePropertiesMustNotHaveNullValue("ColorFlags", fullName));

            // NoModel-request 
            string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
            "<entry 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://odata.org/test/$metadata#MySet/$entity\">" +
                "<category term=\"#NS.MyEntityType\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                    "<id />" +
                    "<title />" +
                    "<updated>" + ODataAtomConvert.ToAtomString(updatedTime) + "</updated>" +
                    "<author><name /></author>" +
                    "<content type=\"application/xml\">" +
                    "<m:properties>" +
                        "<d:ColorFlags m:null=\"true\" />" +
                    "</m:properties>" +
                "</content>" +
            "</entry>";
            this.WriteRequestWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);

            // NoModel-response  
            this.WriteResponseWithoutModelAndValidatePayload(nestedItemToWrite: new[] { entryClone() }, expectedPayload: expectedPayload);
        }
Example #11
0
        /// <summary>Write the entry element.</summary>
        /// <param name="expanded">Expanded result provider for the specified <paramref name="element"/>.</param>
        /// <param name="element">Element representing the entry element.</param>
        /// <param name="resourceInstanceInFeed">true if the resource instance being serialized is inside a feed; false otherwise.</param>
        /// <param name="expectedType">Expected type of the entry element.</param>
        private void WriteEntry(IExpandedResult expanded, object element, bool resourceInstanceInFeed, ResourceType expectedType)
        {
            Debug.Assert(element != null, "element != null");
            Debug.Assert(expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "expectedType != null && expectedType.ResourceTypeKind == ResourceTypeKind.EntityType");
            this.IncrementSegmentResultCount();

            ODataEntry entry = new ODataEntry();
            if (!resourceInstanceInFeed)
            {
                entry.SetSerializationInfo(new ODataFeedAndEntrySerializationInfo { NavigationSourceName = this.CurrentContainer.Name, NavigationSourceEntityTypeName = this.CurrentContainer.ResourceType.FullName, ExpectedTypeName = expectedType.FullName });
            }

            string title = expectedType.Name;
#pragma warning disable 618
            if (this.contentFormat == ODataFormat.Atom)
#pragma warning restore 618
            {
                AtomEntryMetadata entryAtom = new AtomEntryMetadata();
                entryAtom.EditLink = new AtomLinkMetadata { Title = title };
                entry.SetAnnotation(entryAtom);
            }

            ResourceType actualResourceType = WebUtil.GetNonPrimitiveResourceType(this.Provider, element);
            if (actualResourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
            {
                // making sure that the actual resource type is an entity type
                throw new DataServiceException(500, Microsoft.OData.Service.Strings.BadProvider_InconsistentEntityOrComplexTypeUsage(actualResourceType.FullName));
            }

            EntityToSerialize entityToSerialize = this.WrapEntity(element, actualResourceType);

            // populate the media resource, if the entity is a MLE.
            entry.MediaResource = this.GetMediaResource(entityToSerialize, title);

            // Write the type name
            this.PayloadMetadataPropertyManager.SetTypeName(entry, this.CurrentContainer.ResourceType.FullName, actualResourceType.FullName);

            // Write Id element
            this.PayloadMetadataPropertyManager.SetId(entry, () => entityToSerialize.SerializedKey.Identity);

            // Write "edit" link
            this.PayloadMetadataPropertyManager.SetEditLink(entry, () => entityToSerialize.SerializedKey.RelativeEditLink);

            // Write the etag property, if the type has etag properties
            this.PayloadMetadataPropertyManager.SetETag(entry, () => this.GetETagValue(element, actualResourceType));
            
            IEnumerable<ProjectionNode> projectionNodes = this.GetProjections();
            if (projectionNodes != null)
            {
                // Filter the projection nodes for the actual type of the entity
                // The projection node might refer to the property in a derived type. If the TargetResourceType of
                // the projection node is not a super type, then we do not want to serialize this property.
                projectionNodes = projectionNodes.Where(projectionNode => projectionNode.TargetResourceType.IsAssignableFrom(actualResourceType));

                // Because we are going to enumerate through these multiple times, create a list.
                projectionNodes = projectionNodes.ToList();

                // And add the annotation to tell ODataLib which properties to write into content (the projections)
                entry.SetAnnotation(new ProjectedPropertiesAnnotation(projectionNodes.Select(p => p.PropertyName)));
            }

            // Populate the advertised actions
            IEnumerable<ODataAction> actions;
            if (this.TryGetAdvertisedActions(entityToSerialize, resourceInstanceInFeed, out actions))
            {
                foreach (ODataAction action in actions)
                {
                    entry.AddAction(action);
                }
            }

            // Populate all the normal properties
            entry.Properties = this.GetEntityProperties(entityToSerialize, projectionNodes);

            // And start the entry
            var args = new DataServiceODataWriterEntryArgs(entry, element, this.Service.OperationContext);
            this.dataServicesODataWriter.WriteStart(args);

            // Now write all the navigation properties
            this.WriteNavigationProperties(expanded, entityToSerialize, resourceInstanceInFeed, projectionNodes);

            // And write the end of the entry
            this.dataServicesODataWriter.WriteEnd(args);

#if ASTORIA_FF_CALLBACKS
            this.Service.InternalOnWriteItem(target, element);
#endif
        }
        internal static void AddTypeNameAnnotationAsNeeded(ODataEntry entry, IEdmEntityType odataPathType,
            ODataMetadataLevel metadataLevel)
        {
            // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties
            // null when values should not be serialized. The TypeName property is different and should always be
            // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not
            // to serialize the type name (a null value prevents serialization).

            // Note that this annotation should not be used for Atom or JSON verbose formats, as it will interfere with
            // the correct default behavior for those formats.

            Contract.Assert(entry != null);

            // Only add an annotation if we want to override ODataLib's default type name serialization behavior.
            if (ShouldAddTypeNameAnnotation(metadataLevel))
            {
                string typeName;

                // Provide the type name to serialize (or null to force it not to serialize).
                if (ShouldSuppressTypeNameSerialization(entry, odataPathType, metadataLevel))
                {
                    typeName = null;
                }
                else
                {
                    typeName = entry.TypeName;
                }

                entry.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation
                {
                    TypeName = typeName
                });
            }
        }
        private static void AddEntryMetadata(EntityInstance payloadElement, ODataEntry entry)
        {
            AtomEntryMetadata metadata = null;

            foreach (XmlTreeAnnotation epmTree in payloadElement.Annotations.OfType<XmlTreeAnnotation>())
            {
                if (epmTree.NamespaceName == TestAtomConstants.AtomNamespace)
                {
                    if (metadata == null)
                    {
                        metadata = new AtomEntryMetadata();
                    }

                    string localName = epmTree.LocalName;
                    if (localName == TestAtomConstants.AtomAuthorElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomPersonMetadata author = CreateAuthorMetadata(epmTree.Children);

                        List<AtomPersonMetadata> authors;
                        if (metadata.Authors == null)
                        {
                            authors = new List<AtomPersonMetadata>();
                            metadata.Authors = authors;
                        }
                        else
                        {
                            authors = (List<AtomPersonMetadata>)metadata.Authors;
                        }
                        authors.Add(author);
                    }
                    else if (localName == TestAtomConstants.AtomCategoryElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomCategoryMetadata category = CreateCategoryMetadata(epmTree.Children);

                        List<AtomCategoryMetadata> categories;
                        if (metadata.Categories == null)
                        {
                            categories = new List<AtomCategoryMetadata>();
                            metadata.Categories = categories;
                        }
                        else
                        {
                            categories = (List<AtomCategoryMetadata>)metadata.Categories;
                        }
                        categories.Add(category);
                    }
                    else if (localName == TestAtomConstants.AtomContributorElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomPersonMetadata contributor = CreateAuthorMetadata(epmTree.Children);

                        List<AtomPersonMetadata> contributors;
                        if (metadata.Contributors == null)
                        {
                            contributors = new List<AtomPersonMetadata>();
                            metadata.Contributors = contributors;
                        }
                        else
                        {
                            contributors = (List<AtomPersonMetadata>)metadata.Contributors;
                        }
                        contributors.Add(contributor);
                    }
                    else if (localName == TestAtomConstants.AtomIdElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        entry.Id = string.IsNullOrEmpty(epmTree.PropertyValue) ? null : new Uri(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomLinkElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomLinkMetadata link = CreateLinkMetadata(epmTree.Children);

                        List<AtomLinkMetadata> links;
                        if (metadata.Links == null)
                        {
                            links = new List<AtomLinkMetadata>();
                            metadata.Links = links;
                        }
                        else
                        {
                            links = (List<AtomLinkMetadata>)metadata.Links;
                        }
                        links.Add(link);
                    }
                    else if (localName == TestAtomConstants.AtomPublishedElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Published = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue);
                    }
                    else if (localName == TestAtomConstants.AtomRightsElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Rights = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomSourceElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Source = CreateFeedMetadata(epmTree.Children, null);
                    }
                    else if (localName == TestAtomConstants.AtomSummaryElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Summary = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomTitleElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        AtomTextConstructKind atomConstructKind = GetAtomConstructKind(epmTree.Children);
                        metadata.Title = new AtomTextConstruct { Kind = atomConstructKind, Text = epmTree.PropertyValue };
                    }
                    else if (localName == TestAtomConstants.AtomUpdatedElementName)
                    {
                        Debug.Assert(!epmTree.IsAttribute);
                        metadata.Updated = string.IsNullOrEmpty(epmTree.PropertyValue) ? (DateTimeOffset?)null : DateTimeOffset.Parse(epmTree.PropertyValue);
                    }
                    else
                    {
                        throw new NotSupportedException("Unsupported atom metadata '" + localName + "' found for entry!");
                    }
                }
            }

            // Fix up metadata for baselining
            metadata = metadata.Fixup();

            if (metadata != null)
            {
                entry.SetAnnotation<AtomEntryMetadata>(metadata);
            }
        }
        /// <summary>
        /// Creates and returns an ODataEntry from the given value.
        /// </summary>
        /// <param name="entityType">The value type.</param>
        /// <param name="value">The entry value.</param>
        /// <param name="properties">The given properties to serialize.</param>
        /// <returns>An ODataEntry representing the given value.</returns>
        internal ODataEntry CreateODataEntry(Type entityType, object value, params ClientPropertyAnnotation[] properties)
        {
            Debug.Assert(entityType != null, "entityType != null");
            Debug.Assert(value != null, "value != null");

            ClientEdmModel model = this.requestInfo.Model;
            ClientTypeAnnotation entityTypeAnnotation = model.GetClientTypeAnnotation(entityType);
            Debug.Assert(entityTypeAnnotation != null, "entityTypeAnnotation != null");
            Debug.Assert(entityTypeAnnotation.IsEntityType, "Unexpected type");

            ODataEntry odataEntityValue = new ODataEntry()
            {
                TypeName = entityTypeAnnotation.ElementTypeName,
            };

            string serverTypeName = this.requestInfo.GetServerTypeName(entityTypeAnnotation);
            odataEntityValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName });

            odataEntityValue.Properties = this.PopulateProperties(value, serverTypeName, properties.Any() ? properties : entityTypeAnnotation.PropertiesToSerialize(), null);

            return odataEntityValue;
        }
        internal static void AddTypeNameAnnotationAsNeeded(ODataEntry entry, IEdmEntityType odataPathType,
            ODataMetadataLevel metadataLevel)
        {
            // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties
            // null when values should not be serialized. The TypeName property is different and should always be
            // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not
            // to serialize the type name (a null value prevents serialization).

            // Note: In the current version of ODataLib the default behavior likely now matches the requirements for
            // minimal metadata mode. However, there have been behavior changes/bugs there in the past, so the safer
            // option is for this class to take control of type name serialization in minimal metadata mode.

            Contract.Assert(entry != null);

            string typeName = null; // Set null to force the type name not to serialize.

            // Provide the type name to serialize.
            if (!ShouldSuppressTypeNameSerialization(entry, odataPathType, metadataLevel))
            {
                typeName = entry.TypeName;
            }

            entry.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation
            {
                TypeName = typeName
            });
        }
Example #16
0
        /// <summary>
        /// Creates an ODataEntry instance with the default values for 'Id', 'ReadLink' and 'Updated'
        /// that can be used and modified in tests.
        /// </summary>
        /// <param name="typeName">The optional type name for the default entry.</param>
        /// <param name="model">The product model to generate the type in (if not null).</param>
        /// <returns>The newly created ODataEntry instance.</returns>
        public static ODataEntry CreateDefaultEntryWithAtomMetadata(string entitySetName = null, string typeName = null, EdmModel model = null)
        {
            if (model != null && typeName != null)
            {
                EdmEntityType entityType = new EdmEntityType(DefaultNamespaceName, typeName);
                entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(isNullable: false)));
                model.AddElement(entityType);

                typeName = entityType.FullName();

                EdmEntityContainer container = new EdmEntityContainer(DefaultNamespaceName, "DefaultContainer");
                model.AddElement(container);

                if (entitySetName != null)
                {
                    container.AddEntitySet(entitySetName, entityType);
                }
            }

            ODataEntry entry = new ODataEntry()
            {
                Id = DefaultEntryId,
                ReadLink = DefaultEntryReadLink,
                TypeName = typeName,
                SerializationInfo = MySerializationInfo
            };
            AtomEntryMetadata metadata = new AtomEntryMetadata()
            {
                Updated = DateTimeOffset.Parse(DefaultEntryUpdated)
            };
            entry.SetAnnotation<AtomEntryMetadata>(metadata);
            return entry;
        }
        private IEnumerable<ODataItem> CreatePayload(ProjectedPropertiesTestCase testCase)
        {
            // First create the entry itself (it might get wrapped later)
            ODataEntry entry = new ODataEntry()
            {
                TypeName = "TestModel.EntityType",
                Properties = new List<ODataProperty>() 
                    {
                        new ODataProperty { Name = "StringProperty", Value = "foo" },
                        new ODataProperty { Name = "NumberProperty", Value = 42 },
                        new ODataProperty { Name = "SimpleComplexProperty", Value = new ODataComplexValue 
                        { 
                            TypeName = "TestModel.SimplexComplexType", 
                            Properties = new ODataProperty[] {
                                new ODataProperty { Name = "Name", Value = "Bart" }
                        } } },
                        new ODataProperty { Name = "DeepComplexProperty", Value = new ODataComplexValue 
                        { 
                            TypeName = "TestModel.NestedComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty { Name = "InnerComplexProperty", Value = new ODataComplexValue 
                                { 
                                    TypeName = "TestModel.SimplexComplexType2",
                                    Properties = new ODataProperty[] {
                                        new ODataProperty { Name = "Value", Value = 43 }
                                } } }
                        } } },
                        
                        new ODataProperty { Name = "PrimitiveCollection", Value = new ODataCollectionValue 
                        { 
                            TypeName = "Collection(Edm.String)",
                            Items = new object[] { "Simpson" }
                        } },
                        new ODataProperty { Name = "ComplexCollection", Value = new ODataCollectionValue 
                        { 
                            TypeName = "Collection(TestModel.RatingComplexType)",
                            Items = new object[] {
                                new ODataComplexValue 
                                { 
                                    TypeName = "TestModel.RatingComplexType",
                                    Properties = new ODataProperty[] { new ODataProperty { Name = "Rating", Value = -3 } } 
                                }
                        } } }
                    },
                SerializationInfo = MySerializationInfo
            };

            if (testCase.ResponseOnly)
            {
                // Add a stream property for responses
                ((List<ODataProperty>)entry.Properties).Add(new ODataProperty { Name = "NamedStream", Value = new ODataStreamReferenceValue { EditLink = new Uri("http://odata.org/namedstream") } });
            }

            ODataItem[] entryItems = new ODataItem[]
            {
                entry,
                new ODataNavigationLink { Name = "DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred"), AssociationLinkUrl = testCase.ResponseOnly ? new Uri("http://odata.org/associationlink2") : null },
                null, // End deferred link

                new ODataNavigationLink { Name = "ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/entry") },
                    new ODataEntry() 
                    {
                        TypeName = "TestModel.ExpandedEntryType",
                        Properties = new ODataProperty[] {
                            new ODataProperty { Name = "ExpandedEntryName", Value = "bar" }
                        },
                        SerializationInfo = MySerializationInfo
                    },
                        new ODataNavigationLink { Name = "ExpandedEntry_DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred") },
                        null, // End deffered link
                        new ODataNavigationLink { Name = "ExpandedEntry_ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") },
                            new ODataFeed { Id = new Uri("http://test/feedid1"), SerializationInfo = MySerializationInfo },
                            null, // End feed
                        null, // End exanded expanded feed link
                    null, // End expanded entry
                null, // End expanded entry nav link

                new ODataNavigationLink { Name = "ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed") },
                    new ODataFeed { Id = new Uri("http://test/feedid2") },
                        new ODataEntry { TypeName = "TestModel.EntityType" },
                        null, // End entry
                        new ODataEntry { TypeName = "TestModel.EntityType", SerializationInfo = MySerializationInfo },
                        null, // End entry
                    null, // End expanded feed
                null, // End expanded feed nav link

                null, // End the top-level entry
            };

            ProjectedPropertiesAnnotation projectedProperties = testCase.TopLevelProjectedProperties;

            if (!testCase.NestedPayload)
            {
                this.Assert.IsNull(testCase.NestedProjectedProperties, "For a non-nested payload, no nested annotation must be specified.");
                entry.SetAnnotation(projectedProperties);
                return entryItems;
            }

            // If we are processing a test case for a nested payload, wrap the entry items into a wrapping entry with an expanded navigation link.
            ODataEntry wrappingEntry = new ODataEntry() 
            {
                TypeName = "TestModel.WrappingEntityType",
                Properties = new[] { new ODataProperty { Name = "Wrapping_ID", Value = 1 } },
                SerializationInfo = MySerializationInfo
            };
            IEnumerable<ODataItem> wrappedItems = 
                new ODataItem[] { wrappingEntry, new ODataNavigationLink { Name = "Wrapping_ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/wrapping") }}
                .Concat(entryItems)
                .Concat(new ODataItem[] { null, null });

            ProjectedPropertiesAnnotation nestedProjectedProperties = testCase.NestedProjectedProperties;
            entry.SetAnnotation(nestedProjectedProperties);
            wrappingEntry.SetAnnotation(projectedProperties);
            
            return wrappedItems;
        }
        private static void WriteEntry(ODataWriter writer, object entity, IEnumerable<string> projectedProperties)
        {
            var entry = new ODataEntry()
            {
                Id = new Uri("http://temp.org/" + Guid.NewGuid()),
                SerializationInfo = MySerializationInfo
            };

            if (projectedProperties != null)
            {
                entry.SetAnnotation<ProjectedPropertiesAnnotation>(new ProjectedPropertiesAnnotation(projectedProperties));
            }
            
            entry.Properties = entity.GetType().GetProperties().Select(p => new ODataProperty() { Name = p.Name, Value = p.GetValue(entity, null) });

            writer.WriteStart(entry);
            writer.WriteEnd();
        }
Example #19
0
        /// <summary>
        /// Creates the materializer entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="format">The format the entry was read in.</param>
        /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param>
        /// <param name="model">The client model.</param>
        /// <returns>A new materializer entry.</returns>
        public static MaterializerEntry CreateEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model)
        {
            Debug.Assert(entry.GetAnnotation<MaterializerEntry>() == null, "MaterializerEntry has already been created.");

            MaterializerEntry materializerEntry = new MaterializerEntry(entry, format, isTracking, model);
            entry.SetAnnotation<MaterializerEntry>(materializerEntry);

            return materializerEntry;
        }