tarakan/test/support/repository_code_instrumented_github_client.ex
Maxfield Luke af6077b9c3
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s
Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
2026-07-29 04:43:40 -04:00

139 lines
3.4 KiB
Elixir

defmodule Tarakan.RepositoryCode.InstrumentedGitHubClient do
@moduledoc false
@behaviour Tarakan.GitHubClient
alias Tarakan.GitHubStub
def child_spec(opts) do
%{id: __MODULE__, start: {__MODULE__, :start_link, [opts]}}
end
def start_link(_opts) do
Agent.start_link(
fn ->
%{
calls: %{},
notify: nil,
block_once: MapSet.new(),
visibility: :public
}
end,
name: __MODULE__
)
end
def configure(opts) do
Agent.update(__MODULE__, fn state ->
%{
state
| notify: Keyword.get(opts, :notify, state.notify),
block_once: MapSet.new(Keyword.get(opts, :block_once, [])),
visibility: Keyword.get(opts, :visibility, state.visibility)
}
end)
end
def count(kind), do: Agent.get(__MODULE__, &Map.get(&1.calls, kind, 0))
@impl true
def fetch_repository(owner, name, opts \\ []) do
record_call(:repository)
case Agent.get(__MODULE__, & &1.visibility) do
:public ->
GitHubStub.fetch_repository(owner, name, opts)
:private ->
# A visibility change always rotates the upstream ETag, so a
# conditional request can never mask it with a 304.
with {:ok, metadata} <- GitHubStub.fetch_repository(owner, name) do
{:ok, %{metadata | private: true, visibility: "private"}}
end
:not_found ->
{:error, :not_found}
{:github_id, github_id} ->
with {:ok, metadata} <- GitHubStub.fetch_repository(owner, name) do
{:ok, %{metadata | github_id: github_id}}
end
end
end
@impl true
def fetch_repository_by_id(github_id) do
record_call(:repository_by_id)
case Agent.get(__MODULE__, & &1.visibility) do
:public ->
GitHubStub.fetch_repository_by_id(github_id)
:private ->
with {:ok, metadata} <- GitHubStub.fetch_repository_by_id(github_id) do
{:ok, %{metadata | private: true, visibility: "private"}}
end
:not_found ->
{:error, :not_found}
{:github_id, _forced_id} ->
GitHubStub.fetch_repository_by_id(github_id)
end
end
@impl true
def fetch_commit(owner, name, sha) do
record_call(:commit)
GitHubStub.fetch_commit(owner, name, sha)
end
@impl true
def fetch_branch_head(owner, name, branch) do
record_call(:head)
GitHubStub.fetch_branch_head(owner, name, branch)
end
@impl true
def list_branches(owner, name) do
record_call(:branches)
GitHubStub.list_branches(owner, name)
end
@impl true
def fetch_tree(owner, name, tree_sha, recursive) do
record_call(:tree)
GitHubStub.fetch_tree(owner, name, tree_sha, recursive)
end
@impl true
def fetch_text_blob(owner, name, blob_sha) do
record_call(:blob)
GitHubStub.fetch_text_blob(owner, name, blob_sha)
end
defp record_call(kind) do
{notify, block?} =
Agent.get_and_update(__MODULE__, fn state ->
block? = MapSet.member?(state.block_once, kind)
next_state = %{
state
| calls: Map.update(state.calls, kind, 1, &(&1 + 1)),
block_once: MapSet.delete(state.block_once, kind)
}
{{state.notify, block?}, next_state}
end)
if block? and is_pid(notify) do
send(notify, {:upstream_blocked, kind, self()})
receive do
{:release_upstream, ^kind} -> :ok
after
5_000 -> :ok
end
end
end
end