public void FindDefaultFunctionType_NoFunctionTypes() => Assert.Throws <ArgumentException>(() => HostingInternals.FindDefaultFunctionType( typeof(FunctionTargetTest), typeof(FunctionsEnvironmentVariablesConfigurationSourceTest), typeof(AbstractHttpFunction), // Abstract, doesn't count typeof(INamedHttpFunction) // Interface, doesn't count ));
public async Task NonGenericCloudEventFunction() { var services = new ServiceCollection(); services.AddLogging(); HostingInternals.AddServicesForFunctionTarget(services, typeof(EventIdRememberingFunction)); var provider = services.BuildServiceProvider(); var function = provider.GetRequiredService <IHttpFunction>(); Assert.Equal(typeof(EventIdRememberingFunction), provider.GetRequiredService <HostingInternals.FunctionTypeProvider>().FunctionType); string eventId = Guid.NewGuid().ToString(); var context = new DefaultHttpContext { Request = { // No actual content, but that's okay. ContentType = "application/json", Headers = { { "ce-specversion", "1.0" }, { "ce-source", "test" }, { "ce-type", "test" }, { "ce-id", eventId } } } }; await function.HandleAsync(context); Assert.Equal(eventId, EventIdRememberingFunction.LastEventId); }
public async Task TargetFunction_EventFunction_Generic() { var services = new ServiceCollection(); services.AddLogging(); HostingInternals.AddServicesForFunctionTarget(services, typeof(StorageCloudEventFunction)); var provider = services.BuildServiceProvider(); var function = provider.GetRequiredService <IHttpFunction>(); Assert.Equal(typeof(StorageCloudEventFunction), provider.GetRequiredService <HostingInternals.FunctionTypeProvider>().FunctionType); var eventId = Guid.NewGuid().ToString(); var context = new DefaultHttpContext { Request = { ContentType = "application/json", Body = new MemoryStream(Encoding.UTF8.GetBytes("{\"name\": \"testdata\"}")), Headers = { { "ce-specversion", "1.0" }, { "ce-source", "test" }, { "ce-type", "test" }, { "ce-id", eventId } } }, }; await function.HandleAsync(context); Assert.Equal(eventId, StorageCloudEventFunction.LastEventId); Assert.Equal("testdata", StorageCloudEventFunction.LastData.Name); }
public void FindDefaultFunctionType_TypedCloudEventFunction() { var expected = typeof(StorageCloudEventFunction); var actual = HostingInternals.FindDefaultFunctionType(typeof(StorageCloudEventFunction)); Assert.Equal(expected, actual); }
/// <summary> /// Uses the functions startup classes specified by <see cref="FunctionsStartupAttribute"/> when configuring the host. /// Startup classes can contribute to logging, configuration sources, services, and application configuration. /// </summary> /// <param name="webHostBuilder">The web host builder to configure.</param> /// <param name="assembly">The assembly to query for attributes specifying startup classes.</param> /// <returns>The original builder, for method chaining.</returns> public static IWebHostBuilder UseFunctionsStartups(this IWebHostBuilder webHostBuilder, Assembly assembly) { foreach (var startup in HostingInternals.GetStartups(assembly)) { webHostBuilder.UseFunctionsStartup(startup); } return(webHostBuilder); }
public void GetStartups() { var startups = HostingInternals.GetStartups(typeof(FunctionsStartupTest).Assembly); var actualTypes = startups.Select(startup => startup.GetType()).ToArray(); // TestStartup2 comes before TestStartup1 due to the Order properties. var expectedTypes = new[] { typeof(TestStartup2), typeof(TestStartup1) }; Assert.Equal(expectedTypes, actualTypes); }
public void NonInstantiableFunctionType() { var services = new ServiceCollection(); services.AddLogging(); HostingInternals.AddServicesForFunctionTarget(services, typeof(NonInstantiableFunction)); // We only find out when we try to get an IHttpFunction that we can't actually instantiate the function. var provider = services.BuildServiceProvider(); Assert.Throws <InvalidOperationException>(() => provider.GetRequiredService <IHttpFunction>()); }
public void FindDefaultFunctionType_SingleFunctionType() { var expected = typeof(DefaultFunction); var actual = HostingInternals.FindDefaultFunctionType( typeof(FunctionTargetTest), typeof(FunctionsEnvironmentVariablesConfigurationSourceTest), typeof(AbstractHttpFunction), // Abstract, doesn't count typeof(INamedHttpFunction), // Interface, doesn't count typeof(DefaultFunction)); Assert.Equal(expected, actual); }
public async Task DependencyScopingIntegration() { // We create a test server, but we bypass most of it - we're really just using it to create // services we can execute an HttpContext against. (This is simpler than interacting via // an actual HTTP request and response.) var host = Host.CreateDefaultBuilder() .ConfigureWebHost(webHostBuilder => webHostBuilder .ConfigureServices((context, services) => services.AddFunctionTarget <DependencyTestFunction>()) .UseFunctionsStartup(new DependencyStartup()) .Configure((context, app) => app.UseFunctionsFramework(context)) .UseTestServer()) .Build(); var testServer = host.GetTestServer(); // Execute two requests, and remember the dependencies we saw in each of them. var request1Deps = await MakeRequestAsync(); var request2Deps = await MakeRequestAsync(); // We should get dependencies on every call Assert.NotNull(request1Deps.dep1); Assert.NotNull(request1Deps.dep2); Assert.NotNull(request2Deps.dep1); Assert.NotNull(request2Deps.dep2); // Dependency1 is registered as a singleton, so we should have the same value in both requests. Assert.Same(request1Deps.dep1, request2Deps.dep1); // Dependency2 is registered as a scoped service, so we should have different values in each request. Assert.NotSame(request1Deps.dep2, request2Deps.dep2); async Task <(object dep1, object dep2)> MakeRequestAsync() { using (var scope = testServer.Services.CreateScope()) { var context = new DefaultHttpContext { RequestServices = scope.ServiceProvider }; await HostingInternals.Execute(context); return(context.Items[DependencyTestFunction.Dependency1Key], context.Items[DependencyTestFunction.Dependency2Key]); } } }
/// <summary> /// Internal method called directly by EntryPoint, so that it can validate that the appropriate startup /// classes to use haven't changed as a result of using the startup classes that *have* been executed. /// </summary> internal static IApplicationBuilder UseFunctionsFramework(this IApplicationBuilder app, WebHostBuilderContext context, bool validateStartups) => HostingInternals.ConfigureApplication(context, app, validateStartups);
/// <summary> /// Adds the given startup class into the service collection. This method can be called multiple times, and all startup /// classes will be used. /// Startup classes can contribute to logging, configuration sources, services, and application configuration. /// </summary> /// <param name="webHostBuilder">The web host builder to configure.</param> /// <param name="startup">The startup class to use.</param> /// <returns>The original builder, for method chaining.</returns> public static IWebHostBuilder UseFunctionsStartup( this IWebHostBuilder webHostBuilder, FunctionsStartup startup) => HostingInternals.AddStartup(webHostBuilder, startup);
/// <summary> /// Adds services required for the Functions Framework to use the specified function target type, which must /// implement one of the Functions Framework function interfaces. /// </summary> /// <remarks> /// If this method completes successfully, a binding for <see cref="IHttpFunction"/> will definitely /// have been added to the service collection. Other bindings may also be present, in order to adapt /// the function to <see cref="IHttpFunction"/>. /// </remarks> /// <param name="services">The service collection to configure.</param> /// <param name="type">The target function type.</param> /// <returns>The original service collection, for method chaining.</returns> public static IServiceCollection AddFunctionTarget(this IServiceCollection services, Type type) => HostingInternals.AddServicesForFunctionTarget(services, type);
public void FunctionTypeImplementsMultipleInterfaces() { var services = new ServiceCollection(); Assert.Throws <InvalidOperationException>(() => HostingInternals.AddServicesForFunctionTarget(services, typeof(MultipleCloudEventTypes))); }
/// <summary> /// Adds a configuration source for the Functions Framework based on command line arguments. /// </summary> /// <param name="builder">The configuration builder to add the source to.</param> /// <param name="args">The command line arguments to use for configuration.</param> /// <returns>The original builder, for method chaining.</returns> public static IConfigurationBuilder AddFunctionsCommandLine(this IConfigurationBuilder builder, string[] args) => HostingInternals.AddCommandLineArguments(builder, args);
/// <summary> /// Configures the given application builder to use the Functions Framework. /// This method executes the <see cref="FunctionsStartup.Configure(WebHostBuilderContext, AspNetCore.Builder.IApplicationBuilder)"/> /// method on any registered functions startup classes before adding handlers that /// return "not found" responses for fixed paths (e.g. "favicon.ico") and setting the terminal /// handler to execute the target function. /// </summary> /// <remarks> /// This method requires (at a minimum) that a function target has been registered, /// as it uses the <see cref="IHttpFunction"/> interface to handle requests. /// The target is typically registered using the /// <see cref="FunctionsFrameworkServiceCollectionExtensions.AddFunctionTarget(IServiceCollection, WebHostBuilderContext, System.Reflection.Assembly)"/> /// method or another <c>AddFunctionTarget</c> overload. /// </remarks> /// <param name="app">The application builder to configure.</param> /// <param name="context">The context of the web host builder being configured.</param> /// <returns>The original builder, for method chaining.</returns> public static IApplicationBuilder UseFunctionsFramework(this IApplicationBuilder app, WebHostBuilderContext context) => HostingInternals.ConfigureApplication(context, app);
public void NonFunctionType() { var services = new ServiceCollection(); Assert.Throws <InvalidOperationException>(() => HostingInternals.AddServicesForFunctionTarget(services, typeof(NonFunction))); }
public void FindDefaultFunctionType_MultipleCloudEventTypesFunction() => Assert.Throws <ArgumentException>(() => HostingInternals.FindDefaultFunctionType( typeof(MultipleCloudEventTypes)));
public void FindDefaultFunctionType_MultipleFunctionTypes() => Assert.Throws <ArgumentException>(() => HostingInternals.FindDefaultFunctionType( typeof(DefaultFunction), typeof(EventIdRememberingFunction)));
/// <summary> /// Adds required services to the service collection within the given web host builder for the Functions Framework /// to use a target function from the given assembly. If the Functions Framework configuration within the web host /// builder (typically provided by command line arguments or environment variables) /// does not specify a target function, the assembly is scanned for a single compatible function type. /// </summary> /// <remarks> /// If this method completes successfully, a binding for <see cref="IHttpFunction"/> will definitely /// have been added to the service collection. Other bindings may also be present, in order to adapt /// the function to <see cref="IHttpFunction"/>. /// </remarks> /// <param name="services">The service collection to configure.</param> /// <param name="context">The context of the web host builder being configured.</param> /// <param name="assembly">The assembly expected to contain the Functions Framework target function.</param> /// <returns>The original builder, for method chaining.</returns> public static IServiceCollection AddFunctionTarget(this IServiceCollection services, WebHostBuilderContext context, Assembly assembly) => HostingInternals.AddServicesForFunctionTarget(services, HostingInternals.GetFunctionTarget(context, assembly));