internal static IEdmModel GetEdmModel(this ApiActionDescriptor actionDescriptor, Type entityClrType) { if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } if (entityClrType == null) { throw Error.ArgumentNull("entityClrType"); } // save the EdmModel to the action descriptor return actionDescriptor.Properties.GetOrAdd(EdmModelKey + entityClrType.FullName, _ => { // It's safe to create HttpConfiguration, since it's used as an assembly Resolver. //ODataConventionModelBuilder builder = new ODataConventionModelBuilder(new HttpConfiguration(), isQueryCompositionMode: true); ODataConventionModelBuilder builder = new ODataConventionModelBuilder(new HttpConfiguration()); EntityTypeConfiguration entityTypeConfiguration = builder.AddEntity(entityClrType); builder.AddEntitySet(entityClrType.Name, entityTypeConfiguration); IEdmModel edmModel = builder.GetEdmModel(); Contract.Assert(edmModel != null); return edmModel; }) as IEdmModel; }
internal static IEdmModel GetEdmModel(this HttpActionDescriptor actionDescriptor, Type entityClrType) { if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } if (entityClrType == null) { throw Error.ArgumentNull("entityClrType"); } // save the EdmModel to the action descriptor return actionDescriptor.Properties.GetOrAdd(EdmModelKey + entityClrType.FullName, _ => { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); IEntityTypeConfiguration entityTypeConfiguration = builder.AddEntity(entityClrType); builder.AddEntitySet(entityClrType.Name, entityTypeConfiguration); return builder.GetEdmModel(); }) as IEdmModel; }
public void DollarMetadata_Works_WithPrincipalKeyOnBaseType_ButBaseTypeNotInEdmModel(Type entityType) { // Arrange const string expect = " <Association Name=\"System_Web_Http_OData_Formatter_DependentEntity_DerivedProp_System_Web_Http_OData_Formatter_DerivedPrincipalEntity_DerivedPropPartner\">\r\n" + " <End Type=\"System.Web.Http.OData.Formatter.DerivedPrincipalEntity\" Role=\"DerivedProp\" Multiplicity=\"0..1\" />\r\n" + " <End Type=\"System.Web.Http.OData.Formatter.DependentEntity\" Role=\"DerivedPropPartner\" Multiplicity=\"0..1\" />\r\n" + " <ReferentialConstraint>\r\n" + " <Principal Role=\"DerivedProp\">\r\n" + " <PropertyRef Name=\"Id\" />\r\n" + " </Principal>\r\n" + " <Dependent Role=\"DerivedPropPartner\">\r\n" + " <PropertyRef Name=\"DerivedPrincipalEntityId\" />\r\n" + " </Dependent>\r\n" + " </ReferentialConstraint>\r\n" + " </Association>"; ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.AddEntity(entityType); builder.Entity <DependentEntity>(); IEdmModel model = builder.GetEdmModel(); HttpServer server = new HttpServer(); server.Configuration.Routes.MapODataServiceRoute("odata", "odata", model); HttpClient client = new HttpClient(server); // Act HttpResponseMessage response = client.GetAsync("http://localhost/odata/$metadata").Result; // Assert Assert.True(response.IsSuccessStatusCode); Assert.Equal("application/xml", response.Content.Headers.ContentType.MediaType); Assert.Contains(expect, response.Content.ReadAsStringAsync().Result); }
public void GetEdmModel_PropertyWithDatabaseAttribute_ConfigAnnotationOnPropertyOnEntityType() { // Arrange MockType type = new MockType("Entity") .Property(typeof(int), "ID", new DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)) .Property(typeof(int?), "Count"); ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.AddEntity(type); // Act IEdmModel model = builder.GetEdmModel(); // Assert IEdmEntityType entity = model.AssertHasEntityType(type); IEdmStructuralProperty idProperty = entity.AssertHasPrimitiveProperty(model, "ID", EdmPrimitiveTypeKind.Int32, isNullable: false); var idAnnotation = model.GetAnnotationValue <EdmStringConstant>( idProperty, StoreGeneratedPatternAnnotation.AnnotationsNamespace, StoreGeneratedPatternAnnotation.AnnotationName); Assert.Equal(DatabaseGeneratedOption.Identity.ToString(), idAnnotation.Value); IEdmStructuralProperty countProperty = entity.AssertHasPrimitiveProperty(model, "Count", EdmPrimitiveTypeKind.Int32, isNullable: true); var countAnnotation = model.GetAnnotationValue <EdmStringConstant>( countProperty, StoreGeneratedPatternAnnotation.AnnotationsNamespace, StoreGeneratedPatternAnnotation.AnnotationName); Assert.Null(countAnnotation); }
internal static IEdmModel GetEdmModel(this HttpActionDescriptor actionDescriptor, Type entityClrType) { if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } if (entityClrType == null) { throw Error.ArgumentNull("entityClrType"); } // save the EdmModel to the action descriptor return actionDescriptor.Properties.GetOrAdd(ModelKeyPrefix + entityClrType.FullName, _ => { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(actionDescriptor.Configuration, isQueryCompositionMode: true); EntityTypeConfiguration entityTypeConfiguration = builder.AddEntity(entityClrType); builder.AddEntitySet(entityClrType.Name, entityTypeConfiguration); IEdmModel edmModel = builder.GetEdmModel(); Contract.Assert(edmModel != null); return edmModel; }) as IEdmModel; }
public void ApplyTo_Picks_DefaultOrder(string oDataQuery, Type elementType, string expectedExpression) { IQueryable query = Array.CreateInstance(elementType, 0).AsQueryable(); var modelBuilder = new ODataConventionModelBuilder(); modelBuilder.AddEntitySet("entityset", modelBuilder.AddEntity(elementType)); var model = modelBuilder.GetEdmModel(); var message = new HttpRequestMessage( HttpMethod.Get, new Uri("http://server/service/entityset?" + oDataQuery) ); var options = new ODataQueryOptions(new ODataQueryContext(model, elementType, "entityset"), message); IQueryable finalQuery = options.ApplyTo(query); string queryExpression = finalQuery.Expression.ToString(); queryExpression = queryExpression.Substring(queryExpression.IndexOf("OrderBy")); Assert.Equal(queryExpression, expectedExpression); }
public void GetEdmModel_PropertyWithDatabaseAttribute_ConfigAnnotationOnPropertyOnEntityType() { // Arrange MockType type = new MockType("Entity") .Property(typeof(int), "ID", new DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)) .Property(typeof(int?), "Count"); ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.AddEntity(type); // Act IEdmModel model = builder.GetEdmModel(); // Assert IEdmEntityType entity = model.AssertHasEntityType(type); IEdmStructuralProperty idProperty = entity.AssertHasPrimitiveProperty(model, "ID", EdmPrimitiveTypeKind.Int32, isNullable: false); var idAnnotation = model.GetAnnotationValue<EdmStringConstant>( idProperty, StoreGeneratedPatternAnnotation.AnnotationsNamespace, StoreGeneratedPatternAnnotation.AnnotationName); Assert.Equal(DatabaseGeneratedOption.Identity.ToString(), idAnnotation.Value); IEdmStructuralProperty countProperty = entity.AssertHasPrimitiveProperty(model, "Count", EdmPrimitiveTypeKind.Int32, isNullable: true); var countAnnotation = model.GetAnnotationValue<EdmStringConstant>( countProperty, StoreGeneratedPatternAnnotation.AnnotationsNamespace, StoreGeneratedPatternAnnotation.AnnotationName); Assert.Null(countAnnotation); }
/// <summary> /// Maps ODataService Route and registers routes for any controller actions that use a [Route] attribute /// </summary> /// <param name="config">The configuration.</param> public static void Register( HttpConfiguration config ) { config.EnableCors( new Rock.Rest.EnableCorsFromOriginAttribute() ); config.Filters.Add( new Rock.Rest.Filters.ValidateAttribute() ); config.Services.Replace( typeof( IExceptionLogger ), new RockApiExceptionLogger() ); config.Services.Replace( typeof( IExceptionHandler ), new RockApiExceptionHandler() ); config.Formatters.Insert( 0, new Rock.Utility.RockJsonMediaTypeFormatter() ); // Add API route for dataviews config.Routes.MapHttpRoute( name: "DataViewApi", routeTemplate: "api/{controller}/DataView/{id}", defaults: new { action = "DataView" } ); // finds all [Route] attributes on REST controllers and creates the routes config.MapHttpAttributeRoutes(); // Add any custom api routes foreach ( var type in Rock.Reflection.FindTypes( typeof( Rock.Rest.IHasCustomRoutes ) ) ) { try { var controller = (Rock.Rest.IHasCustomRoutes)Activator.CreateInstance( type.Value ); if ( controller != null ) { controller.AddRoutes( RouteTable.Routes ); } } catch { // ignore, and skip adding routes if the controller raises an exception } } //// Add Default API Service routes //// Instead of being able to use one default route that gets action from http method, have to //// have a default route for each method so that other actions do not match the default (i.e. DataViews). //// Also, this will make controller routes case-insensitive (vs the odata routing) config.Routes.MapHttpRoute( name: "DefaultApiGetById", routeTemplate: "api/{controller}/{id}", defaults: new { action = "GetById" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiGetFunction", routeTemplate: "api/{controller}({key})", defaults: new { action = "GET" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiGetList", routeTemplate: "api/{controller}", defaults: new { action = "GET" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiPut", routeTemplate: "api/{controller}/{id}", defaults: new { action = "PUT", id = System.Web.Http.RouteParameter.Optional }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "PUT", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiPost", routeTemplate: "api/{controller}/{id}", defaults: new { action = "POST", id = System.Web.Http.RouteParameter.Optional, controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "POST", "OPTIONS" } ) } ); config.Routes.MapHttpRoute( name: "DefaultApiDelete", routeTemplate: "api/{controller}/{id}", defaults: new { action = "DELETE", id = System.Web.Http.RouteParameter.Optional }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "DELETE", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); // build OData model and create service route (mainly for metadata) ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); var entityTypeList = Reflection.FindTypes( typeof( Rock.Data.IEntity ) ) .Where( a => !a.Value.IsAbstract && ( a.Value.GetCustomAttribute<NotMappedAttribute>() == null ) && (a.Value.GetCustomAttribute<DataContractAttribute>() != null) ) .OrderBy( a => a.Key ).Select( a => a.Value ); foreach ( var entityType in entityTypeList ) { var entityTypeConfig = builder.AddEntity( entityType ); var entitySetConfig = builder.AddEntitySet( entityType.Name.Pluralize(), entityTypeConfig ); } config.Routes.MapODataServiceRoute( "api", "api", builder.GetEdmModel() ); }
public static IEdmModel GetEdmModel(HttpConfiguration configuration) { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(configuration); foreach (var type in Creator.EntityTypes) { var entity = builder.AddEntity(type); builder.AddEntitySet(type.Name, entity); } return builder.GetEdmModel(); }
public void DollarMetadata_Works_WithPrincipalKeyOnBaseType_ButBaseTypeNotInEdmModel(Type entityType) { // Arrange const string expect = " <Association Name=\"System_Web_Http_OData_Formatter_DependentEntity_DerivedProp_System_Web_Http_OData_Formatter_DerivedPrincipalEntity_DerivedPropPartner\">\r\n" + " <End Type=\"System.Web.Http.OData.Formatter.DerivedPrincipalEntity\" Role=\"DerivedProp\" Multiplicity=\"0..1\" />\r\n" + " <End Type=\"System.Web.Http.OData.Formatter.DependentEntity\" Role=\"DerivedPropPartner\" Multiplicity=\"0..1\" />\r\n" + " <ReferentialConstraint>\r\n" + " <Principal Role=\"DerivedProp\">\r\n" + " <PropertyRef Name=\"Id\" />\r\n" + " </Principal>\r\n" + " <Dependent Role=\"DerivedPropPartner\">\r\n" + " <PropertyRef Name=\"DerivedPrincipalEntityId\" />\r\n" + " </Dependent>\r\n" + " </ReferentialConstraint>\r\n" + " </Association>"; ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.AddEntity(entityType); builder.Entity<DependentEntity>(); IEdmModel model = builder.GetEdmModel(); HttpServer server = new HttpServer(); server.Configuration.Routes.MapODataServiceRoute("odata", "odata", model); HttpClient client = new HttpClient(server); // Act HttpResponseMessage response = client.GetAsync("http://localhost/odata/$metadata").Result; // Assert Assert.True(response.IsSuccessStatusCode); Assert.Equal("application/xml", response.Content.Headers.ContentType.MediaType); Assert.Contains(expect, response.Content.ReadAsStringAsync().Result); }
/// <summary> /// Maps ODataService Route and registers routes for any controller actions that use a [Route] attribute /// </summary> /// <param name="config">The configuration.</param> public static void Register( HttpConfiguration config ) { config.EnableCors( new Rock.Rest.EnableCorsFromOriginAttribute() ); config.Filters.Add( new Rock.Rest.Filters.ValidateAttribute() ); config.Services.Replace( typeof( IExceptionLogger ), new RockApiExceptionLogger() ); config.Services.Replace( typeof( IExceptionHandler ), new RockApiExceptionHandler() ); config.Formatters.Insert( 0, new Rock.Utility.RockJsonMediaTypeFormatter() ); // Change DateTimeZoneHandling to Unspecified instead of the default of RoundTripKind since Rock doesn't store dates in a timezone aware format // So, since Rock doesn't do TimeZones, we don't want Transmission of DateTimes to specify TimeZone either. config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Unspecified; // Add API route for dataviews config.Routes.MapHttpRoute( name: "DataViewApi", routeTemplate: "api/{controller}/DataView/{id}", defaults: new { action = "DataView" } ); // Add API route for Launching a Workflow config.Routes.MapHttpRoute( name: "LaunchWorkflowApi", routeTemplate: "api/{controller}/LaunchWorkflow/{id}", defaults: new { action = "LaunchWorkflow" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "POST" } ), } ); // Add API route for DeleteAttributeValue config.Routes.MapHttpRoute( name: "DeleteAttributeValueApi", routeTemplate: "api/{controller}/AttributeValue/{id}", defaults: new { action = "DeleteAttributeValue" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "DELETE" } ), } ); // Add API route for SetAttributeValue config.Routes.MapHttpRoute( name: "SetAttributeValueApi", routeTemplate: "api/{controller}/AttributeValue/{id}", defaults: new { action = "SetAttributeValue" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "POST" } ), } ); // Add API route for setting context config.Routes.MapHttpRoute( name: "SetContextApi", routeTemplate: "api/{controller}/SetContext/{id}", defaults: new { action = "SetContext" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "PUT", "OPTIONS" } ), } ); // finds all [Route] attributes on REST controllers and creates the routes config.MapHttpAttributeRoutes(); // Add any custom api routes foreach ( var type in Rock.Reflection.FindTypes( typeof( Rock.Rest.IHasCustomRoutes ) ) ) { try { var controller = (Rock.Rest.IHasCustomRoutes)Activator.CreateInstance( type.Value ); if ( controller != null ) { controller.AddRoutes( RouteTable.Routes ); } } catch { // ignore, and skip adding routes if the controller raises an exception } } //// Add Default API Service routes //// Instead of being able to use one default route that gets action from http method, have to //// have a default route for each method so that other actions do not match the default (i.e. DataViews). //// Also, this will make controller routes case-insensitive (vs the odata routing) config.Routes.MapHttpRoute( name: "DefaultApiGetById", routeTemplate: "api/{controller}/{id}", defaults: new { action = "GetById" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiGetFunction", routeTemplate: "api/{controller}({key})", defaults: new { action = "GET" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiGetList", routeTemplate: "api/{controller}", defaults: new { action = "GET" }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "GET", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiPut", routeTemplate: "api/{controller}/{id}", defaults: new { action = "PUT", id = System.Web.Http.RouteParameter.Optional }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "PUT", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiPatch", routeTemplate: "api/{controller}/{id}", defaults: new { action = "PATCH", id = System.Web.Http.RouteParameter.Optional }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "PATCH", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApiPost", routeTemplate: "api/{controller}/{id}", defaults: new { action = "POST", id = System.Web.Http.RouteParameter.Optional, controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "POST", "OPTIONS" } ) } ); config.Routes.MapHttpRoute( name: "DefaultApiDelete", routeTemplate: "api/{controller}/{id}", defaults: new { action = "DELETE", id = System.Web.Http.RouteParameter.Optional }, constraints: new { httpMethod = new HttpMethodConstraint( new string[] { "DELETE", "OPTIONS" } ), controllerName = new Rock.Rest.Constraints.ValidControllerNameConstraint() } ); // build OData model and create service route (mainly for metadata) ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); var entityTypeList = Reflection.FindTypes( typeof( Rock.Data.IEntity ) ) .Where( a => !a.Value.IsAbstract && ( a.Value.GetCustomAttribute<NotMappedAttribute>() == null ) && ( a.Value.GetCustomAttribute<DataContractAttribute>() != null ) ) .OrderBy( a => a.Key ).Select( a => a.Value ); foreach ( var entityType in entityTypeList ) { var entityTypeConfig = builder.AddEntity( entityType ); var tableAttribute = entityType.GetCustomAttribute<TableAttribute>(); string name; if ( tableAttribute != null ) { name = tableAttribute.Name.Pluralize(); } else { name = entityType.Name.Pluralize(); } var entitySetConfig = builder.AddEntitySet( name, entityTypeConfig ); } config.Routes.MapODataServiceRoute( "api", "api", builder.GetEdmModel() ); }