Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches:
- master
env:
VERSION: 10.0.10
VERSION: 10.0.11
jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock
service name wouldn't resolve outside the compose network at all. Use step 6's derived database
name for the connection string's `Database=`/equivalent value.

For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the
ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and
the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library
libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing.

⚠ Add only the chosen provider's connection string, active. Don't add the other providers'
connection strings as commented-out alternatives — a project commits to exactly one provider, and
commented dead alternatives for providers it doesn't use are clutter, not documentation. If a
Expand Down Expand Up @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope.
$env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv;
$env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv;

$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true";
$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";

& "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update `
--no-build `
Expand Down Expand Up @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope.
az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH;
if ($LastExitCode -ne 0) { throw "error"; };

$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true";
$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";
echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV;
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock
service name wouldn't resolve outside the compose network at all. Use step 6's derived database
name for the connection string's `Database=`/equivalent value.

For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the
ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and
the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library
libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing.

⚠ Add only the chosen provider's connection string, active. Don't add the other providers'
connection strings as commented-out alternatives - a project commits to exactly one provider, and
commented dead alternatives for providers it doesn't use are clutter, not documentation. If a
Expand Down Expand Up @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope.
$env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv;
$env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv;

$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true";
$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";

& "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update `
--no-build `
Expand Down Expand Up @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope.
az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH;
if ($LastExitCode -ne 0) { throw "error"; };

$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true";
$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";
echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV;
```

Expand Down
27 changes: 13 additions & 14 deletions Api.ApiClients.Audit/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ step.
## Solution Structure

Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the
application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested
application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested
inside `{name}/`.

| Directory / File | API | WEB | CON | Description |
Expand Down Expand Up @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config.

**Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a
run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way
(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose
(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose
service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s
note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency
services still need their own unique host ports, same as for an API/Web consumer.
Expand Down Expand Up @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`.
}
```

The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed:
- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`).
- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body.

⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise
Swagger UI's icons/images fail to render.

Expand Down Expand Up @@ -1950,8 +1954,8 @@ Two genuinely different roles a controller plays in a layered Nano solution —
(both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being
explicit about, since it changes what an action's body actually does:

- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this
solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no
- **Public API controller** — the customer/end-user-facing application (the application that exposes the
solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no
`IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data,
Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/
`nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from
Expand Down Expand Up @@ -1999,8 +2003,7 @@ is actively dangerous, not just architecturally unusual:
A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to
the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth`
method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its
own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a
working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the
own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the
Public API itself.

#### Entity controller hierarchy
Expand Down Expand Up @@ -2048,17 +2051,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the
the link doesn't already exist) are made by the Public API before the generic create or delete.
- **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one
transaction (diff the existing rows against the requested ones, then delete and add). Use it only when:
- the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every
live toggle would change access immediately and one Save is one audit entry), or
- a rule can only be checked against the final set, or
- the parent is meaningless without its first children and is created with them.
- the set is edited as a reviewed batch with an explicit Save, where every live change would take effect
immediately and one Save is one audit entry, or
- a rule can only be checked against the final set.

A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include
Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise.

Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and
assign / remove for later edits of the same relationship unless create genuinely needs its first children.

#### Query criteria

The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes
Expand Down Expand Up @@ -2859,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in)
| `BaseEntityDeletable` / `<TIdentity>` | `IEntityDeletable` only | |
| `BaseEntityCreatableAndUpdatable` / `<TIdentity>` | Create + update, no delete | |
| `BaseEntityCreatableAndDeletable` / `<TIdentity>` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. |
| `BaseEntityUser` / `<TIdentity>` | Update + delete (`IEntityUser<TIdentity>`) | Adds `IdentityUser` (`IdentityUserEx<TIdentity>`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). |
| `BaseEntityUser` / `<TIdentity>` | Update + delete (`IEntityUser<TIdentity>`) | Adds `IdentityUser` (`IdentityUserEx<TIdentity>`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). |
| `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. |
| `BaseEntityIdentity` / `<TIdentity>` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock
service name wouldn't resolve outside the compose network at all. Use step 6's derived database
name for the connection string's `Database=`/equivalent value.

For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the
ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and
the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library
libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing.

⚠ Add only the chosen provider's connection string, active. Don't add the other providers'
connection strings as commented-out alternatives — a project commits to exactly one provider, and
commented dead alternatives for providers it doesn't use are clutter, not documentation. If a
Expand Down Expand Up @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope.
$env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv;
$env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv;

$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true";
$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";

& "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update `
--no-build `
Expand Down Expand Up @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope.
az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH;
if ($LastExitCode -ne 0) { throw "error"; };

$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true";
$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";
echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV;
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock
service name wouldn't resolve outside the compose network at all. Use step 6's derived database
name for the connection string's `Database=`/equivalent value.

For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the
ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and
the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library
libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing.

⚠ Add only the chosen provider's connection string, active. Don't add the other providers'
connection strings as commented-out alternatives - a project commits to exactly one provider, and
commented dead alternatives for providers it doesn't use are clutter, not documentation. If a
Expand Down Expand Up @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope.
$env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv;
$env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv;

$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true";
$env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";

& "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update `
--no-build `
Expand Down Expand Up @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope.
az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH;
if ($LastExitCode -ne 0) { throw "error"; };

$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true";
$env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable";
echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV;
```

Expand Down
Loading
Loading