Files
breakpilot-compliance/admin-compliance/lib/sdk/dsfa/api.ts
Benjamin Admin 308d559c85
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 38s
CI / test-python-backend-compliance (push) Successful in 33s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 19s
feat: DSFA Section 8 KI-Anwendungsfälle + Bundesland RAG-Ingest
- Migration 028: ai_use_case_modules JSONB + section_8_complete auf compliance_dsfas
- Neues ai-use-case-types.ts: AIUseCaseModule Interface, 8 Typen, Art22Assessment,
  AI Act Risikoklassen, WP248-Kriterien, Privacy by Design, createEmptyModule() Helper
- types.ts: Section 8 in DSFA_SECTIONS, ai_use_case_modules im DSFA Interface,
  section_8_complete in DSFASectionProgress
- api.ts: addAIUseCaseModule, updateAIUseCaseModule, removeAIUseCaseModule
- 5 neue UI-Komponenten: AIUseCaseTypeSelector, Art22AssessmentPanel,
  AIRiskCriteriaChecklist, AIUseCaseModuleEditor (7 Tabs), AIUseCaseSection
- DSFASidebar: Section 8 Eintrag + calculateSectionProgress case 8
- ReviewScheduleSection: ai_use_case_module Trigger-Typ ergänzt
- page.tsx: Section 8 Rendering + Weiter-Button auf activeSection < 8 + KI-Module Counter
- scripts/ingest-dsfa-bundesland.sh: WP248 + alle 17 Behörden → bp_dsfa_corpus
- Docs: dsfa.md Section 8 + RAG-Corpus, Developer Portal DSFA mit AI-Modul-Code-Beispielen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 09:20:27 +01:00

439 lines
12 KiB
TypeScript

/**
* DSFA API Client
*
* API client functions for DSFA (Data Protection Impact Assessment) endpoints.
*/
import type {
DSFA,
DSFAListResponse,
DSFAStatsResponse,
CreateDSFARequest,
CreateDSFAFromAssessmentRequest,
CreateDSFAFromAssessmentResponse,
UpdateDSFASectionRequest,
SubmitForReviewResponse,
ApproveDSFARequest,
DSFATriggerInfo,
} from './types'
import type { AIUseCaseModule } from './ai-use-case-types'
// =============================================================================
// CONFIGURATION
// =============================================================================
const getBaseUrl = () => {
if (typeof window !== 'undefined') {
// Browser environment
return process.env.NEXT_PUBLIC_SDK_API_URL || '/api/sdk/v1'
}
// Server environment
return process.env.SDK_API_URL || 'http://localhost:8080/api/sdk/v1'
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const errorBody = await response.text()
let errorMessage = `HTTP ${response.status}`
try {
const errorJson = JSON.parse(errorBody)
errorMessage = errorJson.error || errorJson.message || errorMessage
} catch {
// Keep HTTP status message
}
throw new Error(errorMessage)
}
return response.json()
}
function getHeaders(): HeadersInit {
return {
'Content-Type': 'application/json',
}
}
// =============================================================================
// DSFA CRUD OPERATIONS
// =============================================================================
/**
* List all DSFAs for the current tenant
*/
export async function listDSFAs(status?: string): Promise<DSFA[]> {
const url = new URL(`${getBaseUrl()}/dsgvo/dsfas`, window.location.origin)
if (status) {
url.searchParams.set('status', status)
}
const response = await fetch(url.toString(), {
method: 'GET',
headers: getHeaders(),
credentials: 'include',
})
const data = await handleResponse<DSFAListResponse>(response)
return data.dsfas || []
}
/**
* Get a single DSFA by ID
*/
export async function getDSFA(id: string): Promise<DSFA> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}`, {
method: 'GET',
headers: getHeaders(),
credentials: 'include',
})
return handleResponse<DSFA>(response)
}
/**
* Create a new DSFA
*/
export async function createDSFA(data: CreateDSFARequest): Promise<DSFA> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas`, {
method: 'POST',
headers: getHeaders(),
credentials: 'include',
body: JSON.stringify(data),
})
return handleResponse<DSFA>(response)
}
/**
* Update an existing DSFA
*/
export async function updateDSFA(id: string, data: Partial<DSFA>): Promise<DSFA> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}`, {
method: 'PUT',
headers: getHeaders(),
credentials: 'include',
body: JSON.stringify(data),
})
return handleResponse<DSFA>(response)
}
/**
* Delete a DSFA
*/
export async function deleteDSFA(id: string): Promise<void> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}`, {
method: 'DELETE',
headers: getHeaders(),
credentials: 'include',
})
if (!response.ok) {
throw new Error(`Failed to delete DSFA: ${response.statusText}`)
}
}
// =============================================================================
// DSFA SECTION OPERATIONS
// =============================================================================
/**
* Update a specific section of a DSFA
*/
export async function updateDSFASection(
id: string,
sectionNumber: number,
data: UpdateDSFASectionRequest
): Promise<DSFA> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}/sections/${sectionNumber}`, {
method: 'PUT',
headers: getHeaders(),
credentials: 'include',
body: JSON.stringify(data),
})
return handleResponse<DSFA>(response)
}
// =============================================================================
// DSFA WORKFLOW OPERATIONS
// =============================================================================
/**
* Submit a DSFA for DPO review
*/
export async function submitDSFAForReview(id: string): Promise<SubmitForReviewResponse> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}/submit-for-review`, {
method: 'POST',
headers: getHeaders(),
credentials: 'include',
})
return handleResponse<SubmitForReviewResponse>(response)
}
/**
* Approve or reject a DSFA (DPO/CISO/GF action)
*/
export async function approveDSFA(id: string, data: ApproveDSFARequest): Promise<{ message: string }> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}/approve`, {
method: 'POST',
headers: getHeaders(),
credentials: 'include',
body: JSON.stringify(data),
})
return handleResponse<{ message: string }>(response)
}
// =============================================================================
// DSFA STATISTICS
// =============================================================================
/**
* Get DSFA statistics for the dashboard
*/
export async function getDSFAStats(): Promise<DSFAStatsResponse> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/stats`, {
method: 'GET',
headers: getHeaders(),
credentials: 'include',
})
return handleResponse<DSFAStatsResponse>(response)
}
// =============================================================================
// UCCA INTEGRATION
// =============================================================================
/**
* Create a DSFA from a UCCA assessment (pre-filled)
*/
export async function createDSFAFromAssessment(
assessmentId: string,
data?: CreateDSFAFromAssessmentRequest
): Promise<CreateDSFAFromAssessmentResponse> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/from-assessment/${assessmentId}`, {
method: 'POST',
headers: getHeaders(),
credentials: 'include',
body: JSON.stringify(data || {}),
})
return handleResponse<CreateDSFAFromAssessmentResponse>(response)
}
/**
* Get a DSFA by its linked UCCA assessment ID
*/
export async function getDSFAByAssessment(assessmentId: string): Promise<DSFA | null> {
try {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/by-assessment/${assessmentId}`, {
method: 'GET',
headers: getHeaders(),
credentials: 'include',
})
if (response.status === 404) {
return null
}
return handleResponse<DSFA>(response)
} catch (error) {
// Return null if DSFA not found
return null
}
}
/**
* Check if a DSFA is required for a UCCA assessment
*/
export async function checkDSFARequired(assessmentId: string): Promise<DSFATriggerInfo> {
const response = await fetch(`${getBaseUrl()}/ucca/assessments/${assessmentId}/dsfa-required`, {
method: 'GET',
headers: getHeaders(),
credentials: 'include',
})
return handleResponse<DSFATriggerInfo>(response)
}
// =============================================================================
// EXPORT
// =============================================================================
/**
* Export a DSFA as JSON
*/
export async function exportDSFAAsJSON(id: string): Promise<Blob> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}/export?format=json`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
credentials: 'include',
})
if (!response.ok) {
throw new Error(`Export failed: ${response.statusText}`)
}
return response.blob()
}
/**
* Export a DSFA as PDF
*/
export async function exportDSFAAsPDF(id: string): Promise<Blob> {
const response = await fetch(`${getBaseUrl()}/dsgvo/dsfas/${id}/export/pdf`, {
method: 'GET',
headers: {
'Accept': 'application/pdf',
},
credentials: 'include',
})
if (!response.ok) {
throw new Error(`PDF export failed: ${response.statusText}`)
}
return response.blob()
}
// =============================================================================
// RISK & MITIGATION OPERATIONS
// =============================================================================
/**
* Add a risk to a DSFA
*/
export async function addDSFARisk(dsfaId: string, risk: {
category: string
description: string
likelihood: 'low' | 'medium' | 'high'
impact: 'low' | 'medium' | 'high'
affected_data?: string[]
}): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const newRisk = {
id: crypto.randomUUID(),
...risk,
risk_level: calculateRiskLevelString(risk.likelihood, risk.impact),
affected_data: risk.affected_data || [],
}
const updatedRisks = [...(dsfa.risks || []), newRisk]
return updateDSFA(dsfaId, { risks: updatedRisks } as Partial<DSFA>)
}
/**
* Remove a risk from a DSFA
*/
export async function removeDSFARisk(dsfaId: string, riskId: string): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const updatedRisks = (dsfa.risks || []).filter(r => r.id !== riskId)
return updateDSFA(dsfaId, { risks: updatedRisks } as Partial<DSFA>)
}
/**
* Add a mitigation to a DSFA
*/
export async function addDSFAMitigation(dsfaId: string, mitigation: {
risk_id: string
description: string
type: 'technical' | 'organizational' | 'legal'
responsible_party: string
}): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const newMitigation = {
id: crypto.randomUUID(),
...mitigation,
status: 'planned' as const,
residual_risk: 'medium' as const,
}
const updatedMitigations = [...(dsfa.mitigations || []), newMitigation]
return updateDSFA(dsfaId, { mitigations: updatedMitigations } as Partial<DSFA>)
}
/**
* Update mitigation status
*/
export async function updateDSFAMitigationStatus(
dsfaId: string,
mitigationId: string,
status: 'planned' | 'in_progress' | 'implemented' | 'verified'
): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const updatedMitigations = (dsfa.mitigations || []).map(m => {
if (m.id === mitigationId) {
return {
...m,
status,
...(status === 'implemented' && { implemented_at: new Date().toISOString() }),
...(status === 'verified' && { verified_at: new Date().toISOString() }),
}
}
return m
})
return updateDSFA(dsfaId, { mitigations: updatedMitigations } as Partial<DSFA>)
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
// =============================================================================
// AI USE CASE MODULE OPERATIONS
// =============================================================================
/**
* Add a new AI use case module to a DSFA (Section 8)
*/
export async function addAIUseCaseModule(dsfaId: string, module: AIUseCaseModule): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const existing = dsfa.ai_use_case_modules || []
return updateDSFA(dsfaId, { ai_use_case_modules: [...existing, module] } as Partial<DSFA>)
}
/**
* Update an existing AI use case module in a DSFA
*/
export async function updateAIUseCaseModule(
dsfaId: string,
moduleId: string,
updates: Partial<AIUseCaseModule>
): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const existing = dsfa.ai_use_case_modules || []
const updated = existing.map(m =>
m.id === moduleId ? { ...m, ...updates, updated_at: new Date().toISOString() } : m
)
return updateDSFA(dsfaId, { ai_use_case_modules: updated } as Partial<DSFA>)
}
/**
* Remove an AI use case module from a DSFA
*/
export async function removeAIUseCaseModule(dsfaId: string, moduleId: string): Promise<DSFA> {
const dsfa = await getDSFA(dsfaId)
const updated = (dsfa.ai_use_case_modules || []).filter(m => m.id !== moduleId)
return updateDSFA(dsfaId, { ai_use_case_modules: updated } as Partial<DSFA>)
}
function calculateRiskLevelString(
likelihood: 'low' | 'medium' | 'high',
impact: 'low' | 'medium' | 'high'
): string {
const matrix: Record<string, Record<string, string>> = {
low: { low: 'low', medium: 'low', high: 'medium' },
medium: { low: 'low', medium: 'medium', high: 'high' },
high: { low: 'medium', medium: 'high', high: 'very_high' },
}
return matrix[likelihood]?.[impact] || 'medium'
}