Skip to content

Commit

Permalink
Merge d4acc66 into e57e401
Browse files Browse the repository at this point in the history
  • Loading branch information
mijailr committed Nov 22, 2019
2 parents e57e401 + d4acc66 commit 252df9c
Show file tree
Hide file tree
Showing 21 changed files with 267 additions and 49 deletions.
12 changes: 4 additions & 8 deletions .github/workflows/elixir.yml
Expand Up @@ -49,13 +49,9 @@ jobs:
run: |
mix do ecto.create, ecto.migrate
- name: Run Tests
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: |
mix test
mix coveralls.json
mix credo
- name: Code Coverage
uses: codecov/codecov-action@v1.0.3
with:
token: ${{secrets.CODECOV_TOKEN}}
file: cover/excoveralls.json

mix test
mix coveralls.post -s $GITHUB_SHA -n $GITHUB_EVENT_NAME -b $GITHUB_REF -c $GITHUB_ACTOR
3 changes: 1 addition & 2 deletions config/config.exs
@@ -1,8 +1,6 @@
import Config

config :comment,
github_secret_key: "dummysecretkey",
github_app_id: "000000",
port: 3000,
ecto_repos: [Comment.Repo]

Expand Down Expand Up @@ -37,3 +35,4 @@ config :tentacat,
request_options: []

import_config("#{Mix.env()}.exs")

4 changes: 4 additions & 0 deletions config/dev.exs
@@ -1,5 +1,9 @@
import Config

config :comment,
github_secret_key: System.get_env("GITHUB_SECRET_KEY", "dummysecretkey"),
github_app_id: System.get_env("GITHUB_APP_ID", "00000")

config :comment, Comment.Repo,
database: "comment_dev",
hostname: System.get_env("POSTGRES_HOST", "localhost"),
Expand Down
4 changes: 4 additions & 0 deletions config/test.exs
@@ -1,5 +1,9 @@
import Config

config :comment,
github_secret_key: "dummysecretkey",
github_app_id: "00000"

config :comment, Comment.Repo,
database: "comment_test",
hostname: System.get_env("POSTGRES_HOST", "localhost"),
Expand Down
14 changes: 14 additions & 0 deletions lib/comment/body_reader.ex
@@ -0,0 +1,14 @@
defmodule Comment.BodyReader do
@moduledoc """
Used for store a raw copy of the request body before be
processed by Plug.Parsers to be used in the signature verification.
"""
alias Plug.Conn

@spec read_body(Conn.t(), Keyword.t()) :: {:ok, binary, Conn.t()} | {:error, Conn.term()}
def read_body(%Conn{} = conn, opts) do
{:ok, body, conn} = Conn.read_body(conn, opts)
conn = update_in(conn.assigns[:raw_body], &[body | &1 || []])
{:ok, body, conn}
end
end
39 changes: 39 additions & 0 deletions lib/comment/github.ex
Expand Up @@ -3,4 +3,43 @@ defmodule Comment.Github do
This module handle the Github actions for installations
and pull requests.
"""

alias Comment.{Installation, Repository}

def handle_request("installation" = event, %{
"action" => action,
"installation" => installation,
"repositories" => repositories
}) do
event
|> handle_action(action, installation, repositories)
end

def handle_request(_event, params) do
IO.puts(params)
end

defp handle_action("installation", "created", details, repos) do
params = %{
installation_id: details["id"],
account_login: details["account"]["login"],
account_id: details["target_id"],
account_type: details["target_type"]
}

repositories =
repos
|> Enum.map(&convert_repositories/1)

Installation.create(params, repositories)
end

defp convert_repositories(repo) do
Repository.changeset(%{
full_name: repo["full_name"],
name: repo["name"],
private: repo["private"],
repository_id: repo["id"]
})
end
end
6 changes: 3 additions & 3 deletions lib/comment/github/verify_request.ex
Expand Up @@ -2,7 +2,7 @@ defmodule Comment.Github.VerifyRequest do
@moduledoc """
This module handle the Plug for Github requests
"""
import Plug.Conn, only: [get_req_header: 2, read_body: 1]
import Plug.Conn, only: [get_req_header: 2]

alias Comment.Crypto
alias Comment.{HTTPBadRequest, HTTPNotAuthorized}
Expand Down Expand Up @@ -36,13 +36,13 @@ defmodule Comment.Github.VerifyRequest do
raise(HTTPBadRequest)
end

{:ok, body, _} = read_body(conn)
%Plug.Conn{assigns: %{raw_body: body}} = conn

result_digest =
secret_key!()
|> Crypto.calculate_signature(body)

unless digest == result_digest, do: raise(HTTPNotAuthorized)
unless digest == result_digest, do: raise("Digest: #{result_digest}")
end

defp secret_key! do
Expand Down
10 changes: 6 additions & 4 deletions lib/comment/models/installation.ex
Expand Up @@ -15,20 +15,22 @@ defmodule Comment.Installation do
field(:account_type, :string)
field(:active, :boolean, default: true)

has_many(:repositories, Repository)
has_many(:repositories, Repository, on_delete: :delete_all)

timestamps()
end

def create(params) do
def create(params, repositories) do
%Installation{}
|> changeset(params)
|> changeset(params, repositories)
|> Repo.insert()
end

def changeset(schema, params) do
def changeset(%Installation{} = schema, params, repositories) do
schema
|> cast(params, [:account_id, :account_login, :account_type, :installation_id])
|> put_assoc(:repositories, repositories)
|> cast_assoc(:repositories, with: &Repository.changeset/1)
|> validate_required([:account_id, :account_login, :account_type, :installation_id])
|> unique_constraint(:installation_id)
end
Expand Down
14 changes: 10 additions & 4 deletions lib/comment/models/repository.ex
Expand Up @@ -5,22 +5,28 @@ defmodule Comment.Repository do
use Ecto.Schema
import Ecto.Changeset

alias Comment.{Installation, Repository}
alias Comment.{Installation, Repository, Repo}

schema "repositories" do
field(:full_name, :string)
field(:name, :integer)
field(:name, :string)
field(:private, :boolean, default: true)
field(:repository_id, :string)
field(:repository_id, :integer)

belongs_to(:installation, Installation)

timestamps()
end

def create(params) do
changeset(params)
|> Repo.insert()
end

def changeset(params) do
%Repository{}
|> cast(params, [:full_name, :name, :private, :repository_id])
|> cast(params, [:full_name, :name, :private, :repository_id, :installation_id])
|> assoc_constraint(:installation)
|> validate_required([:full_name, :name, :repository_id, :installation_id])
end
end
11 changes: 7 additions & 4 deletions lib/comment/server.ex
Expand Up @@ -6,13 +6,14 @@ defmodule Comment.Server do
use Plug.Router
use Plug.ErrorHandler

alias Comment.{Github}
alias Comment.{Github, BodyReader}

plug(:match)

plug(Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
body_reader: {BodyReader, :read_body, []},
json_decoder: Jason
)

Expand All @@ -21,9 +22,11 @@ defmodule Comment.Server do
plug(:dispatch)

post "/webhook/github" do
# [signature] = get_req_header(conn, "x-hub-signature")
# payload = conn.assigns.raw_body
# response = Comment.Github.handle_payload(payload, signature)
conn
|> get_req_header("x-github-event")
|> List.first()
|> Github.handle_request(conn.params)

send_resp(conn, 200, get_req_header(conn, "x-hub-signature"))
end

Expand Down
8 changes: 6 additions & 2 deletions mix.exs
Expand Up @@ -17,7 +17,11 @@ defmodule Comment.MixProject do
coveralls: :test,
"coveralls.detail": :test,
"coveralls.html": :test,
docs: :dev
docs: :dev,
vcr: :test,
"vcr.delete": :test,
"vcr.check": :test,
"vcr.show": :test
],
releases: releases(),
start_permanent: Mix.env() == :prod,
Expand Down Expand Up @@ -59,8 +63,8 @@ defmodule Comment.MixProject do
# Test dependencies
{:credo, "~> 1.1.5", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.0.0-rc.7", only: [:dev], runtime: false},
{:excoveralls, "~> 0.12", only: :test},
{:ex_machina, "~> 2.3", only: :test},
{:excoveralls, "~> 0.12", only: :test},
{:faker, "~> 0.13", only: :test},

# Docs dependencies
Expand Down
3 changes: 3 additions & 0 deletions mix.lock
Expand Up @@ -16,8 +16,10 @@
"erlex": {:hex, :erlex, "0.2.5", "e51132f2f472e13d606d808f0574508eeea2030d487fc002b46ad97e738b0510", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"},
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm"},
"excoveralls": {:hex, :excoveralls, "0.12.0", "50e17a1b116fdb7facc2fe127a94db246169f38d7627b391376a0bc418413ce1", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"},
"exvcr": {:hex, :exvcr, "0.11.0", "59d5c11c9022852e9265d223fbde38c512cc350404f695a7b838cd7fb8dabed8", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"faker": {:hex, :faker, "0.13.0", "8abcb996f010ccd6c85588c89fc047f11134e04da019b70252f95431d721a3dc", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
Expand All @@ -29,6 +31,7 @@
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"},
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
Expand Down
8 changes: 4 additions & 4 deletions priv/repo/migrations/20191119123556_create_installations.exs
Expand Up @@ -3,10 +3,10 @@ defmodule Comment.Repo.Migrations.CreateInstallations do

def change do
create table("installations") do
add(:installation_id, :integer)
add(:account_id, :integer)
add(:account_login, :string)
add(:account_type, :string)
add(:installation_id, :integer, null: false)
add(:account_id, :integer, null: false)
add(:account_login, :string, null: false)
add(:account_type, :string, null: false)
add(:active, :boolean, default: true)

timestamps()
Expand Down
10 changes: 6 additions & 4 deletions priv/repo/migrations/20191119132632_create_repositories.exs
Expand Up @@ -3,13 +3,15 @@ defmodule Comment.Repo.Migrations.CreateRepositories do

def change do
create table(:repositories) do
add(:repository_id, :integer)
add(:name, :string)
add(:full_name, :string)
add(:repository_id, :integer, null: false)
add(:name, :string, null: false)
add(:full_name, :string, null: false)
add(:private, :boolean, default: false)
add(:installation_id, :integer)
add(:installation_id, references(:installations), null: false)

timestamps()
end

create(unique_index(:repositories, [:installation_id]))
end
end
6 changes: 3 additions & 3 deletions test/comment/crypto_test.exs
Expand Up @@ -6,9 +6,9 @@ defmodule Comment.CryptoTest do

doctest Crypto

@key "cbbdd7ee6675f43529a399cfb5f4e3249b8e9285"
@payload "String to be signed"
@signature "129e8ca6a6b279b1065f86b8e9cec6ae7f555372"
@key "dummysecret"
@payload File.read!("test/fixtures/webhook/github.installation.create.json")
@signature "e5a76829031075771f2a99b8e87402302508d148"

test "calculate_signature/2" do
assert calculate_signature(@key, @payload) == @signature
Expand Down
17 changes: 7 additions & 10 deletions test/comment/models/installation_test.exs
@@ -1,20 +1,17 @@
defmodule Comment.Models.InstallationTest do
use Comment.RepoCase
import Comment.Factory
alias Comment.Installation

@invalid_attributes %{installation_id: "NaN", account_id: "not a number"}
@valid_attributes %{
installation_id: 984,
account_login: "random_user",
account_id: 6535,
account_type: "User"
}

test "a valid installation" do
assert {:ok, %Installation{}} = Installation.create(@valid_attributes)
assert {:ok, %Installation{}} =
params_for(:installation)
|> Installation.create([])
end

test "an invalid installation" do
assert {:error, %Ecto.Changeset{}} = Installation.create(@invalid_attributes)
assert {:error, %Ecto.Changeset{}} =
params_for(:installation, installation_id: nil)
|> Installation.create([])
end
end
23 changes: 23 additions & 0 deletions test/comment/models/repository_test.exs
@@ -0,0 +1,23 @@
defmodule Comment.Models.RepositoryTest do
use Comment.RepoCase
import Comment.Factory
alias Comment.Repository

test "a valid installation with repositories" do
installation = insert(:installation)

repo = params_for(:repository, installation_id: installation.id)

{:ok, repository} =
repo
|> Repository.create()

assert %Repository{} = repository
end

test "invalid repositories" do
assert {:error, %Ecto.Changeset{}} =
params_for(:repository, name: nil)
|> Repository.create()
end
end
25 changes: 25 additions & 0 deletions test/comment/server_test.exs
@@ -0,0 +1,25 @@
defmodule Comment.ServerTest do
use ExUnit.Case, async: true
use Comment.RepoCase
use Plug.Test

alias Comment.{Server}

@opts Server.init([])

test "Github webhook installation create" do
body = File.read!("test/fixtures/webhook/github.installation.create.json")
params = Jason.decode!(body)

conn =
conn(:post, "/webhook/github", params)
|> put_req_header("x-hub-signature", "sha1=0b586799339e68bacb2f3bfd51e1b341ef04aa36")
|> put_req_header("x-github-event", "installation")
|> Plug.Conn.assign(:raw_body, body)

conn = Server.call(conn, @opts)

assert conn.state == :sent
assert conn.status == 200
end
end

0 comments on commit 252df9c

Please sign in to comment.