# 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:

```csharp
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):

```csharp
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`](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.Aspire.Hosting/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](https://github.com/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.

## Related packages

- `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](https://github.com/saif-corp/forge/blob/main/src/dotnet/SAIF.Platform.Aspire.Hosting/README.md)
