Skip to main content

lib/scoria/connectors.ex

defmodule Scoria.Connectors do
  @moduledoc """
  Thin connector read-model helpers for the embedded operator surface.
  """

  import Ecto.Query, warn: false

  alias Scoria.Connectors.{CapabilitySnapshot, Connector, Grant}
  alias Scoria.Observe.Approval
  alias Scoria.Repo

  def get_connector!(connector_id), do: Repo.get!(Connector, connector_id)

  def list_connector_fleet(filters \\ %{}) do
    tenant_id = Map.get(filters, :tenant_id) || Map.get(filters, "tenant_id")

    Connector
    |> maybe_filter_tenant(tenant_id)
    |> preload([:capability_snapshot, :grants])
    |> order_by([connector], asc: connector.label, asc: connector.inserted_at)
    |> Repo.all()
    |> Enum.map(&fleet_row/1)
  end

  def get_connector_drawer(connector_id) do
    connector =
      Connector
      |> Repo.get!(connector_id)
      |> Repo.preload([:capability_snapshot, :grants])

    %{
      connector_id: connector.id,
      connector_label: connector.label,
      connector_key: connector.key,
      endpoint_url: connector.endpoint_url,
      transport_kind: connector.transport_kind,
      auth_mode: connector.auth_mode,
      status: connector.status,
      health_state: connector.health_state,
      last_refresh_status: connector.last_refresh_status,
      capability_snapshot: capability_snapshot_item(connector.capability_snapshot),
      grants: Enum.map(connector.grants, &grant_item/1)
    }
  end

  defp maybe_filter_tenant(query, nil), do: query
  defp maybe_filter_tenant(query, tenant_id), do: where(query, [connector], connector.tenant_id == ^tenant_id)

  defp fleet_row(connector) do
    pending_approval_count =
      Approval
      |> where([approval], approval.connector_id == ^connector.id and approval.status == "pending")
      |> Repo.aggregate(:count)

    %{
      connector_id: connector.id,
      connector_label: connector.label,
      connector_key: connector.key,
      health_state: connector.health_state,
      last_refresh_status: connector.last_refresh_status,
      pending_approval_count: pending_approval_count,
      pending_local_tool_count: capability_tool_count(connector.capability_snapshot),
      auth_provenance: auth_provenance(connector)
    }
  end

  defp capability_tool_count(%CapabilitySnapshot{tool_count: count}) when is_integer(count), do: count
  defp capability_tool_count(_snapshot), do: 0

  defp auth_provenance(connector) do
    latest_grant =
      connector.grants
      |> Enum.sort_by(& &1.inserted_at, {:desc, DateTime})
      |> List.first()

    %{status: latest_grant && latest_grant.status || connector.auth_mode}
  end

  defp capability_snapshot_item(nil), do: nil

  defp capability_snapshot_item(snapshot) do
    %{
      catalog_version: snapshot.catalog_version,
      tool_count: snapshot.tool_count,
      last_refresh_status: snapshot.last_refresh_status
    }
  end

  defp grant_item(%Grant{} = grant) do
    %{
      id: grant.id,
      subject_ref: grant.subject_ref,
      status: grant.status,
      granted_scopes: grant.granted_scopes || [],
      last_refresh_status: grant.last_refresh_status
    }
  end
end