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 }