support for favorites

This commit is contained in:
Bart Akeley 2020-03-29 14:36:37 -05:00
parent 68c0cf7bf1
commit f9af5e2be9
6 changed files with 133 additions and 58 deletions

View file

@ -11,9 +11,9 @@ CREATE TYPE CATEGORY AS ENUM ('beverages', 'desserts', 'entrees', 'other');
DROP TABLE IF EXISTS food_items CASCADE;
CREATE TABLE food_items (
id uuid PRIMARY KEY,
name VARCHAR(100),
place_id VARCHAR(50),
category CATEGORY,
name VARCHAR(100) NOT NULL,
place_id VARCHAR(50) NOT NULL,
category CATEGORY NOT NULL,
images VARCHAR,
loc geography(POINT,4326)
);
@ -22,7 +22,7 @@ DROP TABLE IF EXISTS images CASCADE;
CREATE TABLE images (
filename VARCHAR(200) PRIMARY KEY,
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
);
@ -30,10 +30,17 @@ DROP TABLE IF EXISTS quantities CASCADE;
CREATE TABLE quantities (
food_item_id uuid REFERENCES food_items (id) ON DELETE CASCADE,
date timestamp (1) with time zone,
quantity QUANTITY,
quantity QUANTITY NOT NULL,
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 );

View file

@ -4,18 +4,19 @@
quantity-handler
add-food-item-handler
add-image-handler
get-images-handler]]
[aretherecookies.auth :refer [facebook-auth-backend
google-auth-backend]]
get-images-handler
faves-get-handler
faves-put-handler
faves-delete-handler]]
[aretherecookies.auth :refer [auth0-auth-backend]]
[environ.core :refer [env]]
[compojure.handler :refer [api]]
[compojure.core :refer :all]
[compojure.core :refer [defroutes GET POST PUT DELETE]]
[ring.adapter.jetty :as jetty]
[ring.middleware.anti-forgery :refer :all]
[ring.middleware.json :refer [wrap-json-body]]
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
[ring.middleware.defaults :refer [wrap-defaults api-defaults]]
[buddy.auth :refer [throw-unauthorized]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]))
(defroutes app-routes
@ -25,7 +26,10 @@
(POST "/quantity" [] quantity-handler)
(POST "/addfooditem" [] add-food-item-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))
@ -36,10 +40,8 @@
(let [port (Integer. (or port (env :port) 3000))]
(->
(api #'app)
(wrap-authorization facebook-auth-backend)
(wrap-authorization google-auth-backend)
(wrap-authentication facebook-auth-backend)
(wrap-authentication google-auth-backend)
(wrap-authentication auth0-auth-backend)
(wrap-authorization auth0-auth-backend)
wrap-multipart-params
(wrap-json-body {:keywords? true})
(jetty/run-jetty {:port port :join? false}))))

View file

@ -2,16 +2,20 @@
(:require [buddy.auth.backends :as backends]
[clj-http.client :as client]))
; TODO implement this as LRU cache to eliminate memory leaking
(def tokens (atom {}))
(def facebook-me-url "https://graph.facebook.com/me?access_token=")
(def google-userinfo-url "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=")
(def auth0-userinfo-url "https://aretherecookies.auth0.com/userinfo")
(defn verify-token
"perform rest requesting using token and return boolean if successful"
[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
"swap the new token into our cache map atom and return the token"
@ -21,7 +25,7 @@
(defn verify-and-cache-token
"if a REST request is successful against url using token, cache the token and return it"
[token url]
(if (verify-token token url) (cache-token token)))
(if (verify-token token url) (cache-token token) nil))
(defn url-backend
"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))))
(def facebook-auth-backend
(backends/token {:token-name "facebook-token" :authfn (url-backend facebook-me-url)}))
(def google-auth-backend
(backends/token {:token-name "google-token" :authfn (url-backend google-userinfo-url)}))
(def auth0-auth-backend
(backends/token {:token-name "auth0-token" :authfn (url-backend auth0-userinfo-url)}))

View file

@ -97,3 +97,18 @@
"update the list of images for given food item id"
[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}))

View file

@ -3,7 +3,10 @@
insert-quantity
create-food-item
get-images
add-image]]
add-image
get-faves
add-faves
delete-faves]]
[aretherecookies.parsers :refer [parse-special-types
parse-location]]
[aretherecookies.aws :refer [put-s3
@ -113,3 +116,32 @@
first
(update-key-to-s3 :url)
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)))

View file

@ -89,3 +89,21 @@ RETURNING
date,
username,
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