profile import now upserts

This commit is contained in:
Graham McIntire 2026-01-18 11:56:41 -06:00
parent e4b3778da4
commit 5952f32d6b
No known key found for this signature in database
2 changed files with 44 additions and 2 deletions

View file

@ -82,6 +82,30 @@ defmodule Towerops.DeviceProfiles do
|> Repo.insert()
end
@doc """
Upserts a device profile by OS name.
If a profile with the given OS exists, it updates it and deletes all associated
definitions (detection rules, sensors, etc.) to allow re-import from scratch.
Otherwise, creates a new profile.
"""
def upsert_profile(attrs \\ %{}) do
os = attrs[:os] || attrs["os"]
case get_profile(os) do
nil ->
# Create new profile
create_profile(attrs)
existing_profile ->
# Delete all associated definitions to get a clean slate
delete_profile_associations(existing_profile.id)
# Update the profile
update_profile(existing_profile, attrs)
end
end
@doc """
Updates a device profile.
"""
@ -154,6 +178,22 @@ defmodule Towerops.DeviceProfiles do
# Private functions
defp delete_profile_associations(profile_id) do
# Delete in order to respect foreign key constraints
Repo.delete_all(
from s in SensorState,
join: sd in SensorDefinition,
on: s.sensor_definition_id == sd.id,
where: sd.device_profile_id == ^profile_id
)
Repo.delete_all(from s in SensorDefinition, where: s.device_profile_id == ^profile_id)
Repo.delete_all(from p in ProcessorDefinition, where: p.device_profile_id == ^profile_id)
Repo.delete_all(from m in MemPoolDefinition, where: m.device_profile_id == ^profile_id)
Repo.delete_all(from o in OSDefinition, where: o.device_profile_id == ^profile_id)
Repo.delete_all(from d in DetectionRule, where: d.device_profile_id == ^profile_id)
end
defp list_profiles_with_detection_rules do
Repo.all(
from p in DeviceProfile,

View file

@ -118,9 +118,11 @@ defmodule Towerops.DeviceProfiles.Importer do
priority: data["priority"] || 100
}
case DeviceProfiles.create_profile(attrs) do
# Upsert profile - updates if exists, creates if new
# Deletes all associated definitions before update for clean re-import
case DeviceProfiles.upsert_profile(attrs) do
{:ok, profile} ->
# Create detection rules
# Create detection rules (associations were deleted in upsert)
:ok = create_detection_rules(profile.id, data["discovery"] || [])
{:ok, profile}