This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

[Close #13] Overhaul entire architecture

- Redesign entire OTP supervision tree
- Use `Tesla` instead of `HTTPoison`
- Use `Registry` for process communication
- Abstract `Agent` usage with a `Storage` layer
- Use `:gen_statem` for `Tracker`
  • Loading branch information...
aquarhead committed Jun 9, 2017
1 parent 6cd39ec commit 46c75c99dc2affd82d64419cf75452683b747c08
View
@@ -9,13 +9,14 @@ config :card_labeler, CardLabeler.GitHub,
# {repo, project_id, new_col, close_col}
# You can get project ids via https://api.github.com/repos/:owner/:repo/projects
# You can get the column ids via https://api.github.com/projects/:project_id/columns
config :card_labeler, CardLabeler,
worker_configs: [
config :card_labeler, CardLabeler.ReposSupervisor,
repo_configs: [
{"owner/repo", 1, 42, 24},
]
# Configures the interval between worker updates
config :card_labeler, CardLabeler.Worker,
interval: 60
# Provide your configs to overwrite in "secrets.exs"
import_config "secrets.exs"
View
@@ -0,0 +1,13 @@
defmodule CardLabeler.GitHubGraphQL do
use Tesla
@token "Basic " <> Base.encode64(Application.get_env(:card_labeler, CardLabeler.GitHub)[:token])
plug Tesla.Middleware.BaseUrl, "https://api.github.com/graphql"
plug Tesla.Middleware.Headers, %{
"User-Agent" => "card_labeler",
"Authorization" => @token
}
adapter Tesla.Adapter.Hackney
end
View
@@ -0,0 +1,79 @@
defmodule CardLabeler.GitHubV3 do
use Tesla
@token "Basic " <> Base.encode64(Application.get_env(:card_labeler, CardLabeler.GitHub)[:token])
plug Tesla.Middleware.BaseUrl, "https://api.github.com"
plug Tesla.Middleware.Headers, %{
"Accept" => "application/vnd.github.inertia-preview+json",
"User-Agent" => "card_labeler",
"Authorization" => @token,
}
plug Tesla.Middleware.JSON
plug Tesla.Middleware.Retry
plug Tesla.Middleware.DebugLogger
adapter Tesla.Adapter.Hackney
def get_columns(project_id) do
get("/projects/#{project_id}/columns")
|> Map.get(:body)
|> Enum.map(fn column -> {column["id"], column["name"]} end)
|> Enum.into(%{})
end
def get_issues(repo, last_update), do: do_get_issues(repo, last_update, 1, [])
defp do_get_issues(repo, last_update, page, acc) do
resp = build_issues_url(repo, last_update, page) |> get()
new_acc = acc ++ resp.body
if has_next_page?(resp),
do: do_get_issues(repo, last_update, page+1, new_acc),
else: new_acc
end
def get_cards(column_id), do: do_get_cards(column_id, 1, [])
defp do_get_cards(column_id, page, acc) do
resp = get("/projects/columns/#{column_id}/cards?page=#{page}")
cards = Enum.filter(resp.body, fn card ->
Map.has_key?(card, "content_url") and String.contains?(card["content_url"], "/issues/")
end)
new_acc = acc ++ cards
if has_next_page?(resp),
do: do_get_cards(column_id, page+1, new_acc),
else: new_acc
end
defp build_issues_url(repo, nil, page), do: "/repos/#{repo}/issues?per_page=100&page=#{page}&state=all"
defp build_issues_url(repo, last_update, page), do: "/repos/#{repo}/issues?per_page=100&page=#{page}&state=all&since=#{last_update}"
defp has_next_page?(response) do
response.headers
|> Map.get("Link", "")
|> String.contains?("rel=\"next\"")
end
def move_card(card_id, col_id), do: post("/projects/columns/cards/#{card_id}/moves", %{position: "top", column_id: col_id})
def add_card(column_id, issue_id) do
post(
"/projects/columns/#{column_id}/cards",
%{content_id: issue_id, content_type: "Issue"}
)
|> Map.get(:body)
|> Map.get("id")
end
def remove_label(repo, issue_num, label),
do: delete("/repos/#{repo}/issues/#{issue_num}/labels/#{URI.encode(label)}")
def add_label(repo, issue_num, label),
do: post("/repos/#{repo}/issues/#{issue_num}/labels", [label])
def close_issue(repo, issue_num) do
patch(
"/repos/#{repo}/issues/#{issue_num}",
%{state: "closed"}
)
end
end
View
@@ -1,15 +1,16 @@
defmodule CardLabeler do
use Application
alias CardLabeler.ReposSupervisor
def start(_type, _args) do
import Supervisor.Spec, warn: false
children =
Enum.map(Application.get_env(:card_labeler, CardLabeler)[:worker_configs], fn config ->
worker(CardLabeler.Worker, [config], restart: :transient)
end)
children = [
supervisor(Registry, [:unique, CardLabeler.Registry]),
supervisor(ReposSupervisor, [])
]
opts = [strategy: :one_for_one, name: CardLabeler.Supervisor]
opts = [strategy: :rest_for_one, name: CardLabeler.RootSupervisor]
Supervisor.start_link(children, opts)
end
end
View

This file was deleted.

Oops, something went wrong.
View
@@ -0,0 +1,4 @@
defmodule CardLabeler.Models.Issue do
# issue["number"] as key
defstruct [:id, :state, :labels, :column, :card_id]
end
View
@@ -0,0 +1,29 @@
defmodule CardLabeler.Names do
@moduledoc """
Helper functions to compose via tuples for different parts of workers.
All names are registered using `CardLabeler.Registry`.
"""
@typep via_tuple :: {:via, atom, {String.t, atom}}
@spec repo_sup(String.t) :: via_tuple
def repo_sup(repo) do
{:via, Registry, {CardLabeler.Registry, {repo, :supervisor}}}
end
@spec repo_storage(String.t) :: via_tuple
def repo_storage(repo) do
{:via, Registry, {CardLabeler.Registry, {repo, :storage}}}
end
@spec repo_tracker(String.t) :: via_tuple
def repo_tracker(repo) do
{:via, Registry, {CardLabeler.Registry, {repo, :tracker}}}
end
@spec repo_task_sup(String.t) :: via_tuple
def repo_task_sup(repo) do
{:via, Registry, {CardLabeler.Registry, {repo, :task_sup}}}
end
end
View
@@ -0,0 +1,18 @@
defmodule CardLabeler.RepoSup do
use Supervisor
alias CardLabeler.Repo.AgentStorage
alias CardLabeler.Repo.Tracker
def start_link(repo_config) do
Supervisor.start_link(__MODULE__, repo_config, [])
end
def init(repo_config) do
children = [
worker(AgentStorage, [repo_config]),
worker(Tracker, [repo_config]),
]
supervise(children, strategy: :one_for_all)
end
end
@@ -0,0 +1,38 @@
defmodule CardLabeler.Repo.AgentStorage do
@moduledoc """
Use an `Agent` to store issue and card states.
Also provides wrappers for update.
"""
alias CardLabeler.Names
def start_link({repo, _, _, _}) do
Agent.start_link(&Map.new/0, name: Names.repo_storage(repo))
end
## Wrappers
def get(repo, key) do
Agent.get(
Names.repo_storage(repo),
&(Map.get(&1, key))
)
end
def get_all(repo) do
Agent.get(Names.repo_storage(repo), &(&1))
end
def update(repo, key, initial, fun) do
Agent.update(
Names.repo_storage(repo),
&(Map.update(&1, key, initial, fun))
)
end
def update!(repo, key, fun) do
Agent.update(
Names.repo_storage(repo),
&(Map.update!(&1, key, fun))
)
end
end
@@ -0,0 +1,3 @@
defmodule CardLabeler.Repo.Worker.Labeler do
end
View
@@ -0,0 +1,3 @@
defmodule CardLabeler.Repo.Worker.Mover do
end
Oops, something went wrong.

0 comments on commit 46c75c9

Please sign in to comment.