diff --git a/lib/faces/gallery/gallery.ex b/lib/faces/gallery/gallery.ex index 48a59cf..76a2114 100644 --- a/lib/faces/gallery/gallery.ex +++ b/lib/faces/gallery/gallery.ex @@ -3,10 +3,8 @@ defmodule Faces.Gallery do The Gallery context. """ - import Ecto.Query, warn: false alias Faces.Repo - - alias Faces.Gallery.Person + alias Faces.Gallery.{Person, Importer} @doc """ Returns the list of people. @@ -20,4 +18,6 @@ defmodule Faces.Gallery do def list_people do Repo.all(Person) end + + def import_user(username), do: Importer.import(username) end diff --git a/lib/faces/gallery/github_user_data.ex b/lib/faces/gallery/github_user_data.ex new file mode 100644 index 0000000..e50f5f0 --- /dev/null +++ b/lib/faces/gallery/github_user_data.ex @@ -0,0 +1,49 @@ +defmodule Faces.Gallery.GithubUserData do + alias Tentacat.{Client, Users} + + @doc """ + Retrieves a user's information from GitHub. + + ## Examples + + iex> GitHubUserData.get("jamesotron") + {:ok, %{ + "avatar_url" => "https://avatars2.githubusercontent.com/u/59449?v=4", + "location" => "Wellington, New Zealand", + "name" => "James Harton", + "username" => "jamesotron" + }} + + iex> GitHubUserData.get("thisUserReallyDoesntExist") + {:error, "404 while retrieving thisUserReallyDoesntExist from Github: Not Found"} + """ + def get(username) do + with {200, user_data, _} <- get_user_from_github(username), + {:ok, user_data} <- just_the_facts(user_data), + {:ok, user_data} <- add_username(username, user_data) do + {:ok, user_data} + else + {:error, reason} -> + {:error, reason} + + {i, %{"message" => message}, _} when is_integer(i) -> + {:error, "#{i} while retrieving #{username} from Github: #{message}"} + + {i, _, _} when is_integer(i) -> + {:error, "#{i} while retrieving #{username} from Github"} + end + end + + defp get_user_from_github(username) do + username + |> Users.find(Client.new()) + end + + defp just_the_facts(user_data) do + {:ok, Map.take(user_data, ["avatar_url", "name", "location"])} + end + + defp add_username(username, user_data) do + {:ok, Map.put(user_data, "username", username)} + end +end diff --git a/lib/faces/gallery/importer.ex b/lib/faces/gallery/importer.ex new file mode 100644 index 0000000..298ddf6 --- /dev/null +++ b/lib/faces/gallery/importer.ex @@ -0,0 +1,23 @@ +defmodule Faces.Gallery.Importer do + alias Faces.Gallery.{Person, GithubUserData} + alias Faces.Repo + + def import(username) do + with {:ok, user_data} <- GithubUserData.get(username), + {:ok, changeset} <- generate_changeset(user_data), + {:ok, person} <- upsert(changeset) do + {:ok, person} + else + {:error, reason} -> {:error, reason} + end + end + + defp generate_changeset(user_data) do + {:ok, Person.changeset(%Person{}, user_data)} + end + + defp upsert(changeset) do + changeset + |> Repo.insert(on_conflict: :replace_all, conflict_target: [:username]) + end +end diff --git a/lib/faces/gallery/person.ex b/lib/faces/gallery/person.ex index 84aebe2..069d50f 100644 --- a/lib/faces/gallery/person.ex +++ b/lib/faces/gallery/person.ex @@ -2,11 +2,11 @@ defmodule Faces.Gallery.Person do use Ecto.Schema import Ecto.Changeset - schema "people" do - field :avatar_url, :string - field :location, :string - field :name, :string + field(:username, :string) + field(:avatar_url, :string) + field(:location, :string) + field(:name, :string) timestamps() end @@ -14,7 +14,7 @@ defmodule Faces.Gallery.Person do @doc false def changeset(person, attrs) do person - |> cast(attrs, [:name, :location, :avatar_url]) - |> validate_required([:name, :location, :avatar_url]) + |> cast(attrs, [:username, :name, :location, :avatar_url]) + |> validate_required([:username, :name, :avatar_url]) end end diff --git a/mix.exs b/mix.exs index cfcee16..72d2edc 100644 --- a/mix.exs +++ b/mix.exs @@ -40,7 +40,8 @@ defmodule Faces.Mixfile do {:phoenix_html, "~> 2.10"}, {:phoenix_live_reload, "~> 1.0", only: :dev}, {:gettext, "~> 0.11"}, - {:cowboy, "~> 1.0"} + {:cowboy, "~> 1.0"}, + {:tentacat, "~> 0.9.0"} ] end diff --git a/mix.lock b/mix.lock index 12272af..4c3edb5 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,22 @@ %{ + "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, "decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm"}, "ecto": {:hex, :ecto, "2.2.9", "031d55df9bb430cb118e6f3026a87408d9ce9638737bda3871e5d727a3594aae", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, + "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, "file_system": {:hex, :file_system, "0.2.4", "f0bdda195c0e46e987333e986452ec523aed21d784189144f647c43eaf307064", [:mix], [], "hexpm"}, "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.12.1", "8bf2d0e11e722e533903fe126e14d6e7e94d9b7983ced595b75f532e04b7fdc7", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "5.1.1", "cbc3b2fa1645113267cc59c760bafa64b2ea0334635ef06dbac8801e42f7279c", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"}, "phoenix": {:hex, :phoenix, "1.3.2", "2a00d751f51670ea6bc3f2ba4e6eb27ecb8a2c71e7978d9cd3e5de5ccf7378bd", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_html": {:hex, :phoenix_html, "2.11.1", "77b6f7fbd252168c6ec4f573de648d37cc5258cda13266ef001fbf99267eb6f3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, @@ -18,4 +27,7 @@ "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}, "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"}, "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, + "tentacat": {:hex, :tentacat, "0.9.0", "c773d6d3def1a37296330c2787549c0f0f507f45b3a580d32d7d8aa3fdd56d3f", [:mix], [{:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 0.8", [hex: :httpoison, repo: "hexpm", optional: false]}], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, } diff --git a/priv/repo/migrations/20180407212640_create_people.exs b/priv/repo/migrations/20180407212640_create_people.exs index e7f03be..8ac7e6c 100644 --- a/priv/repo/migrations/20180407212640_create_people.exs +++ b/priv/repo/migrations/20180407212640_create_people.exs @@ -3,12 +3,14 @@ defmodule Faces.Repo.Migrations.CreatePeople do def change do create table(:people) do - add :name, :string - add :location, :string - add :avatar_url, :string + add(:username, :string) + add(:name, :string) + add(:location, :string) + add(:avatar_url, :string) timestamps() end + create(index(:people, [:username], unique: true)) end end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 2bbc156..3d6c126 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -9,3 +9,9 @@ # # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. + +alias Faces.Gallery + +Gallery.import_user("jamesotron") +Gallery.import_user("terrcin") +Gallery.import_user("edgurgel") diff --git a/test/faces/gallery/github_user_data_test.exs b/test/faces/gallery/github_user_data_test.exs new file mode 100644 index 0000000..036f0f8 --- /dev/null +++ b/test/faces/gallery/github_user_data_test.exs @@ -0,0 +1,5 @@ +defmodule FacesGalleryGithubUserDataTest do + use ExUnit.Case + alias Faces.Gallery.GithubUserData + doctest Faces.Gallery.GithubUserData +end