From 7af08e122b5673dbfabcc8747fa55f5318a11b8a Mon Sep 17 00:00:00 2001 From: Bart Akeley Date: Sat, 10 Feb 2018 11:49:54 -0600 Subject: [PATCH] new /addfooditem route handler, query *needs re-run of ddl --- scripts/ddl.sql | 30 ++++++--------------- src/aretherecookies/app.clj | 5 ++-- src/aretherecookies/auth.clj | 4 +-- src/aretherecookies/db.clj | 5 +++- src/aretherecookies/handler.clj | 46 ++++++++++++++++++++++++++------- src/aretherecookies/parsers.clj | 26 +++++++------------ src/aretherecookies/queries.sql | 23 +++++++++++++++-- 7 files changed, 84 insertions(+), 55 deletions(-) diff --git a/scripts/ddl.sql b/scripts/ddl.sql index 1af0aa5..4b994df 100644 --- a/scripts/ddl.sql +++ b/scripts/ddl.sql @@ -1,30 +1,25 @@ - CREATE EXTENSION IF NOT EXISTS "postgis"; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -CREATE TYPE QUANTITY_ENUM AS ENUM ('lots', 'many', 'few', 'none'); - -CREATE TYPE CATEGORY_ENUM AS ENUM ('beverages', 'desserts', 'entrees', 'other'); - -create table food_items ( +CREATE TYPE QUANTITY AS ENUM ('lots', 'many', 'few', 'none'); +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_ENUM, + category CATEGORY, images VARCHAR, thumbImage VARCHAR, loc geography(POINT,4326) ); - -create table quantities ( +DROP TABLE quantities CASCADE; +CREATE TABLE quantities ( food_item_id uuid, date timestamp (1) with time zone, - quantity QUANTITY_ENUM, + quantity QUANTITY, PRIMARY KEY(food_item_id, date) ); - CREATE INDEX IF NOT EXISTS food_loc_index ON food_items USING GIST ( loc ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -35,7 +30,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg', ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -46,7 +40,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg', ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -57,7 +50,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg', ST_GeogFromText('SRID=4326;POINT(-97.7419085 30.265019100000004)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -68,7 +60,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg', ST_GeogFromText('SRID=4326;POINT(-97.7532464 30.270667599999996)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -79,7 +70,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg', ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -90,7 +80,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg', ST_GeogFromText('SRID=4326;POINT(-97.742308 30.263963300000004)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -101,7 +90,6 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg', ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)') ); - INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) VALUES ( uuid_generate_v4(), @@ -112,9 +100,7 @@ INSERT INTO food_items (id, name, place_id, category, images, thumbImage, loc) 'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg', ST_GeogFromText('SRID=4326;POINT(-97.73798459999999 30.266898599999998)') ); - INSERT INTO quantities SELECT id, current_timestamp, 'many' FROM food_items; - CREATE VIEW latest_quantities AS SELECT food_item_id, quantity, date from quantities q1 where date = ( SELECT max(date) FROM quantities q2 WHERE q1.food_item_id=q2.food_item_id ); \ No newline at end of file diff --git a/src/aretherecookies/app.clj b/src/aretherecookies/app.clj index b5e087e..fcb4208 100644 --- a/src/aretherecookies/app.clj +++ b/src/aretherecookies/app.clj @@ -1,6 +1,6 @@ (ns aretherecookies.app (:gen-class) - (:require [aretherecookies.handler :refer [food-items-handler quantity-handler]] + (:require [aretherecookies.handler :refer [food-items-handler quantity-handler add-food-item-handler]] [aretherecookies.auth :refer [auth-backend]] [environ.core :refer [env]] [compojure.handler :refer [api]] @@ -16,7 +16,8 @@ (GET "/" [] (str {:csrf-token *anti-forgery-token*})) (POST "/test" [] "ok") (POST "/fooditems" [] food-items-handler) - (POST "/quantity" [] quantity-handler)) + (POST "/quantity" [] quantity-handler) + (POST "/addfooditem" [] add-food-item-handler)) (def app-config (assoc-in api-defaults [:security :anti-forgery] false)) diff --git a/src/aretherecookies/auth.clj b/src/aretherecookies/auth.clj index 895e2c9..b1eb0a7 100644 --- a/src/aretherecookies/auth.clj +++ b/src/aretherecookies/auth.clj @@ -7,13 +7,13 @@ (defn facebook-me [token] (client/get (str "https://graph.facebook.com/me?access_token=" token) {:accept :json})) -(defn facebook-me-ok [token] +(defn facebook-me-ok? [token] (= 200 (:status (facebook-me token))) (swap! tokens assoc (keyword token) true)) (defn facebook-token-auth [_ token] (cond (get @tokens (keyword token)) token - (facebook-me-ok token) token)) + (facebook-me-ok? token) token)) (def auth-backend (backends/token {:token-name "facebook-token" :authfn facebook-token-auth})) \ No newline at end of file diff --git a/src/aretherecookies/db.clj b/src/aretherecookies/db.clj index 45d1a06..4442ec5 100644 --- a/src/aretherecookies/db.clj +++ b/src/aretherecookies/db.clj @@ -63,7 +63,10 @@ :order (get-orderby filter)})) (defn insert-quantity [{:keys [:foodItemId :quantity]}] - (insert-quantity-query @pooled-db {:food_item_id (wrap-in-quotes foodItemId) :quantity (wrap-in-quotes quantity)})) + (insert-quantity-query @pooled-db {:food_item_id foodItemId :quantity quantity})) (defn select-latest-quantity [{:keys [:foodItemId]}] (select-latest-quantity-query @pooled-db {:food_item_id (wrap-in-quotes foodItemId)})) + +(defn create-food-item [{:keys [:name :placeId :category :latitude :longitude]}] + (insert-food-item @pooled-db {:name name :placeId placeId :category category :longitude longitude :latitude latitude})) diff --git a/src/aretherecookies/handler.clj b/src/aretherecookies/handler.clj index 2fd89db..4a5896d 100644 --- a/src/aretherecookies/handler.clj +++ b/src/aretherecookies/handler.clj @@ -1,24 +1,52 @@ (ns aretherecookies.handler - (:require [aretherecookies.db :refer [query-food-items insert-quantity]] - [aretherecookies.parsers :refer [food-items-to-json - parse-special-types]] + (:require [aretherecookies.db :refer [query-food-items + insert-quantity + create-food-item]] + [aretherecookies.parsers :refer [parse-special-types parse-location]] [clojure.data.json :as json] [clojure.string :as str] [buddy.auth :refer [authenticated? throw-unauthorized]])) +(defn safe-json + "converts hashmaps into a JSON string suitable for a network response" + [data] + (json/write-str data :value-fn parse-special-types)) + +(defn bad-request + "returns a HTTP 404 error with the supplied body" + [body] + {:status 400 :body (safe-json body)}) + (defn food-items-handler [req] - (println "req ---->" (:body req)) + (println "/fooditems ---->" (:body req)) (let [{body :body} req] - (json/write-str + (safe-json (hash-map - :filter (get body :filter) - :fooditems (food-items-to-json (query-food-items body))) - :value-fn parse-special-types))) + :filter (:filter body) + :fooditems (map parse-location (query-food-items body)))))) (defn quantity-handler [req] (let [{{foodItemId :foodItemId quantity :quantity} :body} req] (if-not (authenticated? req) (throw-unauthorized)) - (println "req ---->" foodItemId quantity) + (println "/quantity ---->" foodItemId quantity) (json/write-str (insert-quantity {:foodItemId foodItemId :quantity quantity}) :value-fn parse-special-types))) + +(def required-keys [:name :placeId :latitude :longitude :category :quantity]) +(defn get-missing-keys + [foodItem] + (filter #(get foodItem %) required-keys)) + +(defn add-food-item-handler + "validate food item fields from request and insert into database returning newly created item" + [req] + (println "/addfooditem ---->" (:body req)) + (if-not (authenticated? req) (throw-unauthorized)) + (let [food-item (:body req) missing-keys (get-missing-keys food-item)] + (if (< (count missing-keys) 0) + (bad-request {"missingkeys" missing-keys}) + (as-> + (create-food-item food-item) % + (map parse-location %) + (safe-json %))))) diff --git a/src/aretherecookies/parsers.clj b/src/aretherecookies/parsers.clj index bab40c2..2db7e81 100644 --- a/src/aretherecookies/parsers.clj +++ b/src/aretherecookies/parsers.clj @@ -12,24 +12,16 @@ (defn get-coords [item] (-> - item - (:location) - json/read-str - (:coordinates))) - -(defn build-lat-lng [[lng lat]] - (hash-map :longitude lng :latitude lat)) + (:location item) + json/read-str + (:coordinates))) (defn build-latlng [item] - (let [[lng lat] (get-coords item)] - (hash-map :longitude lng :latitude lat))) + (let [[longitude latitude] (get-coords item)] + (hash-map :longitude longitude :latitude latitude))) -(defn parse-location [item] +(defn parse-location [food-item] (-> - item - build-latlng - (merge item) - (dissoc :location))) - -(defn food-items-to-json [query-result] - (map parse-location query-result)) \ No newline at end of file + (build-latlng food-item) + (merge food-item) + (dissoc :location))) diff --git a/src/aretherecookies/queries.sql b/src/aretherecookies/queries.sql index 83b2464..60db918 100644 --- a/src/aretherecookies/queries.sql +++ b/src/aretherecookies/queries.sql @@ -36,11 +36,30 @@ ORDER BY quantity DESC -- :name insert-quantity-query INSERT INTO quantities (food_item_id, quantity, date) -VALUES (:i:food_item_id, :i:quantity, current_timestamp) +VALUES (:v:food_item_id::uuid, :v:quantity::quantity, current_timestamp) RETURNING * -- :name select-latest-quantity-query SELECT food_item_id, quantity, date AS updated FROM quantities WHERE food_item_id=:i:food_item_id -ORDER BY date DESC LIMIT 1 \ No newline at end of file +ORDER BY date DESC LIMIT 1 + +-- :name insert-food-item +INSERT INTO food_items (id, name, place_id, category, loc) + VALUES ( + uuid_generate_v4(), + :v:name, + :v:placeId, + :v:category::category, + ST_SetSRID(ST_Point(:longitude, :latitude), 4326)::geography + ) +RETURNING + id AS id, + name AS name, + place_id AS place_id, + category AS category, + images AS images, + thumbImage AS thumbImage, + ST_AsGeoJSON(loc) AS location, + ST_Distance(loc, ST_SetSRID(ST_Point(:longitude, :latitude), 4326)::geography) / 1609 AS distance