All oversized iace files now comply with the 500-line hard cap: - hazard_library_ai_sw.go split into ai_sw (false_classification..communication) and ai_fw (unauthorized_access..update_failure) - hazard_library_software_hmi.go split into software_hmi (software_fault+hmi) and config_integration (configuration_error+logging+integration) - hazard_library_machine_safety.go split to keep mechanical/electrical/thermal/emc, safety_functions extracted into hazard_library_safety_functions.go - store_hazards.go split: hazard library queries moved to store_hazard_library.go - store_projects.go split: component and classification ops to store_components.go - store_mitigations.go split: evidence/verification/ref-data to store_evidence.go - hazard_library.go GetBuiltinHazardLibrary() updated to call all sub-functions - All iace tests pass (go test ./internal/iace/...) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
173 lines
6.0 KiB
Go
173 lines
6.0 KiB
Go
package iace
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Hazard Library Operations
|
|
// ============================================================================
|
|
|
|
// ListHazardLibrary lists hazard library entries, optionally filtered by category and component type
|
|
func (s *Store) ListHazardLibrary(ctx context.Context, category string, componentType string) ([]HazardLibraryEntry, error) {
|
|
query := `
|
|
SELECT
|
|
id, category, COALESCE(sub_category, ''), name, description,
|
|
default_severity, default_probability,
|
|
COALESCE(default_exposure, 3), COALESCE(default_avoidance, 3),
|
|
applicable_component_types, regulation_references,
|
|
suggested_mitigations,
|
|
COALESCE(typical_causes, '[]'::jsonb),
|
|
COALESCE(typical_harm, ''),
|
|
COALESCE(relevant_lifecycle_phases, '[]'::jsonb),
|
|
COALESCE(recommended_measures_design, '[]'::jsonb),
|
|
COALESCE(recommended_measures_technical, '[]'::jsonb),
|
|
COALESCE(recommended_measures_information, '[]'::jsonb),
|
|
COALESCE(suggested_evidence, '[]'::jsonb),
|
|
COALESCE(related_keywords, '[]'::jsonb),
|
|
is_builtin, tenant_id,
|
|
created_at
|
|
FROM iace_hazard_library WHERE 1=1`
|
|
|
|
args := []interface{}{}
|
|
argIdx := 1
|
|
|
|
if category != "" {
|
|
query += fmt.Sprintf(" AND category = $%d", argIdx)
|
|
args = append(args, category)
|
|
argIdx++
|
|
}
|
|
if componentType != "" {
|
|
query += fmt.Sprintf(" AND applicable_component_types @> $%d::jsonb", argIdx)
|
|
componentTypeJSON, _ := json.Marshal([]string{componentType})
|
|
args = append(args, string(componentTypeJSON))
|
|
argIdx++
|
|
}
|
|
|
|
query += " ORDER BY category ASC, name ASC"
|
|
|
|
rows, err := s.pool.Query(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list hazard library: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var entries []HazardLibraryEntry
|
|
for rows.Next() {
|
|
var e HazardLibraryEntry
|
|
var applicableComponentTypes, regulationReferences, suggestedMitigations []byte
|
|
var typicalCauses, relevantPhases, measuresDesign, measuresTechnical, measuresInfo, evidence, keywords []byte
|
|
|
|
err := rows.Scan(
|
|
&e.ID, &e.Category, &e.SubCategory, &e.Name, &e.Description,
|
|
&e.DefaultSeverity, &e.DefaultProbability,
|
|
&e.DefaultExposure, &e.DefaultAvoidance,
|
|
&applicableComponentTypes, ®ulationReferences,
|
|
&suggestedMitigations,
|
|
&typicalCauses, &e.TypicalHarm, &relevantPhases,
|
|
&measuresDesign, &measuresTechnical, &measuresInfo,
|
|
&evidence, &keywords,
|
|
&e.IsBuiltin, &e.TenantID,
|
|
&e.CreatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list hazard library scan: %w", err)
|
|
}
|
|
|
|
json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes)
|
|
json.Unmarshal(regulationReferences, &e.RegulationReferences)
|
|
json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations)
|
|
json.Unmarshal(typicalCauses, &e.TypicalCauses)
|
|
json.Unmarshal(relevantPhases, &e.RelevantLifecyclePhases)
|
|
json.Unmarshal(measuresDesign, &e.RecommendedMeasuresDesign)
|
|
json.Unmarshal(measuresTechnical, &e.RecommendedMeasuresTechnical)
|
|
json.Unmarshal(measuresInfo, &e.RecommendedMeasuresInformation)
|
|
json.Unmarshal(evidence, &e.SuggestedEvidence)
|
|
json.Unmarshal(keywords, &e.RelatedKeywords)
|
|
|
|
if e.ApplicableComponentTypes == nil {
|
|
e.ApplicableComponentTypes = []string{}
|
|
}
|
|
if e.RegulationReferences == nil {
|
|
e.RegulationReferences = []string{}
|
|
}
|
|
|
|
entries = append(entries, e)
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
// GetHazardLibraryEntry retrieves a single hazard library entry by ID
|
|
func (s *Store) GetHazardLibraryEntry(ctx context.Context, id uuid.UUID) (*HazardLibraryEntry, error) {
|
|
var e HazardLibraryEntry
|
|
var applicableComponentTypes, regulationReferences, suggestedMitigations []byte
|
|
var typicalCauses, relevantLifecyclePhases []byte
|
|
var recommendedMeasuresDesign, recommendedMeasuresTechnical, recommendedMeasuresInformation []byte
|
|
var suggestedEvidence, relatedKeywords []byte
|
|
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT
|
|
id, category, name, description,
|
|
default_severity, default_probability,
|
|
applicable_component_types, regulation_references,
|
|
suggested_mitigations, is_builtin, tenant_id,
|
|
created_at,
|
|
COALESCE(sub_category, ''),
|
|
COALESCE(default_exposure, 3),
|
|
COALESCE(default_avoidance, 3),
|
|
COALESCE(typical_causes, '[]'),
|
|
COALESCE(typical_harm, ''),
|
|
COALESCE(relevant_lifecycle_phases, '[]'),
|
|
COALESCE(recommended_measures_design, '[]'),
|
|
COALESCE(recommended_measures_technical, '[]'),
|
|
COALESCE(recommended_measures_information, '[]'),
|
|
COALESCE(suggested_evidence, '[]'),
|
|
COALESCE(related_keywords, '[]')
|
|
FROM iace_hazard_library WHERE id = $1
|
|
`, id).Scan(
|
|
&e.ID, &e.Category, &e.Name, &e.Description,
|
|
&e.DefaultSeverity, &e.DefaultProbability,
|
|
&applicableComponentTypes, ®ulationReferences,
|
|
&suggestedMitigations, &e.IsBuiltin, &e.TenantID,
|
|
&e.CreatedAt,
|
|
&e.SubCategory,
|
|
&e.DefaultExposure, &e.DefaultAvoidance,
|
|
&typicalCauses, &e.TypicalHarm,
|
|
&relevantLifecyclePhases,
|
|
&recommendedMeasuresDesign, &recommendedMeasuresTechnical, &recommendedMeasuresInformation,
|
|
&suggestedEvidence, &relatedKeywords,
|
|
)
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get hazard library entry: %w", err)
|
|
}
|
|
|
|
json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes)
|
|
json.Unmarshal(regulationReferences, &e.RegulationReferences)
|
|
json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations)
|
|
json.Unmarshal(typicalCauses, &e.TypicalCauses)
|
|
json.Unmarshal(relevantLifecyclePhases, &e.RelevantLifecyclePhases)
|
|
json.Unmarshal(recommendedMeasuresDesign, &e.RecommendedMeasuresDesign)
|
|
json.Unmarshal(recommendedMeasuresTechnical, &e.RecommendedMeasuresTechnical)
|
|
json.Unmarshal(recommendedMeasuresInformation, &e.RecommendedMeasuresInformation)
|
|
json.Unmarshal(suggestedEvidence, &e.SuggestedEvidence)
|
|
json.Unmarshal(relatedKeywords, &e.RelatedKeywords)
|
|
|
|
if e.ApplicableComponentTypes == nil {
|
|
e.ApplicableComponentTypes = []string{}
|
|
}
|
|
if e.RegulationReferences == nil {
|
|
e.RegulationReferences = []string{}
|
|
}
|
|
|
|
return &e, nil
|
|
}
|