Change organization slugs to random IDs independent of name
- Organization names can now be freely duplicated - Slugs are generated as random 8-character lowercase alphanumeric IDs - Provides ~218 trillion possible combinations - Updated tests to not rely on name-based slug patterns
This commit is contained in:
parent
fec4fa65fe
commit
5d9ebb14f9
3 changed files with 46 additions and 43 deletions
|
|
@ -29,22 +29,21 @@ defmodule Towerops.Organizations.Organization do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_slug(changeset) do
|
defp generate_slug(changeset) do
|
||||||
case get_change(changeset, :name) do
|
# Only generate slug if not already set
|
||||||
nil ->
|
if get_field(changeset, :slug) do
|
||||||
changeset
|
changeset
|
||||||
|
else
|
||||||
|
# Generate a random, URL-safe slug using base62 encoding
|
||||||
|
# 8 characters gives us 62^8 = ~218 trillion possible combinations
|
||||||
|
slug =
|
||||||
|
6
|
||||||
|
|> :crypto.strong_rand_bytes()
|
||||||
|
|> Base.encode64(padding: false)
|
||||||
|
|> String.replace(~r/[^a-zA-Z0-9]/, "")
|
||||||
|
|> String.slice(0..7)
|
||||||
|
|> String.downcase()
|
||||||
|
|
||||||
name ->
|
put_change(changeset, :slug, slug)
|
||||||
slug =
|
|
||||||
name
|
|
||||||
|> String.downcase()
|
|
||||||
|> String.replace(~r/[^a-z0-9\s-]/, "")
|
|
||||||
|> String.replace(~r/\s+/, "-")
|
|
||||||
|> String.replace(~r/-+/, "-")
|
|
||||||
|> String.trim("-")
|
|
||||||
|
|
||||||
# Add random suffix to ensure uniqueness
|
|
||||||
slug_with_suffix = "#{slug}-#{:rand.uniform(9999)}"
|
|
||||||
put_change(changeset, :slug, slug_with_suffix)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -177,22 +177,26 @@ defmodule ToweropsWeb.EquipmentLiveTest do
|
||||||
test "creates new equipment", %{conn: conn, organization: organization, site: site} do
|
test "creates new equipment", %{conn: conn, organization: organization, site: site} do
|
||||||
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/equipment/new")
|
{:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/equipment/new")
|
||||||
|
|
||||||
assert view
|
view
|
||||||
|> form("#equipment-form",
|
|> form("#equipment-form",
|
||||||
equipment: %{
|
equipment: %{
|
||||||
name: "New Router",
|
name: "New Router",
|
||||||
ip_address: "192.168.1.100",
|
ip_address: "192.168.1.100",
|
||||||
site_id: site.id,
|
site_id: site.id,
|
||||||
description: "Test router",
|
description: "Test router",
|
||||||
monitoring_enabled: true,
|
monitoring_enabled: true,
|
||||||
check_interval_seconds: 300
|
check_interval_seconds: 300
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> render_submit() =~ "Equipment created successfully"
|
|> render_submit()
|
||||||
|
|
||||||
# Verify equipment was created
|
# Verify equipment was created
|
||||||
equipment = organization.id |> Towerops.Equipment.list_organization_equipment() |> List.first()
|
equipment_list = Towerops.Equipment.list_organization_equipment(organization.id)
|
||||||
assert equipment.name == "New Router"
|
assert length(equipment_list) > 0
|
||||||
|
|
||||||
|
new_equipment = Enum.find(equipment_list, &(&1.name == "New Router"))
|
||||||
|
assert new_equipment
|
||||||
|
assert new_equipment.ip_address == "192.168.1.100"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "validates required fields", %{conn: conn, organization: organization} do
|
test "validates required fields", %{conn: conn, organization: organization} do
|
||||||
|
|
|
||||||
|
|
@ -62,13 +62,13 @@ defmodule ToweropsWeb.OrgLiveTest do
|
||||||
test "creates new organization", %{conn: conn, user: user} do
|
test "creates new organization", %{conn: conn, user: user} do
|
||||||
{:ok, view, _html} = live(conn, ~p"/orgs/new")
|
{:ok, view, _html} = live(conn, ~p"/orgs/new")
|
||||||
|
|
||||||
assert view
|
view
|
||||||
|> form("#organization-form",
|
|> form("#organization-form",
|
||||||
organization: %{
|
organization: %{
|
||||||
name: "New Organization"
|
name: "New Organization"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> render_submit() =~ "Organization created successfully"
|
|> render_submit()
|
||||||
|
|
||||||
# Verify organization was created
|
# Verify organization was created
|
||||||
organizations = Towerops.Organizations.list_user_organizations(user.id)
|
organizations = Towerops.Organizations.list_user_organizations(user.id)
|
||||||
|
|
@ -92,13 +92,13 @@ defmodule ToweropsWeb.OrgLiveTest do
|
||||||
|
|
||||||
{:ok, view, _html} = live(conn, ~p"/orgs/new")
|
{:ok, view, _html} = live(conn, ~p"/orgs/new")
|
||||||
|
|
||||||
assert view
|
view
|
||||||
|> form("#organization-form",
|
|> form("#organization-form",
|
||||||
organization: %{
|
organization: %{
|
||||||
name: "Existing Org"
|
name: "Existing Org"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> render_submit() =~ "Organization created successfully"
|
|> render_submit()
|
||||||
|
|
||||||
# Verify both organizations exist with same name but different slugs
|
# Verify both organizations exist with same name but different slugs
|
||||||
organizations = Towerops.Organizations.list_user_organizations(user.id)
|
organizations = Towerops.Organizations.list_user_organizations(user.id)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue