Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete organisation #15

Merged
merged 12 commits into from
Oct 10, 2019
21 changes: 21 additions & 0 deletions resources/public/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ a:hover {
.inline-block {
display:inline-block;
}

.alert-bottom {
position: fixed;
width: calc(100% - 240px);
top: 90%;
z-index: 20;
box-shadow: 0px 2px 5px grey;
transition: 0.5s all;
margin: 10px;
padding: 5px 10px 5px 10px;
}

.alert-bottom.success {
background-color: #d4edda;
color: #104820;
}

.alert-bottom.error {
background-color: #fdd9d9;
color: #981010;
}
/* End Misc utilities */

/* Login/Signup */
Expand Down
17 changes: 17 additions & 0 deletions src/clj/villagebook/organisation/db.clj
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,20 @@
[:= :organisations.id :organisation_permissions.org_id])
(h/where [:= :user-id user-id])
(sql/format)))))

(defn get-permission
"Returns the permission a user has on an organisation"
([user-id org-id]
(get-permission (config/db-spec) user-id org-id))
([conn user-id org-id]
(-> (jdbc/find-by-keys conn :organisation_permissions {:user_id user-id
:org_id org-id})
first
:permission)))

(defn delete!
"Deletes an organisation by id."
([id]
(delete! (config/db-spec) id))
([conn id]
(jdbc/delete! conn :organisations ["id = ?" id])))
21 changes: 21 additions & 0 deletions src/clj/villagebook/organisation/handlers.clj
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,24 @@
[request]
(let [user-id (get-in request [:identity :id])]
(res/response (:success (models/retrieve-by-user user-id)))))

(defn- delete-send-response
[user-id org-id]
(let [message (models/delete! user-id org-id)
success (:success message)
error (:error message)
permission-error (:permission-error message)]
(cond
success (res/response success)
error (-> (res/response error)
(res/status 500))
permission-error (-> (res/response permission-error)
(res/status 403)))))

(defn delete!
[request]
(let [user-id (get-in request [:identity :id])
id (edn/read-string (get-in request [:params :id]))]
(if (s/valid? ::organisation-spec/id id)
(delete-send-response user-id id)
(res/bad-request "Invalid request."))))
24 changes: 21 additions & 3 deletions src/clj/villagebook/organisation/models.clj
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
(ns villagebook.organisation.models
(:require [villagebook.organisation.db :as db]
[villagebook.factory :as factory]))
[villagebook.factory :as factory]
[clojure.java.jdbc :as jdbc]
[villagebook.config :as config]))

(defonce DEFAULT_PERMISSION :owner)
(defonce OWNER_PERMISSION :owner)

(defn create!
[orgdata user-id]
(let [{org-id :id :as org} (db/create! orgdata)
permission (db/add-user-as! org-id user-id DEFAULT_PERMISSION)]
permission (db/add-user-as! org-id user-id OWNER_PERMISSION)]
{:success org}))

(defn retrieve
Expand All @@ -20,3 +22,19 @@
(defn retrieve-by-user
[user-id]
{:success (db/retrieve-by-user user-id)})

(defn is-owner?
([org-id user-id]
(is-owner? (config/db-spec) org-id user-id))
([conn org-id user-id]
(= (db/get-permission conn user-id org-id) (name OWNER_PERMISSION))))

(defn delete!
[user-id id]
"Check if user is owner of the organisation and delete it"
(jdbc/with-db-transaction [trn (config/db-spec)]
(if (is-owner? trn id user-id)
(if-not (empty? (db/delete! trn id))
{:success "Organisation deleted"}
{:error "Error in deleting organisation"})
{:permission-error "Invalid permission"})))
3 changes: 2 additions & 1 deletion src/clj/villagebook/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
(def api-routes
{"organisations" {:get (with-auth org/retrieve-by-user)
:post (with-auth org/create!)
["/" :id] {:get (with-auth org/retrieve)}}
["/" :id] {:get (with-auth org/retrieve)
:delete (with-auth org/delete!)}}
"user" {:get (with-auth user/retrieve)}})


Expand Down
8 changes: 7 additions & 1 deletion src/cljs/villagebookUI/api/organisation.cljs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
(ns villagebookUI.api.organisation
[:require [ajax.core :refer [GET POST]]])
[:require [ajax.core :refer [GET POST DELETE]]])

(def ^:private create-org-api "/api/organisations")
(def ^:private get-all-orgs-api "/api/organisations")
(defn ^:private delete-org-api [id] (str "/api/organisations/" id))

(defn create [org-data handler error-handler]
(POST create-org-api
Expand All @@ -16,3 +17,8 @@
{:handler handler
:response-format :json
:error-handler error-handler}))

(defn delete [id handler error-handler]
(DELETE (delete-org-api id)
{:handler handler
:error-handler error-handler}))
46 changes: 42 additions & 4 deletions src/cljs/villagebookUI/components/main_content.cljs
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
(ns villagebookUI.components.main-content
(:require [villagebookUI.store.organisations :as org-store]))
(:require [villagebookUI.store.organisations :as org-store]
[villagebookUI.api.organisation :as org-api]
[villagebookUI.fetchers :as fetchers]
[villagebookUI.components.utils :as utils]
[villagebookUI.store.ui :as ui-store]
[villagebookUI.helpers :as helpers]))

(declare navbar content-box delete-org-btn delete-org)

(defn main-content []
[:div.main-content
[:div.navbar
[:h5 (:name (org-store/get-selected))]]])
(let [org (org-store/get-selected)]
[:div.main-content
[navbar org]
(if org
[content-box]
[:h5 "Oops, page not found"])
[utils/alert-bottom (ui-store/get-el-state :alert-bottom)]]))

(defn- navbar
[org]
[:div.navbar
[:h5 (:name org)]
[delete-org-btn org]])

(defn content-box []
[:div])

(defn delete-org-btn
[org]
(if (= (:permission org) "owner")
[:a {:href "#"
:style {:height "30px"}
:on-click #(delete-org org)}
"Delete organisation"]))

(defn delete-org
[org]
(if (js/confirm (str "Are you sure you want to delete " (:name org) "?"))
(org-api/delete (:id org)
(fn [res]
(helpers/show-alert-bottom! :success res)
(fetchers/fetch-orgs! first))
(fn [res]
(helpers/show-alert-bottom! :error res)))))
7 changes: 7 additions & 0 deletions src/cljs/villagebookUI/components/utils.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@
(reset! color %))}]
[:label.item-label.new-item-label {:for "new-item-label"
:style {"backgroundColor" @color}}]])))

(defn alert-bottom
[alert]
[:div.alert-bottom
{:style {:display (if alert :block :none)}
:class (:status alert)}
(:message alert)])
11 changes: 9 additions & 2 deletions src/cljs/villagebookUI/helpers.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(ns villagebookUI.helpers
(:require [villagebookUI.store.session :as session]))

(:require [villagebookUI.store.session :as session]
[villagebookUI.store.ui :as ui-store]))

(defn random-color []
(str "hsl(" (->> (.random js/Math)
Expand Down Expand Up @@ -41,3 +41,10 @@
(on-success))
(fn [res]
(show-error (:response res))))))

(defn show-alert-bottom!
[status message & [time]]
(let [time (if (number? time) (* time 1000) 4000)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

number? seems like the wrong check here. are you checking for presence?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking if time is a number and is not nil.

(ui-store/set! :alert-bottom {:status status
:message message})
(js/setTimeout #(ui-store/set! :alert-bottom nil) time)))
4 changes: 3 additions & 1 deletion src/cljs/villagebookUI/store/core.cljs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
(ns villagebookUI.store.core
(:require [villagebookUI.store.user :as user-store]
[villagebookUI.store.session :as session]
[villagebookUI.store.organisations :as org-store]))
[villagebookUI.store.organisations :as org-store]
[villagebookUI.store.ui :as ui]))

(defn init! []
(session/init!)
(ui/init!)
(user-store/init!)
(org-store/init!))
15 changes: 15 additions & 0 deletions src/cljs/villagebookUI/store/ui.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns villagebookUI.store.ui
(:require [reagent.core :as r]
[villagebookUI.store.state :refer [state]]))

(def ui-state
(r/cursor state [:ui]))

(defn get-el-state [element-key]
(get @ui-state element-key))

(defn set! [element-key val]
(swap! ui-state assoc element-key val))

(defn init! []
(reset! ui-state {}))
23 changes: 22 additions & 1 deletion test/clj/villagebook/organisation/db_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,29 @@
(testing "Should retrieve list of user's organisations with permissions"
(let [{user-id :id} (user-db/create! factory/user1)
{org-id :id} (db/create! factory/organisation)
{permission :permission} (db/add-user-as! org-id user-id "member")
{permission :permission} (db/add-user-as! org-id user-id :member)
required-keys (keys factory/organisation)]
(is (= factory/organisation (-> (db/retrieve-by-user user-id)
first
(select-keys required-keys)))))))

(deftest delete-tests
(let [{user-id :id} (user-db/create! factory/user1)
{org-id :id} (db/create! factory/organisation)
permission (db/add-user-as! org-id user-id :owner)
deleted-row (db/delete! org-id)]

(testing "Should delete an organisation by it's id"
(is (= '(1) deleted-row))
(is (= nil (db/retrieve org-id))))

(testing "All permissions on the organisation should be deleted on cascade"
(is (empty? (db/retrieve-by-user user-id))))))

(deftest get-permission-tests
(testing "Should retrieve user's permission on an organisation"
(let [{user-id :id} (user-db/create! factory/user1)
{org-id :id} (db/create! factory/organisation)
{reqd-permission :permission} (db/add-user-as! org-id user-id :owner)
permission (db/get-permission user-id org-id)]
(is (= permission reqd-permission)))))
15 changes: 15 additions & 0 deletions test/clj/villagebook/organisation/handlers_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,18 @@
response (handlers/retrieve-by-user request)]
(is (= 200 (:status response)))
(is (empty? (:body response))))))

(deftest delete-tests
(testing "Should delete an organisation"
(let [{user-id :id} (user-db/create! factory/user1)
{orgdata :success} (models/create! factory/organisation user-id)
id (:id orgdata)
request {:identity {:id user-id} :params {:id (str id)}}
response (handlers/delete! request)]
(is (= 200 (:status response))))))

(deftest invalid-delete-tests
(testing "Should return 403 if invalid permission or organisation does not exist"
(let [request {:identity {:id 0} :params {:id (str 1)}}
response (handlers/delete! request)]
(is (= 403 (:status response))))))
8 changes: 8 additions & 0 deletions test/clj/villagebook/organisation/models_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,11 @@
(testing "Should return an empty vector if no organisations exist for the user"
(let [{orgs :success} (models/retrieve-by-user 0)]
(is (empty? orgs)))))

(deftest delete-tests
(testing "Should delete an organisation if user is owner"
(let [{user-id :id} (user-db/create! factory/user1)
{orgdata :success} (models/create! factory/organisation user-id)
org-id (:id orgdata)
{message :success} (models/delete! user-id org-id)]
(is (= nil (db/retrieve org-id))))))