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/README.md b/README.md
index 8a308bf5d..9c031945f 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
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..d1ec37d81
--- /dev/null
+++ b/examples/permify/CommunityToolkit.Aspire.Hosting.Permify.AppHost/AppHost.cs
@@ -0,0 +1,9 @@
+var builder = DistributedApplication.CreateBuilder(args);
+
+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/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..f9de3d6b4
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyHostingExtensions.cs
@@ -0,0 +1,154 @@
+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.HttpsEndpointName
+ )
+ .WithEnvironment("PERMIFY_HTTP_ENABLED", "true")
+ .WithEnvironment("PERMIFY_HTTP_PORT", "8080")
+ .WithHttpHealthCheck("/healthz")
+ // 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;
+ }
+
+ ///
+ /// 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/PermifyResource.cs b/src/CommunityToolkit.Aspire.Hosting.Permify/PermifyResource.cs
new file mode 100644
index 000000000..5faf2a24a
--- /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 HttpsEndpointName = "https";
+
+ ///
+ /// 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..dceec3b19
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.Permify/README.md
@@ -0,0 +1,42 @@
+# 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")
+ .WithArgs("-c", "track_commit_timestamp=on")
+ .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/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
new file mode 100644
index 000000000..6a4199679
--- /dev/null
+++ b/tests/CommunityToolkit.Aspire.Hosting.Permify.Tests/CommunityToolkit.Aspire.Hosting.Permify.Tests.csproj
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
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