From 85989c0530c637decfc7b2418c62c007ab6ee12d Mon Sep 17 00:00:00 2001 From: Wannes Gennar Date: Thu, 15 Jan 2026 07:49:29 +0100 Subject: [PATCH 1/4] feat: setup Permify hosting integration --- .github/workflows/tests.yaml | 9 +- CommunityToolkit.Aspire.slnx | 5 ++ .../AppHost.cs | 5 ++ ...lkit.Aspire.Hosting.Permify.AppHost.csproj | 15 ++++ .../Properties/launchSettings.json | 31 +++++++ .../appsettings.json | 9 ++ ...unityToolkit.Aspire.Hosting.Permify.csproj | 13 +++ .../PermifyContainerImageTags.cs | 16 ++++ .../PermifyHostingExtensions.cs | 82 +++++++++++++++++++ .../PermifyResource.cs | 19 +++++ .../README.md | 41 ++++++++++ ...oolkit.Aspire.Hosting.Permify.Tests.csproj | 9 ++ 12 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs create mode 100644 examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/CommunityToolkit.Aspire.Hosting.Permify.AppHost.csproj create mode 100644 examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/Properties/launchSettings.json create mode 100644 examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/appsettings.json create mode 100644 src/CommunityToolkit.Aspire.Hosting.Permify/CommunityToolkit.Aspire.Hosting.Permify.csproj create mode 100644 src/CommunityToolkit.Aspire.Hosting.Permify/PermifyContainerImageTags.cs create mode 100644 src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs create mode 100644 src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs create mode 100644 src/CommunityToolkit.Aspire.Hosting.Permify/README.md create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 38a3df7e6..0c2a6d6ea 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -35,9 +35,8 @@ jobs: Hosting.Flyway.Tests, Hosting.GoFeatureFlag.Tests, Hosting.Golang.Tests, - Hosting.JavaScript.Extensions.Tests, Hosting.Java.Tests, - Hosting.k6.Tests, + Hosting.JavaScript.Extensions.Tests, Hosting.Keycloak.Extensions.Tests, Hosting.KurrentDB.Tests, Hosting.LavinMQ.Tests, @@ -51,6 +50,7 @@ jobs: Hosting.Ollama.Tests, Hosting.OpenTelemetryCollector.Tests, Hosting.PapercutSmtp.Tests, + Hosting.Permify.Tests, Hosting.PostgreSQL.Extensions.Tests, Hosting.PowerShell.Tests, Hosting.Python.Extensions.Tests, @@ -60,12 +60,13 @@ jobs: Hosting.Sftp.Tests, Hosting.Solr.Tests, Hosting.SqlDatabaseProjects.Tests, - Hosting.Sqlite.Tests, Hosting.SqlServer.Extensions.Tests, + Hosting.Sqlite.Tests, Hosting.Stripe.Tests, Hosting.SurrealDb.Tests, Hosting.Umami.Tests, - + Hosting.k6.Tests, + # Client integration tests GoFeatureFlag.Tests, KurrentDB.Tests, diff --git a/CommunityToolkit.Aspire.slnx b/CommunityToolkit.Aspire.slnx index 9d30dd237..9fd407473 100644 --- a/CommunityToolkit.Aspire.slnx +++ b/CommunityToolkit.Aspire.slnx @@ -135,6 +135,9 @@ + + + @@ -214,6 +217,7 @@ + @@ -275,6 +279,7 @@ + diff --git a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs new file mode 100644 index 000000000..63f021edf --- /dev/null +++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs @@ -0,0 +1,5 @@ +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddPermify("permify"); + +builder.Build().Run(); \ No newline at end of file diff --git a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/CommunityToolkit.Aspire.Hosting.Permify.AppHost.csproj b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/CommunityToolkit.Aspire.Hosting.Permify.AppHost.csproj new file mode 100644 index 000000000..a5ca321dc --- /dev/null +++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/CommunityToolkit.Aspire.Hosting.Permify.AppHost.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + enable + enable + 246f588a-9d7e-4cea-9959-c81ec1fa7900 + + + + + + + diff --git a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/Properties/launchSettings.json b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/Properties/launchSettings.json new file mode 100644 index 000000000..bbd59ccb1 --- /dev/null +++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17093;http://localhost:15041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21069", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23152", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22049" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15041", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19262", + "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18202", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20117" + } + } + } +} diff --git a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/appsettings.json b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/appsettings.json new file mode 100644 index 000000000..31c092aa4 --- /dev/null +++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/CommunityToolkit.Aspire.Hosting.Permify.csproj b/src/CommunityToolkit.Aspire.Hosting.Permify/CommunityToolkit.Aspire.Hosting.Permify.csproj new file mode 100644 index 000000000..84fef05cb --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/CommunityToolkit.Aspire.Hosting.Permify.csproj @@ -0,0 +1,13 @@ + + + + A .NET Aspire host integration for Permify. + permify zanzibar rbac abac access-control least-privilege rebac + + + + + + + + diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyContainerImageTags.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyContainerImageTags.cs new file mode 100644 index 000000000..0de9d4caa --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyContainerImageTags.cs @@ -0,0 +1,16 @@ +namespace CommunityToolkit.Aspire.Hosting.Permify; + +/// +/// Contains the (default) container tags for Permify. +/// +internal static class PermifyContainerImageTags +{ + /// Github Container Registry + public const string Registry = "ghcr.io"; + + /// permify/permify + public const string Image = "permify/permify"; + + /// v1.6.0 + public const string Tag = "v1.6.0"; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs new file mode 100644 index 000000000..ddae13a5a --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs @@ -0,0 +1,82 @@ +using Aspire.Hosting.ApplicationModel; +using CommunityToolkit.Aspire.Hosting.Permify; + +namespace Aspire.Hosting; + +/// +/// Provides extension methods for adding Permify to an . +/// +public static class PermifyHostingExtensions +{ + /// + /// Adds a Permify resource to the application. + /// + /// The distributed application builder. + /// The name of the Permify resource. + /// The HTTP port for Permify. + /// The gRPC port for Permify. + public static IResourceBuilder AddPermify( + this IDistributedApplicationBuilder builder, + [ResourceName] string name, + int? httpPort = null, + int? grpcPort = null + ) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + var resource = new PermifyResource(name); + var permifyBuilder = builder.AddResource(resource) + .WithImage(PermifyContainerImageTags.Image) + .WithImageTag(PermifyContainerImageTags.Tag) + .WithImageRegistry(PermifyContainerImageTags.Registry) + // Configure endpoints + .WithHttpsEndpoint( + targetPort: 8080, + port: httpPort, + name: PermifyResource.HttpEndpointName + ) + .WithEnvironment("PERMIFY_HTTP_ENABLED", "true") + .WithEnvironment("PERMIFY_HTTP_PORT", "8080") + .WithHttpHealthCheck("/healthz") + .WithHttpsEndpoint( + targetPort: 8081, + port: grpcPort, + name: PermifyResource.GrpcEndpointName + ) + .WithEnvironment("PERMIFY_GRPC_ENABLED", "true") + .WithEnvironment("PERMIFY_GRPC_PORT", "8081") + // Configure OTLP + .WithOtlpExporter() + .WithEnvironment("PERMIFY_TRACER_ENABLED", "true") + .WithEnvironment("PERMIFY_TRACER_EXPORTER", "otlp") + .WithEnvironment("PERMIFY_METER_ENABLED", "true") + .WithEnvironment("PERMIFY_METER_EXPORTER", "otlp") + .WithEnvironment(ctx => + { + // TODO: Permify requires the endpoint to *just* be the host + port + // it cannot contain a scheme, which makes it difficult to use HostUrl + ctx.EnvironmentVariables["PERMIFY_TRACER_ENDPOINT"] = string.Empty; + ctx.EnvironmentVariables["PERMIFY_METER_ENDPOINT"] = string.Empty; + }); + +#pragma warning disable ASPIRECERTIFICATES001 + permifyBuilder.WithHttpsCertificateConfiguration(ctx => + { + // Configure HTTPS + ctx.EnvironmentVariables["PERMIFY_HTTP_TLS_ENABLED"] = true; + ctx.EnvironmentVariables["PERMIFY_HTTP_TLS_CERT_PATH"] = ctx.CertificatePath; + ctx.EnvironmentVariables["PERMIFY_HTTP_TLS_KEY_PATH"] = ctx.KeyPath; + + // Configure gRPC + ctx.EnvironmentVariables["PERMIFY_GRPC_TLS_ENABLED"] = true; + ctx.EnvironmentVariables["PERMIFY_GRPC_TLS_CERT_PATH"] = ctx.CertificatePath; + ctx.EnvironmentVariables["PERMIFY_GRPC_TLS_KEY_PATH"] = ctx.KeyPath; + + return Task.CompletedTask; + }); +#pragma warning restore ASPIRECERTIFICATES001 + + return permifyBuilder; + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs new file mode 100644 index 000000000..5b5903246 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs @@ -0,0 +1,19 @@ +using Aspire.Hosting.ApplicationModel; + +namespace CommunityToolkit.Aspire.Hosting.Permify; + +/// +/// Resource for the Permify API server. +/// +public sealed class PermifyResource(string name) : ContainerResource(name) +{ + /// + /// The name of the HTTP API endpoint. + /// + public const string HttpEndpointName = "http"; + + /// + /// The name of the gRPC API endpoint. + /// + public const string GrpcEndpointName = "grpc"; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/README.md b/src/CommunityToolkit.Aspire.Hosting.Permify/README.md new file mode 100644 index 000000000..a121decb1 --- /dev/null +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/README.md @@ -0,0 +1,41 @@ +# CommunityToolkit.Aspire.Hosting.Permify library + +Provides extension methods and resource definitions for the .NET Aspire AppHost to support running [Permify](https://permify.co/) containers +with optional watch support. + +## Getting Started + +### Install the package + +In your AppHost project, install the package using the following command: + +```dotnetcli +dotnet add package CommunityToolkit.Aspire.Hosting.Permify +``` + +### Example usage + +Then, in the _Program.cs_ file of `AppHost`, define a Permify resource, then call `AddPermify`: + +```csharp +var permify = builder.AddPermify("permify"); +``` + +If you want to enable watch support you'll need a Postgres database: + +```csharp +var postgres = builder.AddPostgres("postgres") + .AddDatabase("permify-db"); + +var permify = builder.AddPermify("permify") + .WithWatchSupport(postgres); +``` + +## Additional Information + +https://learn.microsoft.com/dotnet/aspire/community-toolkit/ollama + +## Feedback & contributing + +https://github.com/CommunityToolkit/Aspire + diff --git a/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj new file mode 100644 index 000000000..237d66167 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + From a698cd11a6640d0bea2bff63bd79cea632dd6f68 Mon Sep 17 00:00:00 2001 From: Wannes Gennar Date: Thu, 15 Jan 2026 08:08:02 +0100 Subject: [PATCH 2/4] feat: add watch support for Permify --- .../AppHost.cs | 6 +- .../PermifyHostingExtensions.cs | 86 +++++++++++++++++-- .../README.md | 1 + 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs index 63f021edf..d1ec37d81 100644 --- a/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs +++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs @@ -1,5 +1,9 @@ var builder = DistributedApplication.CreateBuilder(args); -builder.AddPermify("permify"); +var database = builder.AddPostgres("permify-postgres") + .AddDatabase("permify-db"); + +builder.AddPermify("permify") + .WithWatchSupport(database); builder.Build().Run(); \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs index ddae13a5a..a4aa366c9 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs @@ -39,13 +39,6 @@ public static IResourceBuilder AddPermify( .WithEnvironment("PERMIFY_HTTP_ENABLED", "true") .WithEnvironment("PERMIFY_HTTP_PORT", "8080") .WithHttpHealthCheck("/healthz") - .WithHttpsEndpoint( - targetPort: 8081, - port: grpcPort, - name: PermifyResource.GrpcEndpointName - ) - .WithEnvironment("PERMIFY_GRPC_ENABLED", "true") - .WithEnvironment("PERMIFY_GRPC_PORT", "8081") // Configure OTLP .WithOtlpExporter() .WithEnvironment("PERMIFY_TRACER_ENABLED", "true") @@ -79,4 +72,83 @@ public static IResourceBuilder AddPermify( return permifyBuilder; } + + /// + /// Enables gRPC support for Permify. + /// + /// The resource to enable gRPC support for + /// An optional port on which to enable gRPC + public static IResourceBuilder WithGrpc( + this IResourceBuilder builder, + int? grpcPort = null + ) + { + return builder + .WithHttpsEndpoint( + targetPort: 8081, + port: grpcPort, + name: PermifyResource.GrpcEndpointName + ) + .WithEnvironment("PERMIFY_GRPC_ENABLED", "true") + .WithEnvironment("PERMIFY_GRPC_PORT", "8081"); + } + + /// + /// Adds as the database for Permify to persist data. + /// + public static IResourceBuilder WithDatabase( + this IResourceBuilder builder, + IResourceBuilder database + ) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(database); + + builder + .WithEnvironment("PERMIFY_DATABASE_ENGINE", "postgres") + .WithEnvironment("PERMIFY_DATABASE_URI", database.Resource.UriExpression) + .WaitFor(database); + + return builder; + } + + /// + /// Configures Permify to enable watch support. + /// + /// + /// Permify's watch support relies on Postgres' track_commit_timestamp feature. + /// Refer to https://docs.permify.co/api-reference/watch/watch-changes#enabling-track-commit-timestamp-on-postgresql + /// for information on how to configure this, or call + /// to automatically configure the flag for you. + /// + public static IResourceBuilder WithWatchSupport( + this IResourceBuilder builder, + IResourceBuilder database + ) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(database); + + return WithDatabase(builder, database) + .WithEnvironment("PERMIFY_SERVICE_WATCH_ENABLED", "true"); + } + + /// + /// Configures permify to enable watch support. + /// The passed resource will be modified to enable track_commit_timestamp. + /// If you don't want this, use instead. + /// + public static IResourceBuilder WithWatchSupport( + this IResourceBuilder builder, + IResourceBuilder server, + IResourceBuilder database + ) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(server); + ArgumentNullException.ThrowIfNull(database); + + server.WithArgs("-c", "track_commit_timestamp=on"); + return builder.WithWatchSupport(database); + } } \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/README.md b/src/CommunityToolkit.Aspire.Hosting.Permify/README.md index a121decb1..dceec3b19 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Permify/README.md +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/README.md @@ -25,6 +25,7 @@ If you want to enable watch support you'll need a Postgres database: ```csharp var postgres = builder.AddPostgres("postgres") + .WithArgs("-c", "track_commit_timestamp=on") .AddDatabase("permify-db"); var permify = builder.AddPermify("permify") From 1838bcef8564fdf5c8ce51ede904e2ce0ebe896d Mon Sep 17 00:00:00 2001 From: Wannes Gennar Date: Thu, 15 Jan 2026 09:02:35 +0100 Subject: [PATCH 3/4] test: add integration and unit tests for Permify --- .../PermifyHostingExtensions.cs | 2 +- .../PermifyResource.cs | 2 +- .../AppHostTests.cs | 32 +++++ ...oolkit.Aspire.Hosting.Permify.Tests.csproj | 9 +- .../PermifyHostingExtensionTests.cs | 113 ++++++++++++++++++ 5 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/AppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/PermifyHostingExtensionTests.cs diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs index a4aa366c9..f9de3d6b4 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs @@ -34,7 +34,7 @@ public static IResourceBuilder AddPermify( .WithHttpsEndpoint( targetPort: 8080, port: httpPort, - name: PermifyResource.HttpEndpointName + name: PermifyResource.HttpsEndpointName ) .WithEnvironment("PERMIFY_HTTP_ENABLED", "true") .WithEnvironment("PERMIFY_HTTP_PORT", "8080") diff --git a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs index 5b5903246..5faf2a24a 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs @@ -10,7 +10,7 @@ public sealed class PermifyResource(string name) : ContainerResource(name) /// /// The name of the HTTP API endpoint. /// - public const string HttpEndpointName = "http"; + public const string HttpsEndpointName = "https"; /// /// The name of the gRPC API endpoint. diff --git a/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/AppHostTests.cs new file mode 100644 index 000000000..2ec9f9a5b --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/AppHostTests.cs @@ -0,0 +1,32 @@ +using Aspire.Components.Common.Tests; +using CommunityToolkit.Aspire.Testing; +using System.Net.Http.Json; + +namespace CommunityToolkit.Aspire.Hosting.Permify.Tests; + +[RequiresDocker] +public class AppHostTests( + AspireIntegrationTestFixture fixture +) : IClassFixture> +{ + [Fact] + public async Task Permify_Starts_And_Responds() + { + var resourceName = "permify"; + + await fixture.ResourceNotificationService + .WaitForResourceAsync(resourceName) + .WaitAsync(TimeSpan.FromMinutes(5)); + + var httpClient = fixture.CreateHttpClient(resourceName); + var request = new HttpRequestMessage(HttpMethod.Post, "/v1/tenants/t1/schemas/write"); + request.Content = JsonContent.Create(new + { + schema = "entity user {}" + }); + + var response = await httpClient.SendAsync(request); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } +} \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj index 237d66167..6a4199679 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj +++ b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj @@ -1,9 +1,8 @@  - - net10.0 - enable - enable - + + + + diff --git a/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/PermifyHostingExtensionTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/PermifyHostingExtensionTests.cs new file mode 100644 index 000000000..0d2716309 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/PermifyHostingExtensionTests.cs @@ -0,0 +1,113 @@ +using Aspire.Hosting; +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Permify.Tests; + +public class PermifyHostingExtensionTests +{ + [Fact] + public void AddPermify_Should_Throw_If_Builder_Is_Null() + { + IDistributedApplicationBuilder builder = null!; + + var exception = Assert.Throws(() => builder.AddPermify("permify")); + Assert.Equal("builder", exception.ParamName); + } + + [Fact] + public void Add_Permify_Should_Throw_If_Name_Is_Null() + { + var builder = DistributedApplication.CreateBuilder(); + + var exception = Assert.Throws(() => builder.AddPermify(null!)); + Assert.Equal("name", exception.ParamName); + } + + [Fact] + public void AddPermify_Should_Add_Permify_Resource() + { + var builder = DistributedApplication.CreateBuilder(); + var permify = builder.AddPermify("permify"); + + Assert.NotNull(permify); + Assert.IsType(permify.Resource); + Assert.Equal("permify", permify.Resource.Name); + } + + [Fact] + public async Task AddPermify_Should_Add_HTTP_Endpoint() + { + var builder = DistributedApplication.CreateBuilder(); + var permify = builder.AddPermify("permify"); + + Assert.NotNull(await permify.GetEndpoint("https").GetValueAsync()); + } + + [Fact] + public async Task AddPermify_Should_Enable_HTTP() + { + var builder = DistributedApplication.CreateBuilder(); + var permify = builder.AddPermify("permify"); + + var environment = await permify.Resource.GetEnvironmentVariablesAsync(); + Assert.Equal("true", environment["PERMIFY_HTTP_ENABLED"]); + } + + [Fact] + public async Task AddPermify_Should_Configure_SSL() + { + var builder = DistributedApplication.CreateBuilder(); + var permify = builder.AddPermify("permify"); + + var environment = await permify.Resource.GetEnvironmentVariablesAsync(); + Assert.Equal("true", environment["PERMIFY_HTTP_TLS_ENABLED"]); + Assert.Equal("true", environment["PERMIFY_GRPC_TLS_ENABLED"]); + Assert.NotEmpty(environment["PERMIFY_HTTP_TLS_CERT_PATH"]); + Assert.NotEmpty(environment["PERMIFY_HTTP_TLS_KEY_PATH"]); + Assert.NotEmpty(environment["PERMIFY_GRPC_TLS_CERT_PATH"]); + Assert.NotEmpty(environment["PERMIFY_GRPC_TLS_KEY_PATH"]); + } + + [Fact] + public async Task AddPermify_WithGrpc_Should_Enable_GRPC() + { + var builder = DistributedApplication.CreateBuilder(); + var permify = builder.AddPermify("permify") + .WithGrpc(); + + var environment = await permify.Resource.GetEnvironmentVariablesAsync(); + Assert.Equal("true", environment["PERMIFY_GRPC_ENABLED"]); + Assert.NotNull(await permify.GetEndpoint("grpc").GetValueAsync()); + } + + [Fact] + public async Task AddPermify_WithDatabase_Should_Configure_Database() + { + var builder = DistributedApplication.CreateBuilder(); + var database = builder.AddPostgres("postgres") + .AddDatabase("permify-db"); + + var permify = builder.AddPermify("permify") + .WithDatabase(database); + + var environment = await permify.Resource.GetEnvironmentVariablesAsync(); + Assert.Equal("postgres", environment["PERMIFY_DATABASE_ENGINE"]); + Assert.NotNull(environment["PERMIFY_DATABASE_URI"]); + } + + [Fact] + public async Task AddPermify_WithWatchSupport_Should_Configure_Watch() + { + var builder = DistributedApplication.CreateBuilder(); + var database = builder.AddPostgres("postgres") + .AddDatabase("permify-db"); + + var permify = builder.AddPermify("permify") + .WithWatchSupport(database); + + var environment = await permify.Resource.GetEnvironmentVariablesAsync(); + Assert.Equal("postgres", environment["PERMIFY_DATABASE_ENGINE"]); + Assert.NotNull(environment["PERMIFY_DATABASE_URI"]); + Assert.Equal("true", environment["PERMIFY_SERVICE_WATCH_ENABLED"]); + } +} \ No newline at end of file From 19b26a895e749834a8e39679d2f04b1976e373f4 Mon Sep 17 00:00:00 2001 From: Wannes Gennar Date: Thu, 15 Jan 2026 09:13:36 +0100 Subject: [PATCH 4/4] docs: update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7091b1954..73bdbd214 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ This repository contains the source code for the Aspire Community Toolkit, a col | - **Learn More**: [`Hosting.Elasticsearch.Extensions`][elasticsearch-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions][elasticsearch-ext-shields]][elasticsearch-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions][elasticsearch-ext-shields-preview]][elasticsearch-ext-nuget-preview] | An integration that contains some additional extensions for hosting Elasticsearch container. | | - **Learn More**: [`Hosting.Umami`][umami-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.Umami][umami-shields]][umami-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Umami][umami-shields-preview]][umami-nuget-preview] | An Aspire hosting integration leveraging the [Umami](https://umami.is/) container. | | - **Learn More**: [`Hosting.Azure.Extensions`][azure-ext-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Azure.Extensions][azure-ext-shields]][azure-ext-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Azure.Extensions][azure-ext-shields-preview]][azure-ext-nuget-preview] | An integration that contains some additional extensions for hosting Azure container. | +| - **Learn More**: [`Hosting.Permify`][permify-integration-docs]
- Stable 📦: [![CommunityToolkit.Aspire.Hosting.Permify][permify-shields]][permify-nuget]
- Preview 📦: [![CommunityToolkit.Aspire.Hosting.Permify][permify-shields-preview]][permify-nuget-preview] | An Aspire hosting integration for [Permify](https://permify.co/). | ## 🙌 Getting Started @@ -292,3 +293,5 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org) [azure-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/ [azure-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.Extensions?label=nuget%20(preview) [azure-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/absoluteLatest +[permify-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Permify +[permify-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Permify/ \ No newline at end of file