refactor(admin): split api-client.ts (885 LOC) and endpoints.ts (1262 LOC) into focused modules
api-client.ts is now a thin delegating class (263 LOC) backed by: - api-client-types.ts (84) — shared types, config, FetchContext - api-client-state.ts (120) — state CRUD + export - api-client-projects.ts (160) — project management - api-client-wiki.ts (116) — wiki knowledge base - api-client-operations.ts (299) — checkpoints, flow, modules, UCCA, import, screening endpoints.ts is now a barrel (25 LOC) aggregating the 4 existing domain files (endpoints-python-core, endpoints-python-gdpr, endpoints-python-ops, endpoints-go). All files stay under the 500-line hard cap. Build verified with `npx next build`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
299
admin-compliance/lib/sdk/api-client-operations.ts
Normal file
299
admin-compliance/lib/sdk/api-client-operations.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* SDK API Client — Operational methods.
|
||||
* (checkpoints, flow, modules, UCCA, document import, screening, health)
|
||||
*/
|
||||
|
||||
import {
|
||||
APIResponse,
|
||||
CheckpointValidationResult,
|
||||
FetchContext,
|
||||
CheckpointStatus,
|
||||
} from './api-client-types'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checkpoint Validation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate a specific checkpoint
|
||||
*/
|
||||
export async function validateCheckpoint(
|
||||
ctx: FetchContext,
|
||||
checkpointId: string,
|
||||
data?: unknown
|
||||
): Promise<CheckpointValidationResult> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<CheckpointValidationResult>>(
|
||||
`${ctx.baseUrl}/checkpoints/validate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tenantId: ctx.tenantId,
|
||||
checkpointId,
|
||||
data,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw ctx.createError(response.error || 'Checkpoint validation failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all checkpoint statuses
|
||||
*/
|
||||
export async function getCheckpoints(
|
||||
ctx: FetchContext
|
||||
): Promise<Record<string, CheckpointStatus>> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<Record<string, CheckpointStatus>>>(
|
||||
`${ctx.baseUrl}/checkpoints?tenantId=${encodeURIComponent(ctx.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
|
||||
return response.data || {}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Flow Navigation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get current flow state
|
||||
*/
|
||||
export async function getFlowState(ctx: FetchContext): Promise<{
|
||||
currentStep: string
|
||||
currentPhase: 1 | 2
|
||||
completedSteps: string[]
|
||||
suggestions: Array<{ stepId: string; reason: string }>
|
||||
}> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<{
|
||||
currentStep: string
|
||||
currentPhase: 1 | 2
|
||||
completedSteps: string[]
|
||||
suggestions: Array<{ stepId: string; reason: string }>
|
||||
}>>(
|
||||
`${ctx.baseUrl}/flow?tenantId=${encodeURIComponent(ctx.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
throw ctx.createError('Failed to get flow state', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to next/previous step
|
||||
*/
|
||||
export async function navigateFlow(
|
||||
ctx: FetchContext,
|
||||
direction: 'next' | 'previous'
|
||||
): Promise<{ stepId: string; phase: 1 | 2 }> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<{
|
||||
stepId: string
|
||||
phase: 1 | 2
|
||||
}>>(
|
||||
`${ctx.baseUrl}/flow`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tenantId: ctx.tenantId,
|
||||
direction,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.data) {
|
||||
throw ctx.createError('Failed to navigate flow', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Modules
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get available compliance modules from backend
|
||||
*/
|
||||
export async function getModules(
|
||||
ctx: FetchContext,
|
||||
filters?: {
|
||||
serviceType?: string
|
||||
criticality?: string
|
||||
processesPii?: boolean
|
||||
aiComponents?: boolean
|
||||
}
|
||||
): Promise<{ modules: unknown[]; total: number }> {
|
||||
const params = new URLSearchParams()
|
||||
if (filters?.serviceType) params.set('service_type', filters.serviceType)
|
||||
if (filters?.criticality) params.set('criticality', filters.criticality)
|
||||
if (filters?.processesPii !== undefined) params.set('processes_pii', String(filters.processesPii))
|
||||
if (filters?.aiComponents !== undefined) params.set('ai_components', String(filters.aiComponents))
|
||||
|
||||
const queryString = params.toString()
|
||||
const url = `${ctx.baseUrl}/modules${queryString ? `?${queryString}` : ''}`
|
||||
|
||||
const response = await ctx.fetchWithRetry<{ modules: unknown[]; total: number }>(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UCCA (Use Case Compliance Assessment)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assess a use case
|
||||
*/
|
||||
export async function assessUseCase(
|
||||
ctx: FetchContext,
|
||||
intake: unknown
|
||||
): Promise<unknown> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${ctx.baseUrl}/ucca/assess`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': ctx.tenantId,
|
||||
},
|
||||
body: JSON.stringify(intake),
|
||||
}
|
||||
)
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all assessments
|
||||
*/
|
||||
export async function getAssessments(ctx: FetchContext): Promise<unknown[]> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<unknown[]>>(
|
||||
`${ctx.baseUrl}/ucca/assessments?tenantId=${encodeURIComponent(ctx.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': ctx.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
return response.data || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single assessment
|
||||
*/
|
||||
export async function getAssessment(
|
||||
ctx: FetchContext,
|
||||
id: string
|
||||
): Promise<unknown> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${ctx.baseUrl}/ucca/assessments/${id}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': ctx.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an assessment
|
||||
*/
|
||||
export async function deleteAssessment(
|
||||
ctx: FetchContext,
|
||||
id: string
|
||||
): Promise<void> {
|
||||
await ctx.fetchWithRetry<APIResponse<void>>(
|
||||
`${ctx.baseUrl}/ucca/assessments/${id}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': ctx.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Document Import & Screening
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Analyze an uploaded document
|
||||
*/
|
||||
export async function analyzeDocument(
|
||||
ctx: FetchContext,
|
||||
formData: FormData
|
||||
): Promise<unknown> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${ctx.baseUrl}/import/analyze`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'X-Tenant-ID': ctx.tenantId },
|
||||
body: formData,
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a dependency file (package-lock.json, requirements.txt, etc.)
|
||||
*/
|
||||
export async function scanDependencies(
|
||||
ctx: FetchContext,
|
||||
formData: FormData
|
||||
): Promise<unknown> {
|
||||
const response = await ctx.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${ctx.baseUrl}/screening/scan`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'X-Tenant-ID': ctx.tenantId },
|
||||
body: formData,
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Health
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Health check
|
||||
*/
|
||||
export async function healthCheck(ctx: FetchContext): Promise<boolean> {
|
||||
try {
|
||||
const response = await ctx.fetchWithTimeout(
|
||||
`${ctx.baseUrl}/health`,
|
||||
{ method: 'GET' },
|
||||
`health-${Date.now()}`
|
||||
)
|
||||
return response.ok
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user