internal static void AddTypeNameAnnotationAsNeeded(ODataPrimitiveValue primitive, ODataMetadataLevel metadataLevel) { Contract.Assert(primitive != null); object value = primitive.Value; // Don't add a type name annotation for Atom or JSON verbose. if (metadataLevel != ODataMetadataLevel.Default) { string typeName; if (ShouldSuppressTypeNameSerialization(value, metadataLevel)) { typeName = null; } else { typeName = GetTypeName(value); } primitive.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
internal static void AddTypeNameAnnotationAsNeeded(ODataComplexValue value, 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(value != 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(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
private static bool ShouldAddTypeNameAnnotation(ODataMetadataLevel metadataLevel) { // Don't interfere with the correct default behavior in non-JSON light formats. // In all JSON light modes, take control of type name serialization. // For primitives (unlike other types), the default behavior does not matches the requirements for minimal // metadata mode, so the annotation is needed even in minimal metadata mode. return metadataLevel != ODataMetadataLevel.Default; }
public virtual EntitySelfLinks BuildEntitySelfLinks(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel) { EntitySelfLinks selfLinks = new EntitySelfLinks(); selfLinks.IdLink = BuildIdLink(instanceContext, metadataLevel); selfLinks.EditLink = BuildEditLink(instanceContext, metadataLevel, selfLinks.IdLink); selfLinks.ReadLink = BuildReadLink(instanceContext, metadataLevel, selfLinks.EditLink); return selfLinks; }
public override Uri BuildEditLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel, string idLink) { if (EditLinkBuilder != null) { return EditLinkBuilder.Factory(instanceContext); } return null; }
public override string BuildIdLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel) { if (IdLinkBuilder != null) { return IdLinkBuilder.Factory(instanceContext); } return null; }
public override Uri BuildReadLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel, Uri editLink) { if (ReadLinkBuilder != null) { return ReadLinkBuilder.Factory(instanceContext); } return null; }
public override Uri BuildNavigationLink(EntityInstanceContext context, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel) { if (NavigationLinkBuilder != null) { return NavigationLinkBuilder(context, navigationProperty, metadataLevel); } return null; }
public void BuildIdLink_Throws_IfIdLinkBuilderIsNull(ODataMetadataLevel metadataLevel) { // Arrange NavigationSourceLinkBuilderAnnotation linkBuilder = new NavigationSourceLinkBuilderAnnotation(_entitySet); // Act & Assert Assert.Throws<InvalidOperationException>( () => linkBuilder.BuildIdLink(new EntityInstanceContext(), (ODataMetadataLevel)metadataLevel), "No IdLink factory was found. Try calling HasIdLink on the NavigationSourceConfiguration for 'Customers'."); }
internal static ODataValue CreatePrimitive(object value, ODataMetadataLevel metadataLevel) { if (value == null) { return new ODataNullValue(); } object supportedValue = ConvertUnsupportedPrimitives(value); ODataPrimitiveValue primitive = new ODataPrimitiveValue(supportedValue); AddTypeNameAnnotationAsNeeded(primitive, metadataLevel); return primitive; }
public void AddTypeNameAnnotationAsNeeded_DoesNotAddAnnotation(ODataMetadataLevel metadataLevel) { // Arrange ODataEnumValue enumValue = new ODataEnumValue("value"); IEdmEnumTypeReference enumType = new EdmEnumTypeReference( new EdmEnumType("TestModel", "EnumType"), isNullable: false); // Act ODataEnumSerializer.AddTypeNameAnnotationAsNeeded(enumValue, enumType, metadataLevel); // Assert SerializationTypeNameAnnotation annotation = enumValue.GetAnnotation<SerializationTypeNameAnnotation>(); Assert.Null(annotation); }
public void BuildIdLink(bool followsConventions, ODataMetadataLevel metadataLevel, bool linkEmitted) { // Arrange _entitySet.HasIdLink(new SelfLinkBuilder<Uri>((context) => new Uri("http://selflink"), followsConventions)); NavigationSourceLinkBuilderAnnotation linkBuilder = new NavigationSourceLinkBuilderAnnotation(_entitySet); // Act Uri generatedIdLink = linkBuilder.BuildIdLink(new EntityInstanceContext(), (ODataMetadataLevel)metadataLevel); // Assert if (linkEmitted) { Assert.Equal(new Uri("http://selflink"), generatedIdLink); } else { Assert.Null(generatedIdLink); } }
public virtual string BuildIdLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel) { if (instanceContext == null) { throw Error.ArgumentNull("instanceContext"); } if (_idLinkBuilder == null) { throw Error.InvalidOperation(SRResources.NoIdLinkFactoryFound, _entitySet.Name); } if (IsDefaultOrFull(metadataLevel) || (IsMinimal(metadataLevel) && !_idLinkBuilder.FollowsConventions)) { return _idLinkBuilder.Factory(instanceContext); } else { // client can infer it and didn't ask for it. return null; } }
internal static void AddTypeNameAnnotationAsNeeded(ODataPrimitiveValue primitive, IEdmPrimitiveTypeReference primitiveType, 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). Contract.Assert(primitive != null); object value = primitive.Value; string typeName = null; // Set null to force the type name not to serialize. // Provide the type name to serialize. if (!ShouldSuppressTypeNameSerialization(value, metadataLevel)) { typeName = primitiveType.FullName(); } primitive.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); }
private static bool IsMinimal(ODataMetadataLevel level) { return(level == ODataMetadataLevel.MinimalMetadata); }
/// <summary> /// Read and serialize outgoing object to HTTP request stream. /// </summary> /// <param name="type">incoming request body object type.</param> /// <param name="value">value for this type.</param> /// <param name="model">Edm model</param> /// <param name="baseAddress">Base address of request.</param> /// <param name="contentType">Content Type.</param> /// <param name="internalUrlHelper">internal url helper.</param> /// <param name="internalRequest">internal request.</param> /// <param name="internalRequestHeaders">internal request headers.</param> /// <param name="getODataMessageWrapper">Function to obtain message wrapper.</param> /// <param name="getEdmTypeSerializer">Function to obtain serializer.</param> /// <param name="getODataPayloadSerializer">Function to obtain payload serializer.</param> /// <param name="getODataSerializerContext">Function to obtain payload serializer context.</param> internal static void WriteToStream( Type type, object value, IEdmModel model, Uri baseAddress, MediaTypeHeaderValue contentType, IUrlHelper internalUrlHelper, HttpRequest internalRequest, IHeaderDictionary internalRequestHeaders, Func <IServiceProvider, ODataMigrationMessageWrapper> getODataMessageWrapper, Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer, Func <Type, ODataSerializer> getODataPayloadSerializer, Func <ODataSerializerContext> getODataSerializerContext) { if (model == null) { throw new InvalidOperationException("Request must have model"); } ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer); // special case: if the top level serializer is an ODataPrimitiveSerializer then swap it out for an ODataMigrationPrimitiveSerializer // This only applies to the top level because inline primitives are translated but top level primitives are not, unless we use a customized serializer. if (serializer is ODataPrimitiveSerializer) { serializer = new ODataMigrationPrimitiveSerializer(); } ODataPath path = internalRequest.ODataFeature().Path; IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource; // serialize a response string preferHeader = GetRequestPreferHeader(internalRequestHeaders); string annotationFilter = null; if (!String.IsNullOrEmpty(preferHeader)) { ODataMigrationMessageWrapper messageWrapper = getODataMessageWrapper(null); messageWrapper.SetHeader("Prefer", preferHeader); annotationFilter = messageWrapper.PreferHeader().AnnotationFilter; } ODataMigrationMessageWrapper responseMessageWrapper = getODataMessageWrapper(internalRequest.GetRequestContainer()); IODataResponseMessage responseMessage = responseMessageWrapper; if (annotationFilter != null) { responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter; } ODataMessageWriterSettings writerSettings = internalRequest.GetWriterSettings(); writerSettings.BaseUri = baseAddress; writerSettings.Version = ODataVersion.V4; // Todo how to specify v3? Maybe don't because reading as v4 writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; string metadataLink = internalUrlHelper.CreateODataLink(MetadataSegment.Instance); if (metadataLink == null) { throw new SerializationException("Unable to determine metadata url"); } writerSettings.ODataUri = new ODataUri { ServiceRoot = baseAddress, SelectAndExpand = internalRequest.ODataFeature()?.SelectExpandClause, Apply = internalRequest.ODataFeature().ApplyClause, Path = (path == null || IsOperationPath(path)) ? null : path.Path, }; ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata; if (contentType != null) { IEnumerable <KeyValuePair <string, string> > parameters = contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name.Value, val.Value.Value)); metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType.Value, parameters); } using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model)) { ODataSerializerContext writeContext = getODataSerializerContext(); writeContext.NavigationSource = targetNavigationSource; writeContext.Model = model; writeContext.RootElementName = GetRootElementName(path) ?? "root"; writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet; writeContext.Path = path; writeContext.SelectExpandClause = internalRequest.ODataFeature()?.SelectExpandClause; writeContext.MetadataLevel = metadataLevel; // Use reflection to write QueryOptions. // If the QueryOptions is not written, the @odata.nextlink may not be set properly in the response body. var odataFeature = internalRequest.ODataFeature(); var queryOptionsInfo = odataFeature.GetType().GetProperty("QueryOptions", BindingFlags.Instance | BindingFlags.NonPublic); if (queryOptionsInfo != null) { var queryOptions = queryOptionsInfo.GetValue(odataFeature) as ODataQueryOptions; writeContext.GetType().GetProperty("QueryOptions").SetValue(writeContext, queryOptions); } // Substitute stream to swap @odata.context Stream substituteStream = new MemoryStream(); Stream originalStream = messageWriter.SubstituteResponseStream(substituteStream); serializer.WriteObject(value, type, messageWriter, writeContext); StreamReader reader = new StreamReader(substituteStream); substituteStream.Seek(0, SeekOrigin.Begin); JToken responsePayload = JToken.Parse(reader.ReadToEnd()); // If odata context is present, replace with odata metadata if (responsePayload["@odata.context"] != null) { responsePayload["odata.metadata"] = responsePayload["@odata.context"].ToString().Replace("$entity", "@Element"); ((JObject)responsePayload).Property("@odata.context").Remove(); } // Write to actual stream // We cannot dispose of the stream because this method does not own the stream (subsequent methods will close the streamwriter) StreamWriter streamWriter = new StreamWriter(originalStream); JsonTextWriter writer = new JsonTextWriter(streamWriter); JsonSerializer jsonSerializer = new JsonSerializer(); jsonSerializer.Serialize(writer, responsePayload); writer.Flush(); messageWriter.SubstituteResponseStream(originalStream); } }
public void BuildNavigationLink_ReturnsTheNavigationLinkUri(bool followsConventions, ODataMetadataLevel metadataLevel) { // Arrange var navigationProperty = _entitySet.EntityType.AddNavigationProperty(typeof(Customer).GetProperty("Orders"), EdmMultiplicity.Many); IEdmModel model = _modelBuilder.GetEdmModel(); IEdmNavigationProperty edmNavProperty = model.GetEdmTypeReference(typeof(Customer)).AsEntity().DeclaredNavigationProperties().Single(p => p.Name == "Orders"); NavigationSourceLinkBuilderAnnotation linkBuilder = new NavigationSourceLinkBuilderAnnotation(_entitySet); linkBuilder.AddNavigationPropertyLinkBuilder(edmNavProperty, new NavigationLinkBuilder((context, property) => new Uri("http://navigationlink"), followsConventions)); // Act Uri generatedNavigationLink = linkBuilder.BuildNavigationLink(new ResourceContext(), edmNavProperty, (ODataMetadataLevel)metadataLevel); // Assert Assert.Equal("http://navigationlink/", generatedNavigationLink.AbsoluteUri); }
/// <summary> /// Adds the type name annotations required for proper json light serialization. /// </summary> /// <param name="value">The collection value for which the annotations have to be added.</param> /// <param name="metadataLevel">The OData metadata level of the response.</param> protected internal static void AddTypeNameAnnotationAsNeeded(ODataCollectionValue value, 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(value != 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(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.SetAnnotation <SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
internal static string CreateMetadataFragment(IEdmFunctionImport action, IEdmModel model, ODataMetadataLevel metadataLevel) { IEdmEntityContainer container = action.Container; string actionName = action.Name; string fragment; if ((metadataLevel == ODataMetadataLevel.MinimalMetadata || metadataLevel == ODataMetadataLevel.NoMetadata) && model.IsDefaultEntityContainer(container)) { fragment = actionName; } else { fragment = container.Name + "." + actionName; } return fragment; }
internal static bool ShouldAddTypeNameAnnotation(ODataMetadataLevel metadataLevel) { switch (metadataLevel) { // For complex types, the default behavior matches the requirements for minimal metadata mode, so no // annotation is necessary. case ODataMetadataLevel.MinimalMetadata: return false; // In other cases, this class must control the type name serialization behavior. case ODataMetadataLevel.FullMetadata: case ODataMetadataLevel.NoMetadata: default: // All values already specified; just keeping the compiler happy. return true; } }
internal static bool ShouldSuppressTypeNameSerialization(object value, ODataMetadataLevel metadataLevel) { // For dynamic properties in minimal metadata level, the type name always appears as declared property. if (metadataLevel != ODataMetadataLevel.FullMetadata) { return true; } return CanTypeBeInferredInJson(value); }
internal static void WriteToStream( Type type, object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func <IServiceProvider, ODataMessageWrapper> getODataMessageWrapper, Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer, Func <Type, ODataSerializer> getODataPayloadSerializer, Func <ODataSerializerContext> getODataSerializerContext) { if (model == null) { throw Error.InvalidOperation(SRResources.RequestMustHaveModel); } IEdmStructuredObject edmStructuredObject = value as IEdmStructuredObject; if (edmStructuredObject != null) { edmStructuredObject.SetModel(model); } ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer); ODataPath path = internalRequest.Context.Path; IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource; // serialize a response IODataResponseMessage responseMessage = PrepareResponseMessage(internalRequest, internalRequestHeaders, getODataMessageWrapper); ODataMessageWriterSettings writerSettings = internalRequest.WriterSettings; writerSettings.BaseUri = baseAddress; writerSettings.Version = version; writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; string metadataLink = internaUrlHelper.CreateODataLink(MetadataSegment.Instance); if (metadataLink == null) { throw new SerializationException(SRResources.UnableToDetermineMetadataUrl); } //Set this variable if the SelectExpandClause is different from the processed clause on the Query options SelectExpandClause selectExpandDifferentFromQueryOptions = null; if (internalRequest.Context.QueryOptions != null && internalRequest.Context.QueryOptions.SelectExpand != null) { if (internalRequest.Context.QueryOptions.SelectExpand.ProcessedSelectExpandClause != internalRequest.Context.ProcessedSelectExpandClause) { selectExpandDifferentFromQueryOptions = internalRequest.Context.ProcessedSelectExpandClause; } } else if (internalRequest.Context.ProcessedSelectExpandClause != null) { selectExpandDifferentFromQueryOptions = internalRequest.Context.ProcessedSelectExpandClause; } writerSettings.ODataUri = new ODataUri { ServiceRoot = baseAddress, // TODO: 1604 Convert webapi.odata's ODataPath to ODL's ODataPath, or use ODL's ODataPath. SelectAndExpand = internalRequest.Context.ProcessedSelectExpandClause, Apply = internalRequest.Context.ApplyClause, Path = (path == null || IsOperationPath(path)) ? null : path.Path, }; ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata; if (contentType != null) { IEnumerable <KeyValuePair <string, string> > parameters = contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name, val.Value)); metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType, parameters); } using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model)) { ODataSerializerContext writeContext = getODataSerializerContext(); writeContext.NavigationSource = targetNavigationSource; writeContext.Model = model; writeContext.RootElementName = GetRootElementName(path) ?? "root"; writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet; writeContext.Path = path; writeContext.MetadataLevel = metadataLevel; writeContext.QueryOptions = internalRequest.Context.QueryOptions; //Set the SelectExpandClause on the context if it was explicitly specified. if (selectExpandDifferentFromQueryOptions != null) { writeContext.SelectExpandClause = selectExpandDifferentFromQueryOptions; } serializer.WriteObject(value, type, messageWriter, writeContext); } }
internal static void AddTypeNameAnnotationAsNeeded(ODataEnumValue enumValue, IEdmEnumTypeReference enumType, 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). Contract.Assert(enumValue != 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(metadataLevel)) { typeName = null; } else { typeName = enumType.FullName(); } enumValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
public override Uri BuildNavigationLink(ResourceContext context, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel) { if (NavigationLinkBuilder != null) { return(NavigationLinkBuilder(context, navigationProperty, metadataLevel)); } return(null); }
public void BuildEditLink_WhenEditLinkIsSameAsIdLink_And_IsNotSet(bool followsConventions, ODataMetadataLevel metadataLevel) { // Arrange _entitySet.HasIdLink(new SelfLinkBuilder <Uri>((context) => new Uri("http://selflink/"), followsConventions)); NavigationSourceLinkBuilderAnnotation linkBuilder = new NavigationSourceLinkBuilderAnnotation(_entitySet); // Act Uri generatedEditLink = linkBuilder.BuildEditLink(new ResourceContext(), (ODataMetadataLevel)metadataLevel, new Uri("http://selflink")); // Assert Assert.Null(generatedEditLink); }
/// <summary> /// Adds the type name annotations required for proper json light serialization. /// </summary> /// <param name="value">The collection value for which the annotations have to be added.</param> /// <param name="metadataLevel">The OData metadata level of the response.</param> protected internal static void AddTypeNameAnnotationAsNeeded(ODataCollectionValue value, 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). Contract.Assert(value != 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(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.TypeAnnotation = new ODataTypeAnnotation(typeName); } }
/// <summary> /// Constructs a NavigationLink for a particular <see cref="EntityInstanceContext" />, <see cref="IEdmNavigationProperty" /> and <see cref="ODataMetadataLevel" />. /// </summary> public virtual Uri BuildNavigationLink(EntityInstanceContext instanceContext, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel) { if (instanceContext == null) { throw Error.ArgumentNull("instanceContext"); } if (navigationProperty == null) { throw Error.ArgumentNull("navigationProperty"); } NavigationLinkBuilder navigationLinkBuilder; if (_navigationPropertyLinkBuilderLookup.TryGetValue(navigationProperty, out navigationLinkBuilder) && (metadataLevel == ODataMetadataLevel.FullMetadata || (metadataLevel == ODataMetadataLevel.MinimalMetadata && !navigationLinkBuilder.FollowsConventions))) { return(navigationLinkBuilder.Factory(instanceContext, navigationProperty)); } // Return null to let ODL decide when and how to build the navigation link. return(null); }
/// <summary> /// Constructs the <see cref="EntitySelfLinks" /> for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />. /// </summary> public virtual EntitySelfLinks BuildEntitySelfLinks(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel) { EntitySelfLinks selfLinks = new EntitySelfLinks(); selfLinks.IdLink = BuildIdLink(instanceContext, metadataLevel); selfLinks.EditLink = BuildEditLink(instanceContext, metadataLevel, selfLinks.IdLink); selfLinks.ReadLink = BuildReadLink(instanceContext, metadataLevel, selfLinks.EditLink); return(selfLinks); }
public void BuildReadLink_WhenReadLinkIsNotSameAsEditLink(bool followsConventions, ODataMetadataLevel metadataLevel, bool linkEmitted) { // Arrange _entitySet.HasReadLink(new SelfLinkBuilder <Uri>((context) => new Uri("http://readlink/"), followsConventions)); NavigationSourceLinkBuilderAnnotation linkBuilder = new NavigationSourceLinkBuilderAnnotation(_entitySet); // Act Uri generatedReadLink = linkBuilder.BuildReadLink(new ResourceContext(), metadataLevel, new Uri("http://editLink")); // Assert if (linkEmitted) { Assert.Equal("http://readlink/", generatedReadLink.AbsoluteUri); } else { Assert.Null(generatedReadLink); } }
private static bool ShouldSuppressTypeNameSerialization(ODataMetadataLevel metadataLevel) { Contract.Assert(metadataLevel != ODataMetadataLevel.MinimalMetadata); switch (metadataLevel) { case ODataMetadataLevel.NoMetadata: return true; case ODataMetadataLevel.FullMetadata: default: return false; } }
/// <summary> /// Constructs a ReadLink URL for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />. /// </summary> public virtual Uri BuildReadLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel, Uri editLink) { if (instanceContext == null) { throw Error.ArgumentNull("instanceContext"); } if (_readLinkBuilder != null && (metadataLevel == ODataMetadataLevel.FullMetadata || (metadataLevel == ODataMetadataLevel.MinimalMetadata && !_readLinkBuilder.FollowsConventions))) { // read link is not the same as edit link. Generate if the client asked for it (full metadata modes) or // if the client cannot infer it (not follow conventions). return _readLinkBuilder.Factory(instanceContext); } // Return null to let ODL decide when and how to build the read link. return null; }
internal static bool ShouldOmitAction(IEdmAction action, ActionLinkBuilder builder, ODataMetadataLevel metadataLevel) { Contract.Assert(builder != null); switch (metadataLevel) { case ODataMetadataLevel.MinimalMetadata: case ODataMetadataLevel.NoMetadata: return action.IsBound && builder.FollowsConventions; case ODataMetadataLevel.FullMetadata: default: // All values already specified; just keeping the compiler happy. return false; } }
internal static bool ShouldSuppressTypeNameSerialization(ODataMetadataLevel metadataLevel) { Contract.Assert(metadataLevel != ODataMetadataLevel.MinimalMetadata); switch (metadataLevel) { case ODataMetadataLevel.NoMetadata: return true; case ODataMetadataLevel.FullMetadata: default: // All values already specified; just keeping the compiler happy. return false; } }
internal static bool ShouldSuppressTypeNameSerialization(object value, ODataMetadataLevel metadataLevel) { Contract.Assert(metadataLevel != ODataMetadataLevel.Default); switch (metadataLevel) { case ODataMetadataLevel.NoMetadata: return true; case ODataMetadataLevel.MinimalMetadata: // Currently open properties are not supported, so the type for each property always appears in // metadata. const bool PropertyTypeAppearsInMetadata = true; return PropertyTypeAppearsInMetadata; case ODataMetadataLevel.FullMetadata: default: // All values already specified; just keeping the compiler happy. return CanTypeBeInferredInJson(value); } }
internal static bool ShouldOmitAction(IEdmFunctionImport action, IEdmModel model, ActionLinkBuilder builder, ODataMetadataLevel metadataLevel) { Contract.Assert(model != null); Contract.Assert(builder != null); switch (metadataLevel) { case ODataMetadataLevel.MinimalMetadata: case ODataMetadataLevel.NoMetadata: return model.IsAlwaysBindable(action) && builder.FollowsConventions; case ODataMetadataLevel.Default: case ODataMetadataLevel.FullMetadata: default: // All values already specified; just keeping the compiler happy. return false; } }
internal static async Task WriteToStreamAsync( Type type, object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, HttpRequest request, IHeaderDictionary requestHeaders, ODataSerializerProvider serializerProvider) { if (model == null) { throw Error.InvalidOperation(SRResources.RequestMustHaveModel); } ODataSerializer serializer = GetSerializer(type, value, request, serializerProvider); ODataPath path = request.ODataFeature().Path; IEdmNavigationSource targetNavigationSource = GetTargetNavigationSource(path, model); HttpResponse response = request.HttpContext.Response; // serialize a response string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(requestHeaders); string annotationFilter = null; if (!string.IsNullOrEmpty(preferHeader)) { ODataMessageWrapper messageWrapper = ODataMessageWrapperHelper.Create(response.Body, response.Headers); messageWrapper.SetHeader(RequestPreferenceHelpers.PreferHeaderName, preferHeader); annotationFilter = messageWrapper.PreferHeader().AnnotationFilter; } IODataResponseMessageAsync responseMessage = ODataMessageWrapperHelper.Create(new StreamWrapper(response.Body), response.Headers, request.GetSubServiceProvider()); if (annotationFilter != null) { responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter; } ODataMessageWriterSettings writerSettings = request.GetWriterSettings(); writerSettings.BaseUri = baseAddress; writerSettings.Version = version; writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; string metadataLink = request.CreateODataLink(MetadataSegment.Instance); if (metadataLink == null) { throw new SerializationException(SRResources.UnableToDetermineMetadataUrl); } // Set this variable if the SelectExpandClause is different from the processed clause on the Query options SelectExpandClause selectExpandDifferentFromQueryOptions = null; ODataQueryOptions queryOptions = request.GetQueryOptions(); SelectExpandClause processedSelectExpandClause = request.ODataFeature().SelectExpandClause; if (queryOptions != null && queryOptions.SelectExpand != null) { if (queryOptions.SelectExpand.ProcessedSelectExpandClause != processedSelectExpandClause) { selectExpandDifferentFromQueryOptions = processedSelectExpandClause; } } else if (processedSelectExpandClause != null) { selectExpandDifferentFromQueryOptions = processedSelectExpandClause; } writerSettings.ODataUri = new ODataUri { ServiceRoot = baseAddress, // TODO: 1604 Convert webapi.odata's ODataPath to ODL's ODataPath, or use ODL's ODataPath. SelectAndExpand = processedSelectExpandClause, Apply = request.ODataFeature().ApplyClause, Path = path }; ODataMetadataLevel metadataLevel = ODataMetadataLevel.Minimal; if (contentType != null) { IEnumerable <KeyValuePair <string, string> > parameters = contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name.ToString(), val.Value.ToString())); metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType.ToString(), parameters); } using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model)) { ODataSerializerContext writeContext = BuildSerializerContext(request); writeContext.NavigationSource = targetNavigationSource; writeContext.Model = model; writeContext.RootElementName = GetRootElementName(path) ?? "root"; writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet; writeContext.Path = path; writeContext.MetadataLevel = metadataLevel; writeContext.QueryOptions = queryOptions; //Set the SelectExpandClause on the context if it was explicitly specified. if (selectExpandDifferentFromQueryOptions != null) { writeContext.SelectExpandClause = selectExpandDifferentFromQueryOptions; } await serializer.WriteObjectAsync(value, type, messageWriter, writeContext).ConfigureAwait(false); } }
private static bool ShouldAddTypeNameAnnotation(ODataMetadataLevel metadataLevel) { switch (metadataLevel) { case ODataMetadataLevel.MinimalMetadata: return false; case ODataMetadataLevel.FullMetadata: case ODataMetadataLevel.NoMetadata: default: return true; } }
private static bool IsDefaultOrFull(ODataMetadataLevel level) { return(level == ODataMetadataLevel.Default || level == ODataMetadataLevel.FullMetadata); }
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 }); }
internal static void WriteToStream( Type type, object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func <IServiceProvider, ODataMessageWrapper> getODataMessageWrapper, Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer, Func <Type, ODataSerializer> getODataPayloadSerializer, Func <ODataSerializerContext> getODataSerializerContext) { if (model == null) { throw Error.InvalidOperation(SRResources.RequestMustHaveModel); } ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer); ODataPath path = internalRequest.Context.Path; IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource; // serialize a response string preferHeader = RequestPreferenceHelpers.GetRequestPreferHeader(internalRequestHeaders); string annotationFilter = null; if (!String.IsNullOrEmpty(preferHeader)) { ODataMessageWrapper messageWrapper = getODataMessageWrapper(null); messageWrapper.SetHeader(RequestPreferenceHelpers.PreferHeaderName, preferHeader); annotationFilter = messageWrapper.PreferHeader().AnnotationFilter; } ODataMessageWrapper responseMessageWrapper = getODataMessageWrapper(internalRequest.RequestContainer); IODataResponseMessage responseMessage = responseMessageWrapper; if (annotationFilter != null) { responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter; } ODataMessageWriterSettings writerSettings = internalRequest.WriterSettings; writerSettings.BaseUri = baseAddress; writerSettings.Version = version; writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType; string metadataLink = internaUrlHelper.CreateODataLink(MetadataSegment.Instance); if (metadataLink == null) { throw new SerializationException(SRResources.UnableToDetermineMetadataUrl); } writerSettings.ODataUri = new ODataUri { ServiceRoot = baseAddress, // TODO: 1604 Convert webapi.odata's ODataPath to ODL's ODataPath, or use ODL's ODataPath. SelectAndExpand = internalRequest.Context.SelectExpandClause, Apply = internalRequest.Context.ApplyClause, Path = (path == null || IsOperationPath(path)) ? null : path.Path, }; ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata; if (contentType != null) { IEnumerable <KeyValuePair <string, string> > parameters = contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name, val.Value)); metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType, parameters); } using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model)) { ODataSerializerContext writeContext = getODataSerializerContext(); writeContext.NavigationSource = targetNavigationSource; writeContext.Model = model; writeContext.RootElementName = GetRootElementName(path) ?? "root"; writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet; writeContext.Path = path; writeContext.MetadataLevel = metadataLevel; writeContext.SelectExpandClause = internalRequest.Context.SelectExpandClause; serializer.WriteObject(value, type, messageWriter, writeContext); } }
internal static bool ShouldSuppressTypeNameSerialization(ODataEntry entry, IEdmEntityType edmType, ODataMetadataLevel metadataLevel) { Contract.Assert(entry != null); switch (metadataLevel) { case ODataMetadataLevel.NoMetadata: return true; case ODataMetadataLevel.FullMetadata: return false; case ODataMetadataLevel.MinimalMetadata: default: // All values already specified; just keeping the compiler happy. string pathTypeName = null; if (edmType != null) { pathTypeName = edmType.FullName(); } string entryTypeName = entry.TypeName; return String.Equals(entryTypeName, pathTypeName, StringComparison.Ordinal); } }
public virtual ODataOperation CreateODataOperation(IEdmOperation operation, ResourceSetContext resourceSetContext, ODataSerializerContext writeContext) { if (operation == null) { throw Error.ArgumentNull("operation"); } if (resourceSetContext == null) { throw Error.ArgumentNull("resourceSetContext"); } if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } ODataMetadataLevel metadataLevel = writeContext.MetadataLevel; IEdmModel model = writeContext.Model; if (metadataLevel != ODataMetadataLevel.FullMetadata) { return(null); } OperationLinkBuilder builder; builder = model.GetOperationLinkBuilder(operation); if (builder == null) { return(null); } Uri target = builder.BuildLink(resourceSetContext); if (target == null) { return(null); } Uri baseUri = new Uri(writeContext.InternalUrlHelper.CreateODataLink(MetadataSegment.Instance)); Uri metadata = new Uri(baseUri, "#" + operation.FullName()); ODataOperation odataOperation; IEdmAction action = operation as IEdmAction; if (action != null) { odataOperation = new ODataAction(); } else { odataOperation = new ODataFunction(); } odataOperation.Metadata = metadata; // Always omit the title in minimal/no metadata modes. ODataResourceSerializer.EmitTitle(model, operation, odataOperation); // Omit the target in minimal/no metadata modes unless it doesn't follow conventions. if (metadataLevel == ODataMetadataLevel.FullMetadata || !builder.FollowsConventions) { odataOperation.Target = target; } return(odataOperation); }
public virtual ODataAction CreateODataAction(IEdmFunctionImport action, EntityInstanceContext entityInstanceContext, ODataMetadataLevel metadataLevel) { if (action == null) { throw Error.ArgumentNull("action"); } if (entityInstanceContext == null) { throw Error.ArgumentNull("entityInstanceContext"); } IEdmModel model = entityInstanceContext.EdmModel; ActionLinkBuilder builder = model.GetActionLinkBuilder(action); if (builder == null) { return null; } if (ShouldOmitAction(action, model, builder, metadataLevel)) { return null; } Uri target = builder.BuildActionLink(entityInstanceContext); if (target == null) { return null; } Uri baseUri = new Uri(entityInstanceContext.Url.ODataLink(new MetadataPathSegment())); Uri metadata = new Uri(baseUri, "#" + CreateMetadataFragment(action, model, metadataLevel)); ODataAction odataAction = new ODataAction { Metadata = metadata, }; bool alwaysIncludeDetails = metadataLevel == ODataMetadataLevel.Default || metadataLevel == ODataMetadataLevel.FullMetadata; // Always omit the title in minimal/no metadata modes (it isn't customizable and thus always follows // conventions). if (alwaysIncludeDetails) { odataAction.Title = action.Name; } // Omit the target in minimal/no metadata modes unless it doesn't follow conventions. if (alwaysIncludeDetails || !builder.FollowsConventions) { odataAction.Target = target; } return odataAction; }
public virtual ODataOperation CreateODataOperation(IEdmOperation operation, FeedContext feedContext, ODataSerializerContext writeContext) { if (operation == null) { throw Error.ArgumentNull("operation"); } if (feedContext == null) { throw Error.ArgumentNull("feedContext"); } if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } ODataMetadataLevel metadataLevel = writeContext.MetadataLevel; IEdmModel model = writeContext.Model; if (metadataLevel != ODataMetadataLevel.FullMetadata) { return(null); } ProcedureLinkBuilder builder; IEdmAction action = operation as IEdmAction; if (action != null) { builder = model.GetActionLinkBuilder(action); } else { builder = model.GetFunctionLinkBuilder((IEdmFunction)operation); } if (builder == null) { return(null); } Uri target = builder.BuildLink(feedContext); if (target == null) { return(null); } Uri baseUri = new Uri(writeContext.Url.CreateODataLink(new MetadataPathSegment())); Uri metadata = new Uri(baseUri, "#" + operation.FullName()); ODataOperation odataOperation; if (action != null) { odataOperation = new ODataAction(); } else { odataOperation = new ODataFunction(); } odataOperation.Metadata = metadata; // Always omit the title in minimal/no metadata modes. ODataEntityTypeSerializer.EmitTitle(model, operation, odataOperation); // Omit the target in minimal/no metadata modes unless it doesn't follow conventions. if (metadataLevel == ODataMetadataLevel.FullMetadata || !builder.FollowsConventions) { odataOperation.Target = target; } return(odataOperation); }
internal static bool ShouldAddTypeNameAnnotation(ODataMetadataLevel metadataLevel) { // Don't interfere with the correct default behavior in non-JSON light formats. // In all JSON light modes, take control of type name 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. return metadataLevel != ODataMetadataLevel.Default; }
/// <summary> /// Constructs a NavigationLink for a particular <see cref="ResourceContext" />, <see cref="IEdmNavigationProperty" /> and <see cref="ODataMetadataLevel" />. /// </summary> public virtual Uri BuildNavigationLink(ResourceContext instanceContext, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel) { if (instanceContext == null) { throw new ArgumentNullException(nameof(instanceContext)); } if (navigationProperty == null) { throw new ArgumentNullException(nameof(navigationProperty)); } NavigationLinkBuilder navigationLinkBuilder; if (_navigationPropertyLinkBuilderLookup.TryGetValue(navigationProperty, out navigationLinkBuilder) && !navigationLinkBuilder.FollowsConventions && (metadataLevel == ODataMetadataLevel.Minimal || metadataLevel == ODataMetadataLevel.Full)) { return(navigationLinkBuilder.Factory(instanceContext, navigationProperty)); } // Return null to let ODL decide when and how to build the navigation link. return(null); }
internal static bool ShouldSuppressTypeNameSerialization(ODataEntry entry, IEdmEntitySet entitySet, ODataMetadataLevel metadataLevel) { Contract.Assert(entry != null); Contract.Assert(metadataLevel != ODataMetadataLevel.Default); switch (metadataLevel) { case ODataMetadataLevel.NoMetadata: return true; case ODataMetadataLevel.FullMetadata: return false; case ODataMetadataLevel.MinimalMetadata: default: // All values already specified; just keeping the compiler happy. string entitySetTypeName = GetElementTypeName(entitySet); string entryTypeName = entry.TypeName; return String.Equals(entryTypeName, entitySetTypeName, StringComparison.Ordinal); } }
/// <summary> /// Constructs a NavigationLink for a particular <see cref="EntityInstanceContext" />, <see cref="IEdmNavigationProperty" /> and <see cref="ODataMetadataLevel" />. /// </summary> public virtual Uri BuildNavigationLink(EntityInstanceContext instanceContext, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel) { if (instanceContext == null) { throw Error.ArgumentNull("instanceContext"); } if (navigationProperty == null) { throw Error.ArgumentNull("navigationProperty"); } NavigationLinkBuilder navigationLinkBuilder; if (!_navigationPropertyLinkBuilderLookup.TryGetValue(navigationProperty, out navigationLinkBuilder)) { if (metadataLevel == ODataMetadataLevel.Default) { throw Error.Argument("navigationProperty", SRResources.NoNavigationLinkFactoryFound, navigationProperty.Name, navigationProperty.DeclaringEntityType(), _navigationSourceName); } return(null); } if (IsDefaultOrFull(metadataLevel) || (IsMinimal(metadataLevel) && !navigationLinkBuilder.FollowsConventions)) { return(navigationLinkBuilder.Factory(instanceContext, navigationProperty)); } else { // client can infer it and didn't ask for it. return(null); } }