add graph endpoint

This commit is contained in:
Tao Bror Bojlén 2019-07-12 15:28:05 +01:00
parent 41e2510717
commit beca50fd2e
No known key found for this signature in database
GPG Key ID: C6EC7AAB905F9E6F
7 changed files with 199 additions and 1 deletions

View File

@ -41,6 +41,7 @@ config :backend, Backend.Scheduler,
jobs: [
# At midnight every day
{"@daily", {Backend.Scheduler, :prune_crawls, [1, "month"]}},
# 00.15 daily
{"15 0 * * *", {Backend.Scheduler, :generate_edges, []}}
]

View File

@ -1,5 +1,5 @@
defmodule Backend.Api do
alias Backend.{Instance, Repo}
alias Backend.{Crawl, Edge, Instance, Repo}
import Ecto.Query
@spec list_instances() :: [Instance.t()]
@ -14,4 +14,34 @@ defmodule Backend.Api do
|> preload(:peers)
|> Repo.get_by!(domain: domain)
end
@doc """
Returns a list of instances that
* have at least one successful crawl
* have a user count (required to give the instance a size on the graph)
"""
@spec list_nodes() :: [Instance.t()]
def list_nodes() do
crawl_subquery =
Crawl
|> select([c], %{
instance_domain: c.instance_domain,
crawl_count: count(c.id)
})
|> where([c], is_nil(c.error))
|> group_by([c], c.instance_domain)
Instance
|> join(:inner, [i], c in subquery(crawl_subquery), on: i.domain == c.instance_domain)
|> where([i, c], c.crawl_count > 0 and not is_nil(i.user_count))
|> select([c], [:domain, :user_count, :x, :y])
|> Repo.all()
end
@spec list_edges() :: [Edge.t()]
def list_edges() do
Edge
|> select([e], [:id, :source_domain, :target_domain, :weight])
|> Repo.all()
end
end

View File

@ -0,0 +1,13 @@
defmodule BackendWeb.GraphController do
use BackendWeb, :controller
alias Backend.Api
action_fallback BackendWeb.FallbackController
def index(conn, _params) do
nodes = Api.list_nodes()
edges = Api.list_edges()
render(conn, "index.json", nodes: nodes, edges: edges)
end
end

View File

@ -9,5 +9,6 @@ defmodule BackendWeb.Router do
pipe_through :api
resources "/instances", InstanceController, only: [:index, :show]
resources "/graph", GraphController, only: [:index]
end
end

View File

@ -0,0 +1,39 @@
defmodule BackendWeb.GraphView do
use BackendWeb, :view
alias BackendWeb.GraphView
require Logger
def render("index.json", %{nodes: nodes, edges: edges}) do
%{
nodes: render_many(nodes, GraphView, "node.json"),
edges: render_many(edges, GraphView, "edge.json")
}
end
def render("node.json", %{graph: node}) do
Logger.info(inspect(node))
size =
case node.user_count > 1 do
true -> :math.log(node.user_count)
false -> 1
end
%{
id: node.domain,
label: node.domain,
size: size,
x: node.x,
y: node.y
}
end
def render("edge.json", %{graph: edge}) do
%{
id: edge.id,
source: edge.source_domain,
target: edge.target_domain,
size: edge.weight
}
end
end

View File

@ -0,0 +1,10 @@
defmodule Backend.Repo.Migrations.AddInstanceCoords do
use Ecto.Migration
def change do
alter table(:instances) do
add :x, :float
add :y, :float
end
end
end

View File

@ -0,0 +1,104 @@
defmodule BackendWeb.GraphControllerTest do
use BackendWeb.ConnCase
alias Backend.Api
alias Backend.Api.Graph
@create_attrs %{
id: "some id",
label: "some label",
size: 120.5,
x: 120.5,
y: 120.5
}
@update_attrs %{
id: "some updated id",
label: "some updated label",
size: 456.7,
x: 456.7,
y: 456.7
}
@invalid_attrs %{id: nil, label: nil, size: nil, x: nil, y: nil}
def fixture(:graph) do
{:ok, graph} = Api.create_graph(@create_attrs)
graph
end
setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")}
end
describe "index" do
test "lists all nodes", %{conn: conn} do
conn = get(conn, Routes.graph_path(conn, :index))
assert json_response(conn, 200)["data"] == []
end
end
describe "create graph" do
test "renders graph when data is valid", %{conn: conn} do
conn = post(conn, Routes.graph_path(conn, :create), graph: @create_attrs)
assert %{"id" => id} = json_response(conn, 201)["data"]
conn = get(conn, Routes.graph_path(conn, :show, id))
assert %{
"id" => id,
"id" => "some id",
"label" => "some label",
"size" => 120.5,
"x" => 120.5,
"y" => 120.5
} = json_response(conn, 200)["data"]
end
test "renders errors when data is invalid", %{conn: conn} do
conn = post(conn, Routes.graph_path(conn, :create), graph: @invalid_attrs)
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "update graph" do
setup [:create_graph]
test "renders graph when data is valid", %{conn: conn, graph: %Graph{id: id} = graph} do
conn = put(conn, Routes.graph_path(conn, :update, graph), graph: @update_attrs)
assert %{"id" => ^id} = json_response(conn, 200)["data"]
conn = get(conn, Routes.graph_path(conn, :show, id))
assert %{
"id" => id,
"id" => "some updated id",
"label" => "some updated label",
"size" => 456.7,
"x" => 456.7,
"y" => 456.7
} = json_response(conn, 200)["data"]
end
test "renders errors when data is invalid", %{conn: conn, graph: graph} do
conn = put(conn, Routes.graph_path(conn, :update, graph), graph: @invalid_attrs)
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "delete graph" do
setup [:create_graph]
test "deletes chosen graph", %{conn: conn, graph: graph} do
conn = delete(conn, Routes.graph_path(conn, :delete, graph))
assert response(conn, 204)
assert_error_sent 404, fn ->
get(conn, Routes.graph_path(conn, :show, graph))
end
end
end
defp create_graph(_) do
graph = fixture(:graph)
{:ok, graph: graph}
end
end