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;
|
||||
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,
|
||||
PRIMARY KEY(food_item_id, date)
|
||||
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 );
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
@ -35,11 +39,9 @@
|
|||
[& [port]]
|
||||
(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-multipart-params
|
||||
(wrap-json-body {:keywords? true})
|
||||
(jetty/run-jetty {:port port :join? false}))))
|
||||
(api #'app)
|
||||
(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}))))
|
||||
|
|
|
|||
|
|
@ -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)}))
|
||||
|
|
|
|||
|
|
@ -77,16 +77,16 @@
|
|||
|
||||
(defn create-food-item [{:keys [:name :placeId :category :quantity :latitude :longitude]}]
|
||||
(let [food-item (first (insert-food-item
|
||||
@pooled-db
|
||||
{:name name
|
||||
:placeId placeId
|
||||
:category category
|
||||
:longitude longitude
|
||||
:latitude latitude}))
|
||||
@pooled-db
|
||||
{:name name
|
||||
:placeId placeId
|
||||
:category category
|
||||
:longitude longitude
|
||||
:latitude latitude}))
|
||||
quantity (first (insert-quantity {:foodItemId (:id food-item) :quantity quantity}))]
|
||||
(merge
|
||||
food-item
|
||||
(rename-keys (select-keys quantity [:date :quantity]) {:date :lastupdated}))))
|
||||
food-item
|
||||
(rename-keys (select-keys quantity [:date :quantity]) {:date :lastupdated}))))
|
||||
|
||||
(defn get-images
|
||||
"query database for a list of images for a food item id"
|
||||
|
|
@ -96,4 +96,19 @@
|
|||
(defn add-image
|
||||
"update the list of images for given food item id"
|
||||
[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
|
||||
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
|
||||
|
|
@ -45,9 +48,9 @@
|
|||
(hash-map
|
||||
:filter (:filter body)
|
||||
:fooditems (->>
|
||||
(query-food-items body)
|
||||
(map parse-location)
|
||||
(map (update-key-to-s3 :thumbimage)))))))
|
||||
(query-food-items body)
|
||||
(map parse-location)
|
||||
(map (update-key-to-s3 :thumbimage)))))))
|
||||
|
||||
|
||||
(defn quantity-handler
|
||||
|
|
@ -60,7 +63,7 @@
|
|||
(insert-quantity {:foodItemId foodItemId :quantity quantity})
|
||||
:value-fn parse-special-types)))
|
||||
|
||||
(defn key-found?
|
||||
(defn key-found?
|
||||
[obj key]
|
||||
(not (str/blank? (str (get obj key)))))
|
||||
|
||||
|
|
@ -68,8 +71,8 @@
|
|||
"returns a list of required keys which were not found on the given food item"
|
||||
[foodItem]
|
||||
(remove
|
||||
#(key-found? foodItem %)
|
||||
[:name :placeId :latitude :longitude :category :quantity]))
|
||||
#(key-found? foodItem %)
|
||||
[:name :placeId :latitude :longitude :category :quantity]))
|
||||
|
||||
|
||||
(defn add-food-item-handler
|
||||
|
|
@ -81,7 +84,7 @@
|
|||
(if (> (count missing-keys) 0)
|
||||
(bad-request {"missingkeys" missing-keys})
|
||||
(as->
|
||||
(create-food-item food-item) %
|
||||
(create-food-item food-item) %
|
||||
(parse-location %)
|
||||
(safe-json %)))))
|
||||
|
||||
|
|
@ -91,15 +94,15 @@
|
|||
[req]
|
||||
(let [foodItemId (get-in req [:params :foodItemId])]
|
||||
(->>
|
||||
(get-images foodItemId)
|
||||
(map #(update % :url build-s3-url))
|
||||
safe-json)))
|
||||
(get-images foodItemId)
|
||||
(map #(update % :url build-s3-url))
|
||||
safe-json)))
|
||||
|
||||
|
||||
(defn add-image-handler
|
||||
"given foodItemId from route and photo from multipart form post uploads image into s3 and adds URL into food item record"
|
||||
[req]
|
||||
;(if-not (authenticated? req) (throw-unauthorized))
|
||||
; (if-not (authenticated? req) (throw-unauthorized))
|
||||
(let [username (get-in req [:params :username] "Bart Akeley")
|
||||
foodItemId (get-in req [:params :foodItemId])
|
||||
image (get-in req [:params :photo :tempfile])
|
||||
|
|
@ -107,9 +110,38 @@
|
|||
(println "/addimage ---->" img-name)
|
||||
(put-s3 img-name image)
|
||||
(->>
|
||||
(add-image {:food_item_id foodItemId
|
||||
:filename img-name
|
||||
:username username})
|
||||
first
|
||||
(update-key-to-s3 :url)
|
||||
safe-json)))
|
||||
(add-image {:food_item_id foodItemId
|
||||
:filename img-name
|
||||
:username username})
|
||||
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)))
|
||||
|
|
|
|||
|
|
@ -88,4 +88,22 @@ RETURNING
|
|||
filename as url,
|
||||
date,
|
||||
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