mirror of
https://gitlab.com/wheres-the-tp/server.git
synced 2026-01-25 07:54:57 -06:00
Merge branch '2-allow-users-to-save-favorites-food-items' into 'master'
Resolve "allow users to save favorites food items" Closes #2 See merge request aretherecookies/server!1
This commit is contained in:
commit
f42bfe4afa
6 changed files with 133 additions and 58 deletions
|
|
@ -11,9 +11,9 @@ CREATE TYPE CATEGORY AS ENUM ('beverages', 'desserts', 'entrees', 'other');
|
||||||
DROP TABLE IF EXISTS food_items CASCADE;
|
DROP TABLE IF EXISTS food_items CASCADE;
|
||||||
CREATE TABLE food_items (
|
CREATE TABLE food_items (
|
||||||
id uuid PRIMARY KEY,
|
id uuid PRIMARY KEY,
|
||||||
name VARCHAR(100),
|
name VARCHAR(100) NOT NULL,
|
||||||
place_id VARCHAR(50),
|
place_id VARCHAR(50) NOT NULL,
|
||||||
category CATEGORY,
|
category CATEGORY NOT NULL,
|
||||||
images VARCHAR,
|
images VARCHAR,
|
||||||
loc geography(POINT,4326)
|
loc geography(POINT,4326)
|
||||||
);
|
);
|
||||||
|
|
@ -22,7 +22,7 @@ DROP TABLE IF EXISTS images CASCADE;
|
||||||
CREATE TABLE images (
|
CREATE TABLE images (
|
||||||
filename VARCHAR(200) PRIMARY KEY,
|
filename VARCHAR(200) PRIMARY KEY,
|
||||||
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
|
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
|
||||||
username VARCHAR(100),
|
username VARCHAR(100) NOT NULL,
|
||||||
date timestamp (1) with time zone
|
date timestamp (1) with time zone
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -30,10 +30,17 @@ DROP TABLE IF EXISTS quantities CASCADE;
|
||||||
CREATE TABLE quantities (
|
CREATE TABLE quantities (
|
||||||
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
|
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
|
||||||
date timestamp (1) with time zone,
|
date timestamp (1) with time zone,
|
||||||
quantity QUANTITY,
|
quantity QUANTITY NOT NULL,
|
||||||
PRIMARY KEY (food_item_id, date)
|
PRIMARY KEY (food_item_id, date)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS faves CASCADE;
|
||||||
|
CREATE TABLE faves (
|
||||||
|
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
|
||||||
|
username VARCHAR(200) NOT NULL,
|
||||||
|
date timestamp (1) with time zone NOT NULL,
|
||||||
|
PRIMARY KEY (food_item_id, username)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS food_loc_index ON food_items USING GIST ( loc );
|
CREATE INDEX IF NOT EXISTS food_loc_index ON food_items USING GIST ( loc );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,19 @@
|
||||||
quantity-handler
|
quantity-handler
|
||||||
add-food-item-handler
|
add-food-item-handler
|
||||||
add-image-handler
|
add-image-handler
|
||||||
get-images-handler]]
|
get-images-handler
|
||||||
[aretherecookies.auth :refer [facebook-auth-backend
|
faves-get-handler
|
||||||
google-auth-backend]]
|
faves-put-handler
|
||||||
|
faves-delete-handler]]
|
||||||
|
[aretherecookies.auth :refer [auth0-auth-backend]]
|
||||||
[environ.core :refer [env]]
|
[environ.core :refer [env]]
|
||||||
[compojure.handler :refer [api]]
|
[compojure.handler :refer [api]]
|
||||||
[compojure.core :refer :all]
|
[compojure.core :refer [defroutes GET POST PUT DELETE]]
|
||||||
[ring.adapter.jetty :as jetty]
|
[ring.adapter.jetty :as jetty]
|
||||||
[ring.middleware.anti-forgery :refer :all]
|
[ring.middleware.anti-forgery :refer :all]
|
||||||
[ring.middleware.json :refer [wrap-json-body]]
|
[ring.middleware.json :refer [wrap-json-body]]
|
||||||
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
||||||
[ring.middleware.defaults :refer [wrap-defaults api-defaults]]
|
[ring.middleware.defaults :refer [wrap-defaults api-defaults]]
|
||||||
[buddy.auth :refer [throw-unauthorized]]
|
|
||||||
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]))
|
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]))
|
||||||
|
|
||||||
(defroutes app-routes
|
(defroutes app-routes
|
||||||
|
|
@ -25,7 +26,10 @@
|
||||||
(POST "/quantity" [] quantity-handler)
|
(POST "/quantity" [] quantity-handler)
|
||||||
(POST "/addfooditem" [] add-food-item-handler)
|
(POST "/addfooditem" [] add-food-item-handler)
|
||||||
(POST "/images/:foodItemId" [] add-image-handler)
|
(POST "/images/:foodItemId" [] add-image-handler)
|
||||||
(GET "/images/:foodItemId" [] get-images-handler))
|
(GET "/images/:foodItemId" [] get-images-handler)
|
||||||
|
(POST "/faves" [] faves-get-handler)
|
||||||
|
(PUT "/fave" [] faves-put-handler)
|
||||||
|
(DELETE "/fave" [] faves-delete-handler))
|
||||||
|
|
||||||
(def app-config (assoc-in api-defaults [:security :anti-forgery] false))
|
(def app-config (assoc-in api-defaults [:security :anti-forgery] false))
|
||||||
|
|
||||||
|
|
@ -36,10 +40,8 @@
|
||||||
(let [port (Integer. (or port (env :port) 3000))]
|
(let [port (Integer. (or port (env :port) 3000))]
|
||||||
(->
|
(->
|
||||||
(api #'app)
|
(api #'app)
|
||||||
(wrap-authorization facebook-auth-backend)
|
(wrap-authentication auth0-auth-backend)
|
||||||
(wrap-authorization google-auth-backend)
|
(wrap-authorization auth0-auth-backend)
|
||||||
(wrap-authentication facebook-auth-backend)
|
|
||||||
(wrap-authentication google-auth-backend)
|
|
||||||
wrap-multipart-params
|
wrap-multipart-params
|
||||||
(wrap-json-body {:keywords? true})
|
(wrap-json-body {:keywords? true})
|
||||||
(jetty/run-jetty {:port port :join? false}))))
|
(jetty/run-jetty {:port port :join? false}))))
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,20 @@
|
||||||
(:require [buddy.auth.backends :as backends]
|
(:require [buddy.auth.backends :as backends]
|
||||||
[clj-http.client :as client]))
|
[clj-http.client :as client]))
|
||||||
|
|
||||||
|
; TODO implement this as LRU cache to eliminate memory leaking
|
||||||
(def tokens (atom {}))
|
(def tokens (atom {}))
|
||||||
|
|
||||||
(def facebook-me-url "https://graph.facebook.com/me?access_token=")
|
(def auth0-userinfo-url "https://aretherecookies.auth0.com/userinfo")
|
||||||
|
|
||||||
(def google-userinfo-url "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=")
|
|
||||||
|
|
||||||
(defn verify-token
|
(defn verify-token
|
||||||
"perform rest requesting using token and return boolean if successful"
|
"perform rest requesting using token and return boolean if successful"
|
||||||
[token url]
|
[token url]
|
||||||
(= 200 (:status (client/get (str url token) {:accept :json}))))
|
(try
|
||||||
|
(let [headers {:authorization (str "Bearer " token)}
|
||||||
|
res (if-not (empty? token) (client/get url {:accept :json :headers headers}) {})
|
||||||
|
status (:status res)]
|
||||||
|
(= 200 status))
|
||||||
|
(catch Exception e (print (str "Caught: " e)))))
|
||||||
|
|
||||||
(defn cache-token
|
(defn cache-token
|
||||||
"swap the new token into our cache map atom and return the token"
|
"swap the new token into our cache map atom and return the token"
|
||||||
|
|
@ -21,7 +25,7 @@
|
||||||
(defn verify-and-cache-token
|
(defn verify-and-cache-token
|
||||||
"if a REST request is successful against url using token, cache the token and return it"
|
"if a REST request is successful against url using token, cache the token and return it"
|
||||||
[token url]
|
[token url]
|
||||||
(if (verify-token token url) (cache-token token)))
|
(if (verify-token token url) (cache-token token) nil))
|
||||||
|
|
||||||
(defn url-backend
|
(defn url-backend
|
||||||
"return a buddy auth backend that validates tokens agianst given url"
|
"return a buddy auth backend that validates tokens agianst given url"
|
||||||
|
|
@ -29,8 +33,5 @@
|
||||||
(fn [_ token] (get @tokens (keyword token) (verify-and-cache-token token url))))
|
(fn [_ token] (get @tokens (keyword token) (verify-and-cache-token token url))))
|
||||||
|
|
||||||
|
|
||||||
(def facebook-auth-backend
|
(def auth0-auth-backend
|
||||||
(backends/token {:token-name "facebook-token" :authfn (url-backend facebook-me-url)}))
|
(backends/token {:token-name "auth0-token" :authfn (url-backend auth0-userinfo-url)}))
|
||||||
|
|
||||||
(def google-auth-backend
|
|
||||||
(backends/token {:token-name "google-token" :authfn (url-backend google-userinfo-url)}))
|
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,18 @@
|
||||||
"update the list of images for given food item id"
|
"update the list of images for given food item id"
|
||||||
[image]
|
[image]
|
||||||
(insert-image @pooled-db image))
|
(insert-image @pooled-db image))
|
||||||
|
|
||||||
|
(defn get-faves
|
||||||
|
"select all faves associated with a username"
|
||||||
|
[username]
|
||||||
|
(select-from-faves @pooled-db {:username username}))
|
||||||
|
|
||||||
|
(defn add-faves
|
||||||
|
"add a food item id to faves for this username"
|
||||||
|
[username foodItemIds]
|
||||||
|
(map (fn [foodItemId] (insert-into-faves @pooled-db {:username username :food_item_id foodItemId})) foodItemIds))
|
||||||
|
|
||||||
|
(defn delete-faves
|
||||||
|
"remove a list of food item ids from faves for this username"
|
||||||
|
[username foodItemIds]
|
||||||
|
(delete-from-faves @pooled-db {:username username :food_item_ids foodItemIds}))
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
insert-quantity
|
insert-quantity
|
||||||
create-food-item
|
create-food-item
|
||||||
get-images
|
get-images
|
||||||
add-image]]
|
add-image
|
||||||
|
get-faves
|
||||||
|
add-faves
|
||||||
|
delete-faves]]
|
||||||
[aretherecookies.parsers :refer [parse-special-types
|
[aretherecookies.parsers :refer [parse-special-types
|
||||||
parse-location]]
|
parse-location]]
|
||||||
[aretherecookies.aws :refer [put-s3
|
[aretherecookies.aws :refer [put-s3
|
||||||
|
|
@ -113,3 +116,32 @@
|
||||||
first
|
first
|
||||||
(update-key-to-s3 :url)
|
(update-key-to-s3 :url)
|
||||||
safe-json)))
|
safe-json)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn faves-get-handler
|
||||||
|
"return all the faves for a given username"
|
||||||
|
[req]
|
||||||
|
(if-not (authenticated? req) (throw-unauthorized))
|
||||||
|
(let [email (get-in req [:body :email])]
|
||||||
|
(println "/faves/ ----> POST " email)
|
||||||
|
(->> (get-faves email) safe-json)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn faves-put-handler
|
||||||
|
"add a food item to faves for a user"
|
||||||
|
[req]
|
||||||
|
(if-not (authenticated? req) (throw-unauthorized))
|
||||||
|
(let [email (get-in req [:body :email])
|
||||||
|
foodItemIds (get-in req [:body :foodItemIds])]
|
||||||
|
(println "/fave/" email "----> PUT" foodItemIds)
|
||||||
|
(->> (add-faves email foodItemIds) safe-json)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn faves-delete-handler
|
||||||
|
"add a food item to faves for a user"
|
||||||
|
[req]
|
||||||
|
(if-not (authenticated? req) (throw-unauthorized))
|
||||||
|
(let [email (get-in req [:body :email])
|
||||||
|
foodItemIds (get-in req [:body :foodItemIds])]
|
||||||
|
(println "/faves/" email "----> DELETE" foodItemIds)
|
||||||
|
(->> (delete-faves email foodItemIds) safe-json)))
|
||||||
|
|
|
||||||
|
|
@ -89,3 +89,21 @@ RETURNING
|
||||||
date,
|
date,
|
||||||
username,
|
username,
|
||||||
food_item_id
|
food_item_id
|
||||||
|
|
||||||
|
|
||||||
|
-- :name select-from-faves
|
||||||
|
SELECT food_item_id, date
|
||||||
|
FROM faves
|
||||||
|
WHERE username=:v:username
|
||||||
|
ORDER BY date DESC
|
||||||
|
|
||||||
|
|
||||||
|
-- :name insert-into-faves
|
||||||
|
INSERT INTO faves (food_item_id, username, date)
|
||||||
|
VALUES (:v:food_item_id::uuid, :v:username, current_timestamp)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING food_item_id, date
|
||||||
|
|
||||||
|
|
||||||
|
-- :name delete-from-faves
|
||||||
|
DELETE FROM faves WHERE username=:v:username AND food_item_id IN (:v*:food_item_ids::uuid) RETURNING food_item_id, date
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue