Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
139 lines
3.4 KiB
Elixir
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
|