From a5ee819324d4406fff188fcd47b143cede82ff1a Mon Sep 17 00:00:00 2001 From: Bart Akeley Date: Sat, 14 Oct 2017 22:04:44 -0500 Subject: [PATCH] Initial commit --- .gitignore | 10 ++ README.md | 19 +++ project.clj | 16 +++ scripts/ddl.sql | 132 +++++++++++++++++++ src/aretherecookies_server/handler.clj | 62 +++++++++ test/aretherecookies_server/handler_test.clj | 14 ++ 6 files changed, 253 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 project.clj create mode 100644 scripts/ddl.sql create mode 100644 src/aretherecookies_server/handler.clj create mode 100644 test/aretherecookies_server/handler_test.clj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22d6a48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/target +/lib +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/README.md b/README.md new file mode 100644 index 0000000..9613e6c --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# aretherecookies-server + +the server that knows where the cookies are + +## Prerequisites + +You will need [Leiningen][] 2.0.0 or above installed. + +[leiningen]: https://github.com/technomancy/leiningen + +## Running + +To start a web server for the application, run: + + lein ring server-headless + +To test a request/response, run: + + curl http://localhost:3000/fooditems diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..02e86a0 --- /dev/null +++ b/project.clj @@ -0,0 +1,16 @@ +(defproject aretherecookies-server "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :min-lein-version "2.0.0" + :dependencies [[org.clojure/clojure "1.8.0"] + [compojure "1.5.1"] + [ring/ring-defaults "0.2.1"] + [org.clojure/java.jdbc "0.7.3"] + [org.postgresql/postgresql "42.1.4.jre6"] + [com.mchange/c3p0 "0.9.5.2"] + [ring-middleware-format "0.7.2"]] + :plugins [[lein-ring "0.9.7"]] + :ring {:handler aretherecookies-server.handler/app} + :profiles + {:dev {:dependencies [[javax.servlet/servlet-api "2.5"] + [ring/ring-mock "0.3.0"]]}}) diff --git a/scripts/ddl.sql b/scripts/ddl.sql new file mode 100644 index 0000000..f3b93e6 --- /dev/null +++ b/scripts/ddl.sql @@ -0,0 +1,132 @@ + +CREATE EXTENSION IF NOT EXISTS "postgis"; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +CREATE TYPE quantity AS ENUM ('none', 'few', 'many', 'lots'); + +CREATE TYPE category AS ENUM ('beverages', 'desserts', 'entrees', 'other'); + +create table food_items ( + id uuid PRIMARY KEY, + name VARCHAR(100), + place_id VARCHAR(50), + category category, + images VARCHAR, + thumbImage VARCHAR, + loc geography(POINT,4326) +); + +create table quantities ( + food_item_id uuid, + date timestamp (1) with time zone, + quantity quantity, + PRIMARY KEY(food_item_id, date) +); + +CREATE INDEX 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(), + 'Big John Cookies', + 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0', + 'desserts', + 'https://s-media-cache-ak0.pinimg.com/564x/e7/5f/08/e75f08b00c0bc7f2b01b3d1a636389f6.jpg,http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg', + '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(), + 'Jelly Filled Donuts', + 'ChIJ72if-Qe1RIYRCzMucGEEdBA', + 'desserts', + 'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg', + '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(), + 'Spelt Brownies', + 'ChIJG44vBQi1RIYRvWGHdYUolZY', + 'desserts', + 'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg', + '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(), + 'Oatmeal Raisin Cookies', + 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0', + 'desserts', + 'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg', + '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(), + 'Donuts with Sprinkles', + 'ChIJ72if-Qe1RIYRCzMucGEEdBA', + 'desserts', + 'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg', + '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(), + 'Powdered Donuts', + 'ChIJ72if-Qe1RIYRCzMucGEEdBA', + 'desserts', + 'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg', + '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(), + 'Snickerdoodles', + 'ChIJr3szW6a1RIYRkM7LRpnBIO0', + 'desserts', + 'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg', + '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(), + 'Pizza', + 'ChIJr3szW6a1RIYRkM7LRpnBIO0', + 'desserts', + 'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg', + '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; + +SELECT + *, + ST_AsGeoJSON(loc) as location, + ST_Distance( + loc, + ST_GeogFromText('SRID=4326;POINT(-97.7286718 30.3033267)') + ) / 1609 as distance +FROM food_items +WHERE + ST_DWithin( + loc, + ST_GeogFromText('SRID=4326;POINT(-97.7286718 30.3033267)'), + 10 * 1609 + ) +ORDER BY distance ASC; diff --git a/src/aretherecookies_server/handler.clj b/src/aretherecookies_server/handler.clj new file mode 100644 index 0000000..1f5f718 --- /dev/null +++ b/src/aretherecookies_server/handler.clj @@ -0,0 +1,62 @@ +(ns aretherecookies-server.handler + (:require [compojure.core :refer :all] + [compojure.route :as route] + [ring.middleware.defaults :refer [wrap-defaults site-defaults]] + [clojure.java.jdbc :as jdbc]) + (:import com.mchange.v2.c3p0.ComboPooledDataSource)) + +(def db-spec {:dbtype "postgresql" + :subprotocol "postgresql" + :subname "//localhost:5432/test1" + :user "bartronx7" + :password "abc123"}) + +(defn pool + [spec] + (let [cpds (doto (ComboPooledDataSource.) + (.setDriverClass (:classname spec)) + (.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec))) + (.setUser (:user spec)) + (.setPassword (:password spec)) + ;; expire excess connections after 30 minutes of inactivity: + (.setMaxIdleTimeExcessConnections (* 30 60)) + ;; expire connections after 3 hours of inactivity: + (.setMaxIdleTime (* 3 60 60)))] + {:datasource cpds})) + +(def pooled-db (delay (pool db-spec))) + +(def location-query " + SELECT + id, + name, + place_id, + category, + images, + thumbImage, + ST_AsGeoJSON(loc) as location, + ST_Distance( + loc, + ST_GeogFromText('SRID=4326;POINT(-97.7286718 30.3033267)') + ) / 1609 as distance + FROM food_items + WHERE + ST_DWithin( + loc, + ST_GeogFromText('SRID=4326;POINT(-97.7286718 30.3033267)'), + 10 * 1609 + ) + ORDER BY distance ASC;") + +(jdbc/query @pooled-db + [location-query]) + +(defn food-items-handler [request] + (jdbc/query @pooled-db + [location-query])) + +(defroutes app-routes + (GET "/fooditems" request (food-items-handler request))) + +(def app + (wrap-defaults app-routes site-defaults)) diff --git a/test/aretherecookies_server/handler_test.clj b/test/aretherecookies_server/handler_test.clj new file mode 100644 index 0000000..9fd4897 --- /dev/null +++ b/test/aretherecookies_server/handler_test.clj @@ -0,0 +1,14 @@ +(ns aretherecookies-server.handler-test + (:require [clojure.test :refer :all] + [ring.mock.request :as mock] + [aretherecookies-server.handler :refer :all])) + +(deftest test-app + (testing "main route" + (let [response (app (mock/request :get "/"))] + (is (= (:status response) 200)) + (is (= (:body response) "Hello World")))) + + (testing "not-found route" + (let [response (app (mock/request :get "/invalid"))] + (is (= (:status response) 404)))))