Skip to content

SAIF.Platform.Aspire.Hosting

Aspire AppHost extensions for Forge's local/dev orchestration: custom resources for Keycloak, WireMock, Oracle, and Scalar/Swagger UI wiring, plus the SaifEnvironmentResource abstraction that drives Forge's pipeline generation.

Getting started

Add a mocked dependency with the basic WireMock container resource — points a static image at a directory of mapping files:

var builder = DistributedApplication.CreateBuilder(args);

var wiremock = builder.AddWiremock("wiremock", "./wiremock-mappings");

builder.AddProject<Projects.MyApi>("my-api")
    .WithReference(wiremock);

AddWiremock bind-mounts path into the container's /home/wiremock and exposes an HTTP endpoint. Pass EnableCors = true (the default) via the options delegate to add --enable-stub-cors.

SaifEnvironmentResource

AddSaifEnvironment() is the primary entry point for a Forge AppHost. It registers a non-manifest SaifEnvironmentResource that carries application identity — application name, business domain, owner, tenant (defaults to "corporate") — and pipeline defaults, then wires up Forge's pipeline-generation DAG step automatically (no separate registration call needed):

var builder = DistributedApplication.CreateBuilder(args);

builder.AddSaifEnvironment()
    .WithApplicationName("MyApp")
    .WithBusinessDomain("it")
    .WithOwner("Architecture");

builder.AddApiService<Projects.MyApi>("my-api");

WithPipelineDefaults() overrides the sensible built-in defaults (.NET 10.x, Node 22.x, pipeline-templates@refs/heads/releases/v3) only when a service needs something different — e.g. a custom SecretsVariableGroupName. This API surface is marked [Experimental("SAIFENV001")]. AddSecurity() and AddUpstreamApi(), referenced in its own XML doc example, are also part of this package (Generation/Infrastructure/Security), registering the SecurityResource and UpstreamApiResource used by pipeline generation — no separate package is needed for them.

Keycloak

AddKeycloak(name, realm, ...) wraps Aspire.Hosting.Keycloak's container resource with Forge-specific realm generation: it reads business-role-app-role.yml (business role → app role mappings) and an optional project_id from the app's infra/auth/ext/user/ features folder, renders a set of Mustache (Stubble) templates under ./Keycloak/Templates into realm-import JSON, and calls WithRealmImport so Keycloak boots pre-seeded with those users and roles. Admin credentials default to admin/admin on port 8135 and are passed through as Aspire secret parameters. Use the realm-less AddKeycloak(name, ...) overload plus WithGeneratedRealm(realm, ...) directly if you need to configure realm options (template/import paths, whether to prefix role names with the project ID) separately from container options.

WireMock

There are three related but distinct WireMock integrations in this package, at different points in their evolution:

  • Wiremock/ (AddWiremock) — the simplest option, shown above: a single wiremock/wiremock container bind-mounted to a local directory of static mapping files. No cloud integration.
  • WiremockCli/ (AddWiremockCli + AddMock) — runs mocks via the WireMock CLI executable rather than a container, supports pulling stub definitions from WireMock Cloud projects, and integrates per-mock with the Aspire dashboard and health checks. See WiremockCli/README.md for the full usage example (builder.AddWiremockCli("wiremock").AddMock("my-api", 8080, "wiremock-project-id")) — that README's content is accurate and is summarized, not duplicated, here.
  • WiremockRunner/ (AddWiremockRunner) — the newest variant, built around the wiremock/wiremock-runner container. It detects CI (CI=true or TF_BUILD=true) vs. local dev automatically: in CI it reads WMC_API_TOKEN directly from the environment, while locally it prompts for the token as an Aspire secret parameter. On BeforeStartEvent, it writes a .manifest.json of configured mocks and runs a provision-and-pull PowerShell script that installs @wiremock/cli, provisions any mocks that don't yet have a WireMock Cloud project ID, and pulls stubs for the rest — skipped entirely in test/CI environments where mocks are expected to already be available.

Prefer WiremockCli or WiremockRunner over the plain container resource once you need WireMock Cloud–backed stubs; pick WiremockRunner for the CI-aware token handling if you're setting up a new AppHost.

Oracle

WithOracleDatabaseSeed(database, ...) attaches a seeding configuration to a project resource that seeds an Oracle database: it wires a WithReference to the database resource and passes Schema, SchemaPassword (default "password"), and SeedDelayMilliseconds (default 5000, to let the database finish starting) as environment variables for the seeding project to read. It doesn't wait on the database dependency directly yet — there's a TODO in source referencing davidfowl/WaitForDependenciesAspire.

Scalar and Swagger

AddScalar(name, path, ...) and AddSwagger(name, path, openApiFileName, ...) both follow the same pattern: bind-mount a directory containing an OpenAPI spec into a documentation-UI container (scalar/api-reference or swaggerapi/swagger-ui, respectively) and expose it over HTTP. Scalar reads its spec via the API_REFERENCE_CONFIG environment variable pointed at the mounted path; Swagger UI reads it via SWAGGER_JSON pointed at path/openApiFileName. Both accept an optional fixed HttpPort in their options delegate; otherwise Aspire assigns one.

  • SAIF.Platform — the base service-defaults package most AppHost-referenced projects depend on.
  • SAIF.Platform.Authentication / SAIF.Platform.Authentication.AspNetCore — the security configuration consumed by AddSecurity() in the SaifEnvironmentResource example above.

View source on GitHub