Merge branch 'develop' into fix/reduce-confirm-wait-time-353

This commit is contained in:
Tetiana Churikova 2018-03-19 08:40:09 +02:00 committed by GitHub
commit 8c550a4b64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 191 additions and 214 deletions

View File

@ -5,7 +5,7 @@
:exclusions [joda-time]] :exclusions [joda-time]]
[re-frame "0.10.2"] [re-frame "0.10.2"]
[cljs-ajax "0.7.2"] [cljs-ajax "0.7.2"]
[secretary "1.2.3"] [funcool/bide "1.6.0"]
[reagent-utils "0.2.1"] [reagent-utils "0.2.1"]
[reagent "0.7.0"] [reagent "0.7.0"]
[org.clojure/clojurescript "1.9.946"] [org.clojure/clojurescript "1.9.946"]

View File

@ -102,19 +102,16 @@ WHERE i.repo_id = :repo_id
AND i.confirm_hash is null AND i.confirm_hash is null
AND i.is_open = true; AND i.is_open = true;
-- :name update-repo-generic :! :n -- :name update-repo-name :! :n
/* :require [clojure.string :as string]
[hugsql.parameters :refer [identifier-param-quote]] */
UPDATE repositories UPDATE repositories
SET SET repo = :repo_name
/*~ WHERE repo_id = :repo_id
(string/join "," AND repo != :repo_name
(for [[field _] (:updates params)]
(str (identifier-param-quote (name field) options)
" = :v:updates." (name field))))
~*/
where repo_id = :repo_id;
-- :name update-repo-state :! :n
UPDATE repositories
SET state = :repo_state
WHERE repo_id = :repo_id
-- Issues -------------------------------------------------------------------------- -- Issues --------------------------------------------------------------------------
@ -412,6 +409,14 @@ SELECT exists(SELECT 1
FROM issues FROM issues
WHERE issue_id = :issue_id); WHERE issue_id = :issue_id);
-- :name get-issue :? :1
-- :doc get issue from DB by repo-id and issue-number
SELECT issue_id, issue_number, is_open, winner_login, commit_sha
FROM issues
WHERE repo_id = :repo_id
AND issue_number = :issue_number;
-- :name open-bounties :? :* -- :name open-bounties :? :*
-- :doc all open bounty issues -- :doc all open bounty issues

View File

@ -65,6 +65,7 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
src="https://www.facebook.com/tr?id=293089407869419&ev=PageView&noscript=1" src="https://www.facebook.com/tr?id=293089407869419&ev=PageView&noscript=1"
/></noscript> /></noscript>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css" rel="stylesheet" type="text/css" /> <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css" rel="stylesheet" type="text/css" />
<link href="https://unpkg.com/tachyons@4.9.1/css/tachyons.min.css" rel="stylesheet" type="text/css" />
<link href="/css/style.css?v={{commiteth-version}}" rel="stylesheet" type="text/css" /> <link href="/css/style.css?v={{commiteth-version}}" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/app.js?v={{commiteth-version}}"></script> <script type="text/javascript" src="/js/app.js?v={{commiteth-version}}"></script>
<script> <script>

View File

@ -101,3 +101,9 @@
first first
:exists :exists
boolean))) boolean)))
(defn get-issue
[repo-id issue-number]
(jdbc/with-db-connection [con-db *db*]
(db/get-issue con-db {:repo_id repo-id
:issue_number issue-number})))

View File

@ -25,13 +25,17 @@
(jdbc/with-db-connection [con-db *db*] (jdbc/with-db-connection [con-db *db*]
(db/get-enabled-repositories con-db {:user_id user-id})))) (db/get-enabled-repositories con-db {:user_id user-id}))))
(defn update-repo (defn update-repo-name
[repo-id updates] [repo-id repo-name]
(jdbc/with-db-connection [con-db *db*] (jdbc/with-db-connection [con-db *db*]
(db/update-repo-generic con-db {:repo_id repo-id (db/update-repo-name con-db {:repo_id repo-id
:updates updates}))) :repo_name repo-name})))
(defn update-repo-state
[repo-id repo-state]
(jdbc/with-db-connection [con-db *db*]
(db/update-repo-name con-db {:repo_id repo-id
:repo_state repo-state})))
(defn get-repo (defn get-repo
"Get a repo from DB given it's full name (owner/repo-name)" "Get a repo from DB given it's full name (owner/repo-name)"
[full-name] [full-name]

View File

@ -219,7 +219,7 @@
(str "Tokens: " (str "Tokens: "
(str/join " " (map (fn [[tla balance]] (format "%s: %.2f" (str/join " " (map (fn [[tla balance]] (format "%s: %.2f"
(subs (str tla) 1) (subs (str tla) 1)
(float balance))) (double balance)))
token-balances)) token-balances))
"\n"))) "\n")))

View File

@ -46,67 +46,6 @@
(log/debug "token" token "member?" member?) (log/debug "token" token "member?" member?)
member?)) member?))
(defn enable-repo [repo-id repo full-repo token]
(log/debug "enable-repo" repo-id repo)
(when (github/webhook-exists? full-repo token)
(github/remove-our-webhooks full-repo token))
(let [hook-secret (random/base64 32)]
(repositories/update-repo repo-id {:state 1
:hook_secret hook-secret})
(let [created-hook (github/add-webhook full-repo token hook-secret)]
(log/debug "Created webhook:" created-hook)
(repositories/update-repo repo-id {:hook_id (:id created-hook)})))
(github/create-label full-repo token)
(repositories/update-repo repo-id {:state 2})
(when (add-bounties-for-existing-issues?)
(bounties/add-bounties-for-existing-issues full-repo)))
(defn disable-repo [repo-id full-repo hook-id token]
(log/debug "disable-repo" repo-id full-repo)
(github/remove-webhook full-repo hook-id token)
(repositories/update-repo repo-id {:hook_secret ""
:state 0
:hook_id nil}))
;; NOTE(oskarth): This and above two functions about to be deprecated with Github App
(defn handle-toggle-repo [user params can-create?]
(log/info "XXX handle-toggle-repo" (pr-str user) (pr-str params))
(let [{user-id :id} user
{repo-id :id
full-repo :full_name
owner-avatar-url :owner-avatar-url
token :token
repo :name} params
[owner _] (str/split full-repo #"/")
db-user (users/get-user (:id user))]
(cond (not can-create?)
{:status 400
:body "Please join our Riot - chat.status.im/#/register and request
access in our #openbounty room to have your account whitelisted"}
(empty? (:address db-user))
{:status 400
:body "Please add your ethereum address to your profile first"}
:else
(try
(let [_ (println "CREATING")
db-item (repositories/create (merge params {:user_id user-id
:owner owner}))
is-enabled (= 2 (:state db-item))]
(if is-enabled
(disable-repo repo-id full-repo (:hook_id db-item) token)
(enable-repo repo-id repo full-repo token))
(ok (merge
{:enabled (not is-enabled)}
(select-keys params [:id :full_name]))))
(catch Exception e
(log/error "exception when enabling repo" e)
(repositories/update-repo repo-id {:state -1})
(internal-server-error))))))
(defn in? [coll elem] (defn in? [coll elem]
(some #(= elem %) coll)) (some #(= elem %) coll))
@ -286,9 +225,4 @@
:auth-rules authenticated? :auth-rules authenticated?
:current-user user :current-user user
(log/debug "/user/bounties") (log/debug "/user/bounties")
(ok (user-bounties user))) (ok (user-bounties user))))))
(POST "/repository/toggle" {:keys [params]}
;; NOTE: Don't allow anyone to create repos; manual add
:auth-rules authenticated?
:current-user user
(handle-toggle-repo user params (user-whitelisted? (:login user)))))))

View File

@ -109,26 +109,17 @@
(extract pr-title)))) (extract pr-title))))
(defn ensure-bounty-issue
"Checks if an issue has a bounty label attached and returns its number"
[user repo issue-number]
(when-let [issue (github/get-issue user repo issue-number)]
(when (bounties/has-bounty-label? issue)
issue-number)))
(defn handle-claim (defn handle-claim
[user-id login name avatar_url owner repo repo-id bounty-issue-number pr-id pr-number head-sha merged? event-type] [issue user-id login name avatar_url owner repo repo-id pr-id pr-number head-sha merged? event-type]
(users/create-user user-id login name nil avatar_url) (users/create-user user-id login name nil avatar_url)
(let [issue (github/get-issue owner repo bounty-issue-number) (let [open-or-edit? (contains? #{:opened :edited} event-type)
open-or-edit? (contains? #{:opened :edited} event-type)
close? (= :closed event-type) close? (= :closed event-type)
pr-data {:repo_id repo-id pr-data {:repo_id repo-id
:pr_id pr-id :pr_id pr-id
:pr_number pr-number :pr_number pr-number
:user_id user-id :user_id user-id
:issue_number bounty-issue-number :issue_number (:issue_number issue)
:issue_id (:id issue) :issue_id (:issue_id issue)
:state event-type}] :state event-type}]
;; TODO: in the opened case if the submitting user has no ;; TODO: in the opened case if the submitting user has no
@ -138,18 +129,18 @@
(cond (cond
open-or-edit? (do open-or-edit? (do
(log/info "PR with reference to bounty issue" (log/info "PR with reference to bounty issue"
bounty-issue-number "opened") (:issue_number issue) "opened")
(pull-requests/save (merge pr-data {:state :opened (pull-requests/save (merge pr-data {:state :opened
:commit_sha head-sha}))) :commit_sha head-sha})))
close? (if merged? close? (if merged?
(do (log/info "PR with reference to bounty issue" (do (log/info "PR with reference to bounty issue"
bounty-issue-number "merged") (:issue_number issue) "merged")
(pull-requests/save (pull-requests/save
(merge pr-data {:state :merged (merge pr-data {:state :merged
:commit_sha head-sha})) :commit_sha head-sha}))
(issues/update-commit-sha (:id issue) head-sha)) (issues/update-commit-sha (:issue_id issue) head-sha))
(do (log/info "PR with reference to bounty issue" (do (log/info "PR with reference to bounty issue"
bounty-issue-number "closed with no merge") (:issue_number issue) "closed with no merge")
(pull-requests/save (pull-requests/save
(merge pr-data {:state :closed (merge pr-data {:state :closed
:commit_sha head-sha}))))))) :commit_sha head-sha})))))))
@ -177,26 +168,27 @@
pr-number :number pr-number :number
pr-body :body pr-body :body
pr-title :title} :pull_request}] pr-title :title} :pull_request}]
(log/debug "handle-pull-request-event" event-type owner repo repo-id login pr-body pr-title) (log/info "handle-pull-request-event" event-type owner repo repo-id login pr-body pr-title)
(log/debug (extract-issue-number pr-body pr-title)) (if-let [issue (some->> (extract-issue-number pr-body pr-title)
(if-let [bounty-issue-number (->> (first)
(extract-issue-number pr-body pr-title) (issues/get-issue repo-id))]
(first) (if-not (:commit_sha issue) ; no PR has been merged yet referencing this issue
(ensure-bounty-issue owner repo))] (do
(do (log/info "Referenced bounty issue found" owner repo (:issue_number issue))
(log/debug "Referenced bounty issue found" owner repo bounty-issue-number) (handle-claim issue
(handle-claim user-id user-id
login name login name
avatar_url avatar_url
owner repo owner repo
repo-id repo-id
bounty-issue-number pr-id
pr-id pr-number
pr-number head-sha
head-sha merged?
merged? event-type))
event-type)) (log/info "PR for issue already merged"))
(when (= :edited event-type) (when (= :edited event-type)
; Remove PR if it does not reference any issue
(pull-requests/remove pr-id)))) (pull-requests/remove pr-id))))
@ -207,9 +199,15 @@
new-title (:title gh-issue)] new-title (:title gh-issue)]
(issues/update-issue-title issue-id new-title))) (issues/update-issue-title issue-id new-title)))
(defn update-repo-name [webhook-payload]
"Update repo name in DB if changed"
(let [{repo-id :id
repo-name :name} (:repository webhook-payload)]
(repositories/update-repo-name repo-id repo-name)))
(defn handle-issue (defn handle-issue
[webhook-payload] [webhook-payload]
(update-repo-name webhook-payload)
(when-let [action (:action webhook-payload)] (when-let [action (:action webhook-payload)]
(log/debug "handle-issue" action) (log/debug "handle-issue" action)
(when (labeled-as-bounty? action webhook-payload) (when (labeled-as-bounty? action webhook-payload)
@ -224,17 +222,17 @@
(handle-issue-reopened webhook-payload))) (handle-issue-reopened webhook-payload)))
(ok)) (ok))
(defn enable-repo-2 [repo-id full-repo] (defn enable-repo [repo-id full-repo]
(log/debug "enable-repo-2" repo-id full-repo) (log/debug "enable-repo" repo-id full-repo)
;; TODO(oskarth): Add granular permissions to enable creation of label ;; TODO(oskarth): Add granular permissions to enable creation of label
#_(github/create-label full-repo) #_(github/create-label full-repo)
(repositories/update-repo repo-id {:state 2}) (repositories/update-repo-state repo-id 2)
(when (add-bounties-for-existing-issues?) (when (add-bounties-for-existing-issues?)
(bounties/add-bounties-for-existing-issues full-repo))) (bounties/add-bounties-for-existing-issues full-repo)))
(defn disable-repo-2 [repo-id full-repo] (defn disable-repo [repo-id full-repo]
(log/debug "disable-repo-2" repo-id full-repo) (log/debug "disable-repo" repo-id full-repo)
(repositories/update-repo repo-id {:state 0})) (repositories/update-repo-state repo-id 0))
(defn full-repo->owner [full-repo] (defn full-repo->owner [full-repo]
(try (try
@ -244,8 +242,6 @@
(log/error "exception when parsing repo" e) (log/error "exception when parsing repo" e)
nil))) nil)))
;; NOTE(oskarth): Together with {enable,disable}-repo-2 above, this replaces
;; handle-toggle-repo for Github App.
(defn handle-add-repo [user-id username owner-avatar-url repo can-create?] (defn handle-add-repo [user-id username owner-avatar-url repo can-create?]
(let [repo-id (:id repo) (let [repo-id (:id repo)
repo-name (:name repo) repo-name (:name repo)
@ -285,14 +281,14 @@
_ (log/info "handle-add-repo db-item" db-item) _ (log/info "handle-add-repo db-item" db-item)
is-enabled (= 2 (:state db-item))] is-enabled (= 2 (:state db-item))]
(if is-enabled (if is-enabled
(disable-repo-2 repo-id full-repo) (disable-repo repo-id full-repo)
(enable-repo-2 repo-id full-repo)) (enable-repo repo-id full-repo))
(ok {:enabled (not is-enabled) (ok {:enabled (not is-enabled)
:id repo-id :id repo-id
:full_name full-repo})) :full_name full-repo}))
(catch Exception e (catch Exception e
(log/error "exception when enabling repo" e) (log/error "exception when enabling repo" e)
(repositories/update-repo repo-id {:state -1}) (repositories/update-repo-state repo-id -1)
(internal-server-error)))))) (internal-server-error))))))
(defn handle-installation [{:keys [action installation repositories sender]}] (defn handle-installation [{:keys [action installation repositories sender]}]
@ -335,12 +331,13 @@
(ok)) (ok))
(defn handle-pull-request (defn handle-pull-request
[pull-request] [webhook-payload]
(let [action (keyword (:action pull-request))] (update-repo-name webhook-payload)
(let [action (keyword (:action webhook-payload))]
(when (contains? #{:opened (when (contains? #{:opened
:edited :edited
:closed} action) :closed} action)
(handle-pull-request-event action pull-request)) (handle-pull-request-event action webhook-payload))
(ok))) (ok)))
@ -393,14 +390,5 @@
"pull_request" (handle-pull-request payload) "pull_request" (handle-pull-request payload)
"installation" (handle-installation payload) "installation" (handle-installation payload)
"installation_repositories" (handle-installation-repositories payload) "installation_repositories" (handle-installation-repositories payload)
;; NOTE(oskarth): These two webhooks are / will be deprecated on
;; November 22, 2017 but they keep being called. According to
;; documentation they should contain same format.
;; https://developer.github.com/webhooks/
"integration_installation" (handle-installation payload)
"integration_installation_repositories" (handle-installation-repositories payload)
(ok))) (ok)))
(forbidden))))) (forbidden)))))

View File

@ -254,12 +254,6 @@
(neg? n) (- n) (neg? n) (- n)
:else n)) :else n))
(defn float=
([x y] (float= x y 0.0000001))
([x y epsilon]
(log/debug x y epsilon)
(let [scale (if (or (zero? x) (zero? y)) 1 (abs x))]
(<= (abs (- x y)) (* scale epsilon)))))
(defn update-bounty-token-balances (defn update-bounty-token-balances
"Helper function for updating internal ERC20 token balances to token multisig contract. Will be called periodically for all open bounty contracts." "Helper function for updating internal ERC20 token balances to token multisig contract. Will be called periodically for all open bounty contracts."
@ -322,6 +316,17 @@
(db-bounties/open-bounty-contracts)] (db-bounties/open-bounty-contracts)]
(update-issue-usd-value bounty-addr)))) (update-issue-usd-value bounty-addr))))
(defn float=
([x y] (float= x y 0.0000001))
([x y epsilon]
(log/debug x y epsilon)
(let [scale (if (or (zero? x) (zero? y)) 1 (abs x))]
(<= (abs (- x y)) (* scale epsilon)))))
(defn map-float= [m1 m2]
(and (= (set (keys m1)) (set (keys m2)))
(every? #(float= (get m1 %1) (get m2 %1)) (keys m1))))
(defn update-balances (defn update-balances
[] []
(log/info "In update-balances") (log/info "In update-balances")
@ -344,13 +349,13 @@
(when (or (when (or
(not (float= db-balance-eth balance-eth)) (not (float= db-balance-eth balance-eth))
(not= db-tokens token-balances)) (not (map-float= db-tokens token-balances)))
(log/debug "balances differ") (log/info "balances differ")
(log/debug "ETH (db):" db-balance-eth (type db-balance-eth) ) (log/info "ETH (db):" db-balance-eth (type db-balance-eth) )
(log/debug "ETH (chain):" balance-eth (type balance-eth) ) (log/info "ETH (chain):" balance-eth (type balance-eth) )
(log/debug "ETH cmp:" (float= db-balance-eth balance-eth)) (log/info "ETH cmp:" (float= db-balance-eth balance-eth))
(log/debug "tokens (db):" db-tokens (type db-tokens) (type (:SNT db-tokens))) (log/info "tokens (db):" db-tokens (type db-tokens) (type (:SNT db-tokens)))
(log/debug "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances))) (log/info "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances)))
(log/debug "tokens cmp:" (= db-tokens token-balances)) (log/debug "tokens cmp:" (= db-tokens token-balances))
(issues/update-eth-balance contract-address balance-eth) (issues/update-eth-balance contract-address balance-eth)

View File

@ -135,4 +135,10 @@
[:div.page-nav-text [:span (str "Page " page-number " of " page-count)]] [:div.page-nav-text [:span (str "Page " page-number " of " page-count)]]
[draw-page-numbers page-number page-count container-element]]]))) [draw-page-numbers page-number page-count container-element]]])))
(defn usd-string
"Turn a given float into a USD currency string based on the browsers locale setting.
A more complex and customizable approach can be found in goog.i18n.NumberFormat:
https://google.github.io/closure-library/api/goog.i18n.NumberFormat.html"
[usd-float]
(.toLocaleString usd-float js/navigator.language #js {:style "currency" :currency "USD"}))

View File

@ -1,10 +1,10 @@
(ns commiteth.core (ns commiteth.core
(:require [reagent.core :as r] (:require [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[secretary.core :as secretary]
[goog.events :as events] [goog.events :as events]
[goog.history.EventType :as HistoryEventType] [goog.history.EventType :as HistoryEventType]
[commiteth.ajax :refer [load-interceptors!]] [commiteth.ajax :refer [load-interceptors!]]
[commiteth.routes]
[commiteth.handlers] [commiteth.handlers]
[commiteth.subscriptions] [commiteth.subscriptions]
[commiteth.activity :refer [activity-page]] [commiteth.activity :refer [activity-page]]
@ -53,7 +53,7 @@
[:a [:a
(merge props (merge props
(if (keyword? target) (if (keyword? target)
{:on-click #(rf/dispatch [target])} {:on-click #(commiteth.routes/nav! target)}
{:href target})) {:href target}))
caption]]))]])) caption]]))]]))
@ -66,7 +66,7 @@
[:div.ui.container.user-component [:div.ui.container.user-component
[user-dropdown [user-dropdown
@user @user
[[:update-address "My Payment Details" {}] [[:settings "My Payment Details" {}]
["/logout" "Sign Out" {:class "logout-link"}]] ["/logout" "Sign Out" {:class "logout-link"}]]
mobile?]] mobile?]]
[:a.ui.button.small.login-button {:href js/authorizeUrl} (str "LOG IN" [:a.ui.button.small.login-button {:href js/authorizeUrl} (str "LOG IN"
@ -89,7 +89,7 @@
(for [[page caption] tabs] (for [[page caption] tabs]
(let [props {:class (str "ui item" (let [props {:class (str "ui item"
(when (= @current-page page) " active")) (when (= @current-page page) " active"))
:on-click #(rf/dispatch [:set-active-page page])}] :on-click #(commiteth.routes/nav! page)}]
^{:key page} [:div props caption]))))))) ^{:key page} [:div props caption])))))))
@ -124,15 +124,6 @@
[flash-message-pane])]))) [flash-message-pane])])))
(def pages
{:activity #'activity-page
:bounties #'bounties-page
:repos #'repos-page
:manage-payouts #'manage-payouts-page
:update-address #'update-address-page
:usage-metrics #'usage-metrics-page})
(defn top-hunters [] (defn top-hunters []
(let [top-hunters (rf/subscribe [:top-hunters])] (let [top-hunters (rf/subscribe [:top-hunters])]
(fn [] (fn []
@ -211,7 +202,13 @@
[:div {:class (str (if (show-top-hunters?) "eleven" "sixteen") [:div {:class (str (if (show-top-hunters?) "eleven" "sixteen")
" wide computer sixteen wide tablet column")} " wide computer sixteen wide tablet column")}
[:div.ui.container [:div.ui.container
[(pages @current-page)]]] (case @current-page
:activity [activity-page]
:bounties [bounties-page]
:repos [repos-page]
:manage-payouts [manage-payouts-page]
:settings [update-address-page]
:usage-metrics [usage-metrics-page])]]
(when (show-top-hunters?) (when (show-top-hunters?)
[:div.five.wide.column.computer.only [:div.five.wide.column.computer.only
[:div.ui.container.top-hunters [:div.ui.container.top-hunters
@ -220,28 +217,6 @@
[top-hunters]]])]]] [top-hunters]]])]]]
[footer]]))) [footer]])))
(secretary/set-config! :prefix "#")
(secretary/defroute "/" []
(rf/dispatch [:set-active-page :bounties]))
(secretary/defroute "/activity" []
(rf/dispatch [:set-active-page :activity]))
(secretary/defroute "/repos" []
(if js/user
(rf/dispatch [:set-active-page :repos])
(secretary/dispatch! "/")))
(defn hook-browser-navigation! []
(doto (History.)
(events/listen
HistoryEventType/NAVIGATE
(fn [event]
(secretary/dispatch! (.-token event))))
(.setEnabled true)))
(defn mount-components [] (defn mount-components []
(r/render [#'page] (.getElementById js/document "app"))) (r/render [#'page] (.getElementById js/document "app")))
@ -286,7 +261,7 @@
(when config/debug? (when config/debug?
(enable-re-frisk!)) (enable-re-frisk!))
(load-interceptors!) (load-interceptors!)
(hook-browser-navigation!) (commiteth.routes/setup-nav!)
(load-data true) (load-data true)
(.addEventListener js/window "click" #(rf/dispatch [:clear-flash-message])) (.addEventListener js/window "click" #(rf/dispatch [:clear-flash-message]))
(on-js-load)) (on-js-load))

View File

@ -312,12 +312,6 @@
(:status-text response)))]})) (:status-text response)))]}))
(reg-event-fx
:update-address
(fn [{:keys [db]} [_]]
{:db db
:dispatch [:set-active-page :update-address]}))
(reg-event-db (reg-event-db
:update-user :update-user
(fn [db [_ fields]] (fn [db [_ fields]]

View File

@ -1,6 +1,6 @@
(ns commiteth.manage-payouts (ns commiteth.manage-payouts
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[commiteth.common :refer [human-time]])) [commiteth.common :as common :refer [human-time]]))
@ -57,9 +57,21 @@
(:claims bounty))] (:claims bounty))]
[claim-card bounty claim])))) [claim-card bounty claim]))))
(defn bounty-stats [{:keys [paid unpaid]}]
[:div.cf
[:div.fl-ns.w-50-ns.tc.pv4
[:div.ttu.tracked "Open"]
[:div.f2.pa2 (common/usd-string (:combined-usd-value unpaid))]
[:div (:count unpaid) " bounties"]]
[:div.fl-ns.w-50-ns.tc.pv4
[:div.ttu.tracked "Paid"]
[:div.f2.pa2 (common/usd-string (:combined-usd-value paid))]
[:div (:count paid) " bounties"]]])
(defn manage-payouts-page [] (defn manage-payouts-page []
(let [owner-bounties (rf/subscribe [:owner-bounties]) (let [owner-bounties (rf/subscribe [:owner-bounties])
bounty-stats-data (rf/subscribe [:owner-bounties-stats])
owner-bounties-loading? (rf/subscribe [:get-in [:owner-bounties-loading?]])] owner-bounties-loading? (rf/subscribe [:get-in [:owner-bounties-loading?]])]
(fn [] (fn []
(if @owner-bounties-loading? (if @owner-bounties-loading?
@ -67,17 +79,14 @@
[:div.ui.active.inverted.dimmer [:div.ui.active.inverted.dimmer
[:div.ui.text.loader "Loading"]]] [:div.ui.text.loader "Loading"]]]
(let [web3 (.-web3 js/window) (let [web3 (.-web3 js/window)
bounties (vals @owner-bounties) bounties (vals @owner-bounties)]
unpaid? #(empty? (:payout_hash %))
paid? #(not-empty (:payout_hash %))
unpaid-bounties (filter unpaid? bounties)
paid-bounties (filter paid? bounties)]
[:div.ui.container [:div.ui.container
(when (nil? web3) (when (nil? web3)
[:div.ui.warning.message [:div.ui.warning.message
[:i.warning.icon] [:i.warning.icon]
"To sign off claims, please view Status Open Bounty in Status, Mist or Metamask"]) "To sign off claims, please view Status Open Bounty in Status, Mist or Metamask"])
[bounty-stats @bounty-stats-data]
[:h3 "New claims"] [:h3 "New claims"]
[claim-list unpaid-bounties] [claim-list (filter (complement :paid?) bounties)]
[:h3 "Old claims"] [:h3 "Old claims"]
[claim-list paid-bounties]]))))) [claim-list (filter :paid? bounties)]])))))

View File

@ -0,0 +1,25 @@
(ns commiteth.routes
(:require [bide.core :as bide]
[re-frame.core :as rf]))
(defonce router
(bide/router [["/" :bounties]
["/activity" :activity]
["/repos" :repos]
["/manage-payouts" :manage-payouts]
["/settings" :settings]
["/usage-metrics" :usage-metrics]]))
(defn on-navigate
"A function which will be called on each route change."
[name params query]
(println "Route change to: " name params query)
(rf/dispatch [:set-active-page name]))
(defn setup-nav! []
(bide/start! router {:default :bounties
:on-navigate on-navigate}))
(defn nav! [route-id]
(bide/navigate! router route-id {}))

View File

@ -68,7 +68,26 @@
(reg-sub (reg-sub
:owner-bounties :owner-bounties
(fn [db _] (fn [db _]
(:owner-bounties db))) (->> (for [[id bounty] (:owner-bounties db)]
;; TODO(martinklepsch) we might want to consider using a
;; special prefix or namespace for derived properties that
;; are added to domain records like this
;; e.g. `derived/paid?`
[id (assoc bounty :paid? (boolean (:payout_hash bounty)))])
(into {}))))
(reg-sub
:owner-bounties-stats
:<- [:owner-bounties]
(fn [owner-bounties _]
(let [sum-dollars (fn sum-dollars [bounties]
(reduce + (map #(js/parseFloat (:value_usd %)) bounties)))
{:keys [paid unpaid]} (group-by #(if (:paid? %) :paid :unpaid)
(vals owner-bounties))]
{:paid {:count (count paid)
:combined-usd-value (sum-dollars paid)}
:unpaid {:count (count unpaid)
:combined-usd-value (sum-dollars unpaid)}})))
(reg-sub (reg-sub
:pagination :pagination

3
test/Jenkinsfile vendored
View File

@ -1,7 +1,8 @@
node ('linux1') {sauce('1be1b688-e0e7-4314-92a0-db11f52d3c00') { node ('linux1') {sauce('1be1b688-e0e7-4314-92a0-db11f52d3c00') {
checkout([$class: 'GitSCM', branches: [[name: '*/develop']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/status-im/open-bounty.git']]]) checkout([$class: 'GitSCM', branches: [[name: '*/develop']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CleanBeforeCheckout']], submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/status-im/open-bounty.git']]])
configFileProvider([configFile(fileId: 'sob_automation_test_config', targetLocation: 'test/end-to-end/tests')]) { configFileProvider([configFile(fileId: 'sob_automation_test_config', targetLocation: 'test/end-to-end/tests')]) {
try {sh 'cd test/end-to-end/tests && python3 -m pytest -m sanity --build=$BUILD_NAME -v -n 1' try {withCredentials([string(credentialsId: 'SOB_SAUCE_ACCESS_KEY', variable: 'SAUCE_ACCESS_KEY'), string(credentialsId: 'SOB_SAUCE_USERNAME', variable: 'SAUCE_USERNAME')])
{sh 'cd test && docker build -t end2end . && docker run --rm -e "SAUCE_USERNAME="${SAUCE_USERNAME} -e "SAUCE_ACCESS_KEY="${SAUCE_ACCESS_KEY} --name end2end-container end2end -m pytest -m sanity --build=$BUILD_NAME -v -n 1'}
} }
finally { finally {
saucePublisher() saucePublisher()

View File

@ -1,4 +1,5 @@
import configparser import configparser
import os
class TestData(object): class TestData(object):
@ -9,7 +10,7 @@ class TestData(object):
# define here path to your config.ini file # define here path to your config.ini file
# example - config_example.ini # example - config_example.ini
self.config.read("tests/config.ini") self.config.read(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config.ini'))
# self.issue['title'] is set in GithubPage::create_new_bounty # self.issue['title'] is set in GithubPage::create_new_bounty
# self.issue['id'] is set in GithubPage::create_new_bounty # self.issue['id'] is set in GithubPage::create_new_bounty

View File

@ -18,6 +18,8 @@ class BaseTestCase:
sauce_lab_cap = dict() sauce_lab_cap = dict()
sauce_lab_cap['name'] = test_data.test_name sauce_lab_cap['name'] = test_data.test_name
sauce_lab_cap['build'] = pytest.config.getoption('build') sauce_lab_cap['build'] = pytest.config.getoption('build')
sauce_lab_cap['idleTimeout'] = 900
sauce_lab_cap['commandTimeout'] = 500
sauce_lab_cap['platform'] = "MAC" sauce_lab_cap['platform'] = "MAC"
sauce_lab_cap['browserName'] = 'Chrome' sauce_lab_cap['browserName'] = 'Chrome'
sauce_lab_cap['screenResolution'] = '2048x1536' sauce_lab_cap['screenResolution'] = '2048x1536'
@ -67,8 +69,9 @@ class BaseTestCase:
if cls.environment == 'sauce': if cls.environment == 'sauce':
for caps in cls.capabilities_dev, cls.capabilities_org: for caps in cls.capabilities_dev, cls.capabilities_org:
cls.get_remote_caps(cls) remote = cls.get_remote_caps(cls)
new_caps = caps.to_capabilities() new_caps = caps.to_capabilities()
new_caps.update(remote)
driver = webdriver.Remote(cls.executor_sauce_lab, driver = webdriver.Remote(cls.executor_sauce_lab,
desired_capabilities=new_caps) desired_capabilities=new_caps)
drivers.append(driver) drivers.append(driver)
@ -79,6 +82,7 @@ class BaseTestCase:
cls.driver_dev = drivers[0] cls.driver_dev = drivers[0]
cls.driver_org = drivers[1] cls.driver_org = drivers[1]
for driver in drivers: for driver in drivers:
driver.implicitly_wait(10) driver.implicitly_wait(10)
@ -132,9 +136,9 @@ class BaseTestCase:
remove_installation(cls.driver_org) remove_installation(cls.driver_org)
######DEV ######DEV
cls.github_dev.delete_fork()
cls.github_dev.clean_repo_local_folder() cls.github_dev.clean_repo_local_folder()
cls.github_dev.delete_fork()
try: try:
cls.driver_dev.quit() cls.driver_dev.quit()
cls.driver_org.quit() cls.driver_org.quit()