Issue and PR webhooks
This commit is contained in:
parent
a6459a2a32
commit
4a5196987b
|
@ -1,3 +1,4 @@
|
|||
DROP TABLE users;
|
||||
DROP TABLE repositories;
|
||||
DROP TABLE issues;
|
||||
DROP TABLE pull_requests;
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
CREATE TABLE users (
|
||||
id VARCHAR(40) PRIMARY KEY, -- user id
|
||||
login VARCHAR(64) UNIQUE NOT NULL, -- github login
|
||||
name VARCHAR(128), -- user name
|
||||
email VARCHAR(128), -- user email, if present
|
||||
token VARCHAR(40) NOT NULL, -- github oauth token
|
||||
address VARCHAR(42), -- ETH address
|
||||
created TIME -- user created date
|
||||
CREATE TABLE users
|
||||
(
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
login VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128),
|
||||
email VARCHAR(128),
|
||||
token VARCHAR(40),
|
||||
address VARCHAR(42),
|
||||
created TIME
|
||||
);
|
||||
CREATE UNIQUE INDEX users_login_key ON users (login);
|
||||
|
||||
CREATE TABLE repositories
|
||||
(
|
||||
login VARCHAR(64) NOT NULL, -- github user
|
||||
repo VARCHAR(64) NOT NULL, -- github repo
|
||||
updated TIME, -- date of the last crawl
|
||||
repo_id INTEGER NOT NULL, -- github repository id
|
||||
enabled BOOLEAN DEFAULT TRUE
|
||||
repo_id INTEGER PRIMARY KEY NOT NULL,
|
||||
user_id INTEGER,
|
||||
login VARCHAR(64) NOT NULL,
|
||||
repo VARCHAR(64) NOT NULL,
|
||||
updated TIME,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
hook_id INTEGER
|
||||
);
|
||||
CREATE UNIQUE INDEX repositories_user_repo_pk ON repositories (login, repo);
|
||||
CREATE UNIQUE INDEX repositories_repo_id_pk ON repositories (repo_id);
|
||||
CREATE INDEX repositories_login_repo_index ON repositories (login, repo);
|
||||
CREATE INDEX repositories_repo_id_index ON repositories (repo_id);
|
||||
|
||||
CREATE TABLE issues
|
||||
(
|
||||
issue_id INTEGER PRIMARY KEY NOT NULL,
|
||||
repo_id INTEGER NOT NULL,
|
||||
issue_id INTEGER NOT NULL,
|
||||
address VARCHAR(42),
|
||||
CONSTRAINT issues_repo_id_issue_id_pk PRIMARY KEY (repo_id, issue_id)
|
||||
address VARCHAR(256),
|
||||
issue_number INTEGER,
|
||||
commit_id VARCHAR(40)
|
||||
);
|
||||
|
||||
CREATE TABLE pull_requests
|
||||
(
|
||||
pr_id INTEGER PRIMARY KEY NOT NULL,
|
||||
repo_id INTEGER,
|
||||
user_id INTEGER,
|
||||
parents VARCHAR(4099) -- 100 commit SHAs + 99 commas
|
||||
);
|
||||
|
|
|
@ -4,7 +4,17 @@
|
|||
-- :doc creates a new user record
|
||||
INSERT INTO users
|
||||
(id, login, name, email, token, address, created)
|
||||
VALUES (:id, :login, :name, :email, :token, :address, :created);
|
||||
SELECT
|
||||
:id,
|
||||
:login,
|
||||
:name,
|
||||
:email,
|
||||
:token,
|
||||
:address,
|
||||
:created
|
||||
WHERE NOT exists(SELECT 1
|
||||
FROM users
|
||||
WHERE id = :id);
|
||||
|
||||
-- :name update-user! :! :n
|
||||
-- :doc updates an existing user record
|
||||
|
@ -16,19 +26,19 @@ WHERE id = :id;
|
|||
-- :doc updates user token and returns updated user
|
||||
UPDATE users
|
||||
SET token = :token
|
||||
WHERE login = :login
|
||||
WHERE id = :id
|
||||
RETURNING id, login, name, email, token, address, created;
|
||||
|
||||
-- :name update-user-address! :! :n
|
||||
UPDATE users
|
||||
SET address = :address
|
||||
WHERE login = :login;
|
||||
WHERE id = :id;
|
||||
|
||||
-- :name get-user :? :1
|
||||
-- :doc retrieve a user given the login.
|
||||
SELECT *
|
||||
FROM users
|
||||
WHERE login = :login;
|
||||
WHERE id = :id;
|
||||
|
||||
-- Repositories --------------------------------------------------------------------
|
||||
|
||||
|
@ -56,10 +66,68 @@ RETURNING repo_id, login, repo, enabled;
|
|||
-- :doc returns enabled repositories for a given login
|
||||
SELECT repo_id
|
||||
FROM repositories
|
||||
WHERE login = :login AND enabled = TRUE;
|
||||
WHERE user_id = :user_id AND enabled = TRUE;
|
||||
|
||||
-- :name update-hook-id :! :n
|
||||
-- :doc updates hook_id of a specified repository
|
||||
UPDATE repositories
|
||||
SET hook_id = :hook_id
|
||||
WHERE repo_id = :repo_id;
|
||||
|
||||
-- Issues --------------------------------------------------------------------------
|
||||
|
||||
-- :name create-issue! :! :n
|
||||
-- :doc creates issue
|
||||
INSERT INTO issues (repo_id, issue_id, issue_number, address)
|
||||
SELECT
|
||||
:repo_id,
|
||||
:issue_id,
|
||||
:issue_number,
|
||||
:address
|
||||
WHERE NOT exists(SELECT 1
|
||||
FROM issues
|
||||
WHERE repo_id = :repo_id AND issue_id = :issue_id);
|
||||
|
||||
-- :name close-issue! :<! :1
|
||||
-- :doc updates issue with commit id
|
||||
UPDATE issues
|
||||
SET commit_id = :commit_id
|
||||
WHERE issue_id = :issue_id
|
||||
RETURNING repo_id, issue_id, issue_number, address, commit_id;
|
||||
|
||||
-- Pull Requests -------------------------------------------------------------------
|
||||
|
||||
-- :name create-pull-request! :! :n
|
||||
-- :doc creates pull request
|
||||
INSERT INTO pull_requests (repo_id, pr_id, user_id, parents)
|
||||
SELECT
|
||||
:repo_id,
|
||||
:pr_id,
|
||||
:user_id,
|
||||
:parents
|
||||
WHERE NOT exists(SELECT 1
|
||||
FROM pull_requests
|
||||
WHERE repo_id = :repo_id AND pr_id = :pr_id);
|
||||
|
||||
-- Bounties ------------------------------------------------------------------------
|
||||
|
||||
-- :name bounties-list :? :*
|
||||
-- :doc lists fixed issues
|
||||
SELECT
|
||||
i.address AS issue_address,
|
||||
i.repo_id AS repo_id,
|
||||
p.pr_id AS pr_id,
|
||||
p.user_id AS user_id,
|
||||
u.address AS payout_address,
|
||||
u.login AS user_login,
|
||||
u.name AS user_name,
|
||||
r.repo AS repo_name
|
||||
FROM issues i
|
||||
INNER JOIN pull_requests p
|
||||
ON p.parents LIKE '%' || i.commit_id || '%'
|
||||
AND p.repo_id = i.repo_id
|
||||
INNER JOIN users u
|
||||
ON u.id = p.user_id
|
||||
INNER JOIN repositories r
|
||||
ON r.repo_id = i.repo_id
|
||||
WHERE r.user_id = :owner_id;
|
||||
|
|
|
@ -22,11 +22,12 @@
|
|||
var context = "{{servlet-context}}";
|
||||
var csrfToken = "{{csrf-token}}";
|
||||
var authorizeUrl = "{{authorize-url}}";
|
||||
var token = "{{token}}";
|
||||
var userId = "{{userId}}";
|
||||
var user = "{{login}}";
|
||||
if (user === "") {
|
||||
user = null;
|
||||
}
|
||||
var token = "{{token}}";
|
||||
</script>
|
||||
{% script "/js/app.js" %}
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
(ns commiteth.db.issues
|
||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[clojure.set :refer [rename-keys]]))
|
||||
|
||||
(defn create
|
||||
"Creates issue"
|
||||
[repo-id issue-id issue-number address]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/create-issue! con-db {:repo_id repo-id
|
||||
:issue_id issue-id
|
||||
:issue_number issue-number
|
||||
:address address})))
|
||||
|
||||
(defn close
|
||||
"Updates issue with commit_id"
|
||||
[issue-id commit-id]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/close-issue! con-db {:issue_id issue-id :commit_id commit-id})))
|
|
@ -0,0 +1,10 @@
|
|||
(ns commiteth.db.pull-requests
|
||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[clojure.set :refer [rename-keys]]))
|
||||
|
||||
(defn create
|
||||
"Creates pull-request"
|
||||
[pull-request]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/create-pull-request! con-db pull-request)))
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
(defn get-enabled
|
||||
"Lists enabled repositories ids for a given login"
|
||||
[login]
|
||||
[user-id]
|
||||
(->>
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/get-enabled-repositories con-db {:login login}))
|
||||
(db/get-enabled-repositories con-db {:user_id user-id}))
|
||||
(mapcat vals)))
|
||||
|
||||
(defn update-hook-id
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
(ns commiteth.db.users
|
||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||
[clojure.java.jdbc :as jdbc])
|
||||
(:import [java.util Date UUID]))
|
||||
(:import [java.util Date]))
|
||||
|
||||
(defn create-user
|
||||
[login name email token]
|
||||
[user-id login name email token]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/create-user! con-db
|
||||
{:id (str (UUID/randomUUID))
|
||||
{:id user-id
|
||||
:login login
|
||||
:name name
|
||||
:email email
|
||||
|
@ -16,22 +16,22 @@
|
|||
:created (new Date)})))
|
||||
|
||||
(defn get-user
|
||||
[login]
|
||||
[user-id]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/get-user con-db {:login login})))
|
||||
(db/get-user con-db {:id user-id})))
|
||||
|
||||
(defn exists?
|
||||
[login]
|
||||
[user-id]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(some? (db/get-user con-db {:login login}))))
|
||||
(some? (db/get-user con-db {:id user-id}))))
|
||||
|
||||
(defn update-user-address
|
||||
[login address]
|
||||
[user-id address]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-user-address! con-db {:login login :address address})))
|
||||
(db/update-user-address! con-db {:id user-id :address address})))
|
||||
|
||||
(defn update-user-token
|
||||
"Updates user token and returns updated user"
|
||||
[login token]
|
||||
[user-id token]
|
||||
(jdbc/with-db-connection [con-db *db*]
|
||||
(db/update-user-token! con-db {:login login :token token})))
|
||||
(db/update-user-token! con-db {:id user-id :token token})))
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
:client-id client-id
|
||||
:client-token client-secret})
|
||||
|
||||
(defn- self-auth-params []
|
||||
{:auth (str self ":" self-password)})
|
||||
|
||||
(def repo-fields
|
||||
[:id
|
||||
:name
|
||||
|
@ -85,11 +88,17 @@
|
|||
(defn remove-webhook
|
||||
[token user repo hook-id]
|
||||
(println "removing webhook")
|
||||
(println token user repo hook-id)
|
||||
(repos/delete-hook user repo hook-id (auth-params token)))
|
||||
|
||||
(defn post-comment
|
||||
[user repo issue-id]
|
||||
(issues/create-comment user repo issue-id
|
||||
"a comment with an image link to the web service"
|
||||
{:auth (str self ":" self-password)}))
|
||||
"a comment with an image link to the web service" (self-auth-params)))
|
||||
|
||||
(defn get-commit
|
||||
[user repo commit-id]
|
||||
(repos/specific-commit user repo commit-id (self-auth-params)))
|
||||
|
||||
(defn get-issue-events
|
||||
[user repo issue-id]
|
||||
(issues/issue-events user repo issue-id (self-auth-params)))
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
[ring.util.http-response :as response]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(defn home-page [{login :login token :token}]
|
||||
(layout/render "home.html" {:login login :token token}))
|
||||
(defn home-page [{user-id :id login :login token :token}]
|
||||
(layout/render "home.html" {:userId user-id :login login :token token}))
|
||||
|
||||
(defroutes home-routes
|
||||
(GET "/" {{identity :identity} :session}
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
(let [user (github/get-user token)
|
||||
{email :email
|
||||
name :name
|
||||
login :login} user]
|
||||
login :login
|
||||
user-id :id} user]
|
||||
(or
|
||||
(users/update-user-token login token)
|
||||
(users/create-user login name email token))))
|
||||
(users/update-user-token user-id token)
|
||||
(users/create-user user-id login name email token))))
|
||||
|
||||
(defroutes redirect-routes
|
||||
(GET "/callback" [code state]
|
||||
|
|
|
@ -34,16 +34,16 @@
|
|||
(context "/api" []
|
||||
(POST "/user/address" []
|
||||
:auth-rules authenticated?
|
||||
:body-params [user :- String, address :- String]
|
||||
:body-params [user-id :- String, address :- String]
|
||||
:summary "Update user address"
|
||||
(let [result (users/update-user-address user address)]
|
||||
(let [result (users/update-user-address user-id address)]
|
||||
(if (= 1 result)
|
||||
(ok)
|
||||
(internal-server-error))))
|
||||
(GET "/user" []
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(ok {:user (users/get-user (:login user))}))
|
||||
(ok {:user (users/get-user (:id user))}))
|
||||
(GET "/user/repositories" []
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
|
@ -51,7 +51,7 @@
|
|||
(GET "/repositories" []
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(ok (repositories/get-enabled (:login user))))
|
||||
(ok (repositories/get-enabled (:id user))))
|
||||
(POST "/repository/toggle" {:keys [params]}
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
|
|
|
@ -1,24 +1,95 @@
|
|||
(ns commiteth.routes.webhooks
|
||||
(:require [compojure.core :refer [defroutes POST]]
|
||||
[commiteth.github.core :as github]
|
||||
[ring.util.http-response :refer [ok]]))
|
||||
[commiteth.db.pull-requests :as pull-requests]
|
||||
[commiteth.db.issues :as issues]
|
||||
[commiteth.db.users :as users]
|
||||
[ring.util.http-response :refer [ok]]
|
||||
[clojure.string :refer [join]])
|
||||
(:import [java.util UUID]))
|
||||
|
||||
(def label-name "bounty")
|
||||
|
||||
(defn find-issue-closed-event
|
||||
[events]
|
||||
(first (filter #(= "closed" (:event %)) events)))
|
||||
|
||||
(defn handle-issue-closed
|
||||
[{{{user :login} :owner repo :name} :repository
|
||||
{issue-id :id issue-number :number} :issue}]
|
||||
(future
|
||||
(->>
|
||||
(github/get-issue-events user repo issue-number)
|
||||
(find-issue-closed-event)
|
||||
(:commit_id)
|
||||
(issues/close issue-id))))
|
||||
|
||||
(defn get-commit-parents
|
||||
[commit]
|
||||
(->> commit :parents (map :sha) (join ",")))
|
||||
|
||||
(defn handle-pull-request-closed
|
||||
[{{{owner :login} :owner
|
||||
repo-name :name
|
||||
repo-id :id} :repository
|
||||
{{user-id :id
|
||||
login :login
|
||||
name :name} :user
|
||||
id :id
|
||||
merge-commit-sha :merge_commit_sha} :pull_request}]
|
||||
(future
|
||||
(->>
|
||||
(github/get-commit owner repo-name merge-commit-sha)
|
||||
(get-commit-parents)
|
||||
(hash-map :parents)
|
||||
(merge {:repo_id repo-id
|
||||
:pr_id id
|
||||
:user_id user-id})
|
||||
(pull-requests/create))
|
||||
(users/create-user user-id login name nil nil)))
|
||||
|
||||
(defn labeled-as-bounty?
|
||||
[action issue]
|
||||
(and
|
||||
(= "labeled" action)
|
||||
(= label-name (get-in issue [:label :name]))))
|
||||
|
||||
(defn has-bounty-label?
|
||||
[issue]
|
||||
(let [labels (get-in issue [:issue :labels])]
|
||||
(some #(= label-name (:name %)) labels)))
|
||||
|
||||
(defn gen-address []
|
||||
(UUID/randomUUID))
|
||||
|
||||
(defn handle-issue
|
||||
[issue]
|
||||
(when-let [action (:action issue)]
|
||||
(when (and
|
||||
(= "labeled" action)
|
||||
(= label-name (get-in issue [:label :name])))
|
||||
(when (labeled-as-bounty? action issue)
|
||||
(github/post-comment
|
||||
(get-in issue [:repository :owner :login])
|
||||
(get-in issue [:repository :name])
|
||||
(get-in issue [:issue :number]))))
|
||||
(get-in issue [:issue :number]))
|
||||
(let [repo-id (get-in issue [:repository :id])
|
||||
issue (:issue issue)
|
||||
issue-id (:id issue)
|
||||
issue-number (:number issue)]
|
||||
(issues/create repo-id issue-id issue-number (gen-address))))
|
||||
(when (and
|
||||
(= "closed" action)
|
||||
(has-bounty-label? issue))
|
||||
(handle-issue-closed issue)))
|
||||
(ok (str issue)))
|
||||
|
||||
(defn handle-pull-request
|
||||
[pull-request]
|
||||
(when (= "closed" (:action pull-request))
|
||||
(handle-pull-request-closed pull-request))
|
||||
(ok (str pull-request)))
|
||||
|
||||
(defroutes webhook-routes
|
||||
(POST "/webhook" {:keys [params headers]}
|
||||
(case (get headers "x-github-event")
|
||||
"issues" (handle-issue params)
|
||||
"pull_request" (handle-pull-request params)
|
||||
(ok))))
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
(defn load-user []
|
||||
(when-let [login js/user]
|
||||
(rf/dispatch [:set-active-user {:login login :token js/token}])))
|
||||
(rf/dispatch [:set-active-user {:login login :id js/userId :token js/token}])))
|
||||
|
||||
(defn init! []
|
||||
(rf/dispatch-sync [:initialize-db])
|
||||
|
|
|
@ -84,9 +84,9 @@
|
|||
|
||||
(reg-event-fx
|
||||
:save-user-address
|
||||
(fn [{:keys [db]} [_ user address]]
|
||||
(fn [{:keys [db]} [_ user-id address]]
|
||||
{:db db
|
||||
:http {:method POST
|
||||
:url "/api/user/address"
|
||||
:on-success #(println %)
|
||||
:params {:user user :address address}}}))
|
||||
:params {:user_id user-id :address address}}}))
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
[clojure.set :refer [rename-keys]]))
|
||||
|
||||
(defn save-address
|
||||
[login address]
|
||||
[user-id address]
|
||||
(fn [_]
|
||||
(rf/dispatch [:save-user-address login address])))
|
||||
(rf/dispatch [:save-user-address user-id address])))
|
||||
|
||||
(defn address-settings []
|
||||
(let [user (rf/subscribe [:user])
|
||||
login (:login @user)
|
||||
user-id (:id @user)
|
||||
address (rf/subscribe [:get-in user-address-path])]
|
||||
(fn []
|
||||
[:div.form-group
|
||||
|
@ -19,7 +19,7 @@
|
|||
[input {:placeholder "Address"
|
||||
:value-path user-address-path}]
|
||||
[:button.btn.btn-primary.btn-lg
|
||||
{:on-click (save-address login @address)}
|
||||
{:on-click (save-address user-id @address)}
|
||||
"Save"]])))
|
||||
|
||||
(defn repository-row [repo]
|
||||
|
|
Loading…
Reference in New Issue