feat(portal-bff): ai-client skeleton — vendored protos + grpc client + Principal mapper
CI / commits (pull_request) Successful in 3m34s
CI / scan (pull_request) Successful in 3m47s
CI / check (pull_request) Successful in 6m15s
CI / a11y (pull_request) Successful in 2m26s
Docs site / build (pull_request) Successful in 1m7s
CI / perf (pull_request) Successful in 4m33s

Lands the BFF-side skeleton for the apf-ai-service relay per ADR-0024,
step 2 of the chantier (skeleton + tests against an in-process fake
gRPC server; no live HTTP endpoint yet — that ships in the SSE-bridge
PR).

What ships:

- contract/proto vendoring at apps/portal-bff/src/grpc/proto/apf-ai/
  (common, chat, rag, ingestion, models). pnpm run grpc:sync refreshes
  from the sibling apf-ai-service tree; both .proto and the ts-proto
  TypeScript stubs under grpc/gen/ are committed for hermetic builds
  and reviewable diffs.
- Codegen via pnpm run grpc:codegen using grpc-tools' bundled protoc
  and ts-proto (outputServices=grpc-js, esModuleInterop, forceLong,
  useOptionals=messages). Generated tree is excluded from Prettier
  and ESLint so hand-rules do not chase codegen output.
- assertAiServiceConfig() pre-flight validator alongside the other
  config validators (AI_SERVICE_GRPC_ENDPOINT, AI_SERVICE_CLIENT_ID,
  AI_SERVICE_GRPC_TLS).
- AiClientModule provides ChatClient / RagClient / IngestionClient /
  ModelsClient wrappers, a GrpcMetadataBuilder that stamps every call
  with x-client-id + x-correlation-id (W3C trace-id from OTel when a
  span is active, explicit override or UUID fallback otherwise), and
  a PrincipalMapper that hashes the Entra oid via HashUserIdService
  so the proto Principal.subject matches audit.events.actor_id_hash
  exactly (ADR-0013 cross-reference contract).
- Specs cover: env validator, principal mapper, metadata builder
  (OTel span / override / fallback paths), chat client streaming +
  cancellation against an in-process fake gRPC ChatService, rag
  client unary happy path + error propagation, and module bootstrap.

AiClientModule is NOT imported in AppModule yet — the SSE-bridge
controller and its live route ship in the next PR.
This commit is contained in:
Julien Gautier
2026-05-19 20:50:04 +02:00
parent 3f3f47317b
commit 428d19f60f
32 changed files with 6396 additions and 11 deletions
+9 -5
View File
@@ -5,11 +5,15 @@ export default [
...nx.configs['flat/typescript'],
...nx.configs['flat/javascript'],
{
"ignores": [
"**/dist",
"**/out-tsc",
"**/vitest.config.*.timestamp*"
]
"ignores": [
"**/dist",
"**/out-tsc",
"**/vitest.config.*.timestamp*",
// Generated gRPC TypeScript stubs — hand-rules do not apply to
// codegen output. Lint the wrapper modules in `ai-client/`, not
// the ts-proto-emitted files under `gen/`.
"apps/portal-bff/src/grpc/gen/**"
]
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],