add integrations context with CRUD operations

This commit is contained in:
Graham McIntire 2026-02-12 16:55:57 -06:00
parent 39f0ffe253
commit e302b8e9c4
No known key found for this signature in database
2 changed files with 199 additions and 0 deletions

View file

@ -0,0 +1,62 @@
defmodule Towerops.Integrations do
@moduledoc """
Context for managing third-party integrations per organization.
"""
import Ecto.Query
alias Towerops.Integrations.Integration
alias Towerops.Repo
def list_integrations(organization_id) do
Integration
|> where(organization_id: ^organization_id)
|> order_by(:provider)
|> Repo.all()
end
def get_integration(organization_id, provider) do
case Repo.get_by(Integration, organization_id: organization_id, provider: provider) do
nil -> {:error, :not_found}
integration -> {:ok, integration}
end
end
def get_integration!(organization_id, provider) do
Repo.get_by!(Integration, organization_id: organization_id, provider: provider)
end
def create_integration(organization_id, attrs) do
%Integration{}
|> Integration.changeset(Map.put(attrs, :organization_id, organization_id))
|> Repo.insert()
end
def update_integration(%Integration{} = integration, attrs) do
integration
|> Integration.changeset(attrs)
|> Repo.update()
end
def update_sync_status(%Integration{} = integration, status) do
integration
|> Integration.changeset(%{
last_sync_status: status,
last_synced_at: DateTime.truncate(DateTime.utc_now(), :second)
})
|> Repo.update()
end
def delete_integration(%Integration{} = integration) do
Repo.delete(integration)
end
def change_integration(%Integration{} = integration, attrs \\ %{}) do
Integration.changeset(integration, attrs)
end
def list_enabled_integrations(provider) do
Integration
|> where(provider: ^provider, enabled: true)
|> Repo.all()
end
end

View file

@ -0,0 +1,137 @@
defmodule Towerops.IntegrationsTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Integrations
alias Towerops.Integrations.Integration
setup do
user = user_fixture()
organization = organization_fixture(user.id)
%{organization: organization}
end
describe "list_integrations/1" do
test "returns all integrations for an organization", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem"})
integrations = Integrations.list_integrations(org.id)
assert length(integrations) == 1
end
test "does not return integrations from other orgs", %{organization: org} do
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, _} = Integrations.create_integration(org2.id, %{provider: "preseem"})
assert Integrations.list_integrations(org.id) == []
end
end
describe "get_integration/2" do
test "returns integration by org and provider", %{organization: org} do
{:ok, created} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:ok, fetched} = Integrations.get_integration(org.id, "preseem")
assert fetched.id == created.id
end
test "returns error when not found", %{organization: org} do
assert {:error, :not_found} = Integrations.get_integration(org.id, "preseem")
end
end
describe "get_integration!/2" do
test "returns integration by org and provider", %{organization: org} do
{:ok, created} = Integrations.create_integration(org.id, %{provider: "preseem"})
fetched = Integrations.get_integration!(org.id, "preseem")
assert fetched.id == created.id
end
test "raises when not found", %{organization: org} do
assert_raise Ecto.NoResultsError, fn ->
Integrations.get_integration!(org.id, "preseem")
end
end
end
describe "create_integration/2" do
test "creates with valid attrs", %{organization: org} do
attrs = %{provider: "preseem", credentials: %{"api_key" => "test-123"}, enabled: true}
assert {:ok, %Integration{} = integration} = Integrations.create_integration(org.id, attrs)
assert integration.provider == "preseem"
assert integration.credentials == %{"api_key" => "test-123"}
assert integration.enabled == true
assert integration.organization_id == org.id
end
test "rejects duplicate provider per org", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:error, changeset} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert "has already been taken" in errors_on(changeset).provider
end
end
describe "update_integration/2" do
test "updates credentials", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{
provider: "preseem",
credentials: %{"api_key" => "old-key"}
})
assert {:ok, updated} =
Integrations.update_integration(integration, %{credentials: %{"api_key" => "new-key"}})
assert updated.credentials == %{"api_key" => "new-key"}
end
test "updates enabled flag", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{provider: "preseem", enabled: false})
assert {:ok, updated} = Integrations.update_integration(integration, %{enabled: true})
assert updated.enabled == true
end
end
describe "update_sync_status/2" do
test "updates last_synced_at and last_sync_status", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert integration.last_sync_status == "never"
assert integration.last_synced_at == nil
assert {:ok, updated} = Integrations.update_sync_status(integration, "success")
assert updated.last_sync_status == "success"
assert updated.last_synced_at
end
end
describe "delete_integration/1" do
test "deletes the integration", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:ok, _} = Integrations.delete_integration(integration)
assert {:error, :not_found} = Integrations.get_integration(org.id, "preseem")
end
end
describe "change_integration/2" do
test "returns a changeset", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert %Ecto.Changeset{} = Integrations.change_integration(integration)
end
end
describe "list_enabled_integrations/1" do
test "returns only enabled integrations for a provider", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem", enabled: true})
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, _} = Integrations.create_integration(org2.id, %{provider: "preseem", enabled: false})
enabled = Integrations.list_enabled_integrations("preseem")
assert length(enabled) == 1
assert hd(enabled).organization_id == org.id
end
end
end