PR #27 (squash-merged via Gitea API)
Some checks failed
CI / Frontend Lint (push) Failing after 6s
CI / Frontend Type Check (push) Failing after 7s
CI / Frontend Build (push) Failing after 7s
CI / Frontend E2E Tests (push) Failing after 7s
CI / Orchestrator Build (push) Failing after 7s
CI / Orchestrator Unit Tests (push) Failing after 5s
CI / Orchestrator E2E (Testcontainers) (push) Failing after 6s
CI / Contracts Compile (push) Failing after 7s
CI / Contracts Test (push) Failing after 5s
Security Scan / Dependency Vulnerability Scan (push) Failing after 5s
Security Scan / OWASP ZAP Scan (push) Failing after 3s
Some checks failed
CI / Frontend Lint (push) Failing after 6s
CI / Frontend Type Check (push) Failing after 7s
CI / Frontend Build (push) Failing after 7s
CI / Frontend E2E Tests (push) Failing after 7s
CI / Orchestrator Build (push) Failing after 7s
CI / Orchestrator Unit Tests (push) Failing after 5s
CI / Orchestrator E2E (Testcontainers) (push) Failing after 6s
CI / Contracts Compile (push) Failing after 7s
CI / Contracts Test (push) Failing after 5s
Security Scan / Dependency Vulnerability Scan (push) Failing after 5s
Security Scan / OWASP ZAP Scan (push) Failing after 3s
This commit was merged in pull request #27.
This commit is contained in:
155
orchestrator/src/services/completeCredential/identityClient.ts
Normal file
155
orchestrator/src/services/completeCredential/identityClient.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* HTTP client adapter for `DBIS/cc-identity-core`.
|
||||
*
|
||||
* Provider-switched: when `CC_IDENTITY_URL` is set the client makes
|
||||
* real HTTP calls to the upstream Complete Credential identity
|
||||
* service; otherwise every method returns a deterministic mock so
|
||||
* unit tests, local dev, and CI still work.
|
||||
*
|
||||
* Upstream surface (openapi.yaml + src/server.mjs at recon time):
|
||||
* GET /health
|
||||
* GET /ready
|
||||
* POST /v1/subjects
|
||||
*
|
||||
* Extend as additional endpoints ship upstream.
|
||||
*/
|
||||
|
||||
import { randomUUID } from "crypto";
|
||||
import { logger } from "../../logging/logger";
|
||||
import type {
|
||||
CcHealthStatus,
|
||||
CcSubject,
|
||||
CcSubjectCreate,
|
||||
} from "./types";
|
||||
|
||||
export interface CcIdentityConfig {
|
||||
baseUrl?: string;
|
||||
apiKey?: string;
|
||||
timeoutMs?: number;
|
||||
fetchImpl?: typeof fetch;
|
||||
}
|
||||
|
||||
export interface CcIdentityClient {
|
||||
mode: "live" | "mock";
|
||||
health(): Promise<CcHealthStatus>;
|
||||
ready(): Promise<CcHealthStatus>;
|
||||
createSubject(req: CcSubjectCreate, correlationId?: string): Promise<CcSubject>;
|
||||
}
|
||||
|
||||
function loadConfigFromEnv(): CcIdentityConfig {
|
||||
return {
|
||||
baseUrl: process.env.CC_IDENTITY_URL,
|
||||
apiKey: process.env.CC_IDENTITY_API_KEY,
|
||||
timeoutMs: process.env.CC_IDENTITY_TIMEOUT_MS
|
||||
? parseInt(process.env.CC_IDENTITY_TIMEOUT_MS, 10)
|
||||
: 10_000,
|
||||
};
|
||||
}
|
||||
|
||||
class HttpCcIdentityClient implements CcIdentityClient {
|
||||
readonly mode = "live" as const;
|
||||
private readonly baseUrl: string;
|
||||
private readonly apiKey?: string;
|
||||
private readonly timeoutMs: number;
|
||||
private readonly fetchImpl: typeof fetch;
|
||||
|
||||
constructor(
|
||||
cfg: Required<Pick<CcIdentityConfig, "baseUrl">> & CcIdentityConfig,
|
||||
) {
|
||||
this.baseUrl = cfg.baseUrl.replace(/\/+$/, "");
|
||||
this.apiKey = cfg.apiKey;
|
||||
this.timeoutMs = cfg.timeoutMs ?? 10_000;
|
||||
this.fetchImpl = cfg.fetchImpl ?? fetch;
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
method: "GET" | "POST",
|
||||
path: string,
|
||||
body?: unknown,
|
||||
correlationId?: string,
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${path}`;
|
||||
const headers: Record<string, string> = { Accept: "application/json" };
|
||||
if (body !== undefined) headers["Content-Type"] = "application/json";
|
||||
if (this.apiKey) headers["X-API-Key"] = this.apiKey;
|
||||
if (correlationId) headers["X-Correlation-Id"] = correlationId;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
||||
try {
|
||||
const resp = await this.fetchImpl(url, {
|
||||
method,
|
||||
headers,
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!resp.ok) {
|
||||
const text = await resp.text().catch(() => "");
|
||||
throw new Error(
|
||||
`cc-identity ${method} ${path} failed: HTTP ${resp.status} ${text.slice(0, 200)}`,
|
||||
);
|
||||
}
|
||||
return (await resp.json()) as T;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
health(): Promise<CcHealthStatus> {
|
||||
return this.request<CcHealthStatus>("GET", "/health");
|
||||
}
|
||||
|
||||
ready(): Promise<CcHealthStatus> {
|
||||
return this.request<CcHealthStatus>("GET", "/ready");
|
||||
}
|
||||
|
||||
createSubject(
|
||||
req: CcSubjectCreate,
|
||||
correlationId?: string,
|
||||
): Promise<CcSubject> {
|
||||
return this.request<CcSubject>(
|
||||
"POST",
|
||||
"/v1/subjects",
|
||||
req,
|
||||
correlationId ?? randomUUID(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MockCcIdentityClient implements CcIdentityClient {
|
||||
readonly mode = "mock" as const;
|
||||
|
||||
async health(): Promise<CcHealthStatus> {
|
||||
return { status: "ok", service: "cc-identity-core" };
|
||||
}
|
||||
|
||||
async ready(): Promise<CcHealthStatus> {
|
||||
return { status: "ok", service: "cc-identity-core", persistence: false };
|
||||
}
|
||||
|
||||
async createSubject(req: CcSubjectCreate): Promise<CcSubject> {
|
||||
return {
|
||||
subjectId: randomUUID(),
|
||||
tenantId: req.tenantId ?? "tenant-demo",
|
||||
entityId: req.entityId ?? "entity-demo",
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createCcIdentityClient(
|
||||
cfg: CcIdentityConfig = loadConfigFromEnv(),
|
||||
): CcIdentityClient {
|
||||
if (cfg.baseUrl) {
|
||||
logger.info(
|
||||
{ baseUrl: cfg.baseUrl, mode: "live" },
|
||||
"[CcIdentity] HTTP client",
|
||||
);
|
||||
return new HttpCcIdentityClient({ ...cfg, baseUrl: cfg.baseUrl });
|
||||
}
|
||||
logger.info(
|
||||
{ mode: "mock" },
|
||||
"[CcIdentity] HTTP client (no CC_IDENTITY_URL — mock mode)",
|
||||
);
|
||||
return new MockCcIdentityClient();
|
||||
}
|
||||
Reference in New Issue
Block a user