428d19f60f
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.
50 lines
1.9 KiB
JavaScript
50 lines
1.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// Refresh the vendored apf-ai-service protos from the sibling
|
|
// working tree. Developer convenience only — not invoked from CI.
|
|
//
|
|
// Per ADR-0024 §"Sub-decision 3 — vendored protos", the BFF holds a
|
|
// committed copy of the `.proto` files; this script keeps the copy
|
|
// in sync with the upstream repo when a contract change lands. The
|
|
// generated TypeScript stubs are NOT regenerated here — chain with
|
|
// `pnpm run grpc:codegen` if needed:
|
|
//
|
|
// pnpm run grpc:sync && pnpm run grpc:codegen
|
|
//
|
|
// Exits non-zero (with a clear message) when the sibling repo is not
|
|
// where the script expects, so a contributor whose layout differs
|
|
// gets a useful pointer instead of a silent no-op.
|
|
|
|
import { existsSync, readdirSync, copyFileSync, statSync } from 'node:fs';
|
|
import { join, resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
const WORKSPACE_ROOT = resolve(HERE, '..');
|
|
const UPSTREAM = resolve(WORKSPACE_ROOT, '..', 'apf-ai-service', 'contract', 'proto');
|
|
const VENDORED = resolve(WORKSPACE_ROOT, 'apps', 'portal-bff', 'src', 'grpc', 'proto', 'apf-ai');
|
|
|
|
if (!existsSync(UPSTREAM) || !statSync(UPSTREAM).isDirectory()) {
|
|
console.error(
|
|
`apf-ai-service protos not found at ${UPSTREAM}.\n` +
|
|
`This script expects the sibling working tree at ../apf-ai-service ` +
|
|
`relative to this workspace. Adjust the path or clone the repo and retry.`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const files = readdirSync(UPSTREAM).filter((f) => f.endsWith('.proto'));
|
|
if (files.length === 0) {
|
|
console.error(`No .proto files under ${UPSTREAM} — nothing to sync.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
for (const file of files) {
|
|
copyFileSync(join(UPSTREAM, file), join(VENDORED, file));
|
|
console.log(`synced ${file}`);
|
|
}
|
|
|
|
console.log(
|
|
`\n${files.length} proto file(s) refreshed into ${VENDORED}.\n` +
|
|
`Next: pnpm run grpc:codegen (regenerate TypeScript stubs).`,
|
|
);
|