Add database stuff
This commit is contained in:
parent
397d8a5cee
commit
22971d801f
|
@ -6,7 +6,10 @@
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[expound.alpha :as expound]
|
[expound.alpha :as expound]
|
||||||
[mount.core :as mount]
|
[mount.core :as mount]
|
||||||
[shapey-shifty.core :refer [start-app]]))
|
[shapey-shifty.core :refer [start-app]]
|
||||||
|
[shapey-shifty.db.core]
|
||||||
|
[conman.core :as conman]
|
||||||
|
[luminus-migrations.core :as migrations]))
|
||||||
|
|
||||||
(alter-var-root #'s/*explain-out* (constantly expound/printer))
|
(alter-var-root #'s/*explain-out* (constantly expound/printer))
|
||||||
|
|
||||||
|
@ -29,4 +32,32 @@
|
||||||
(stop)
|
(stop)
|
||||||
(start))
|
(start))
|
||||||
|
|
||||||
|
(defn restart-db
|
||||||
|
"Restarts database."
|
||||||
|
[]
|
||||||
|
(mount/stop #'shapey-shifty.db.core/*db*)
|
||||||
|
(mount/start #'shapey-shifty.db.core/*db*)
|
||||||
|
(binding [*ns* 'shapey-shifty.db.core]
|
||||||
|
(conman/bind-connection shapey-shifty.db.core/*db* "sql/queries.sql")))
|
||||||
|
|
||||||
|
(defn reset-db
|
||||||
|
"Resets database."
|
||||||
|
[]
|
||||||
|
(migrations/migrate ["reset"] (select-keys env [:database-url])))
|
||||||
|
|
||||||
|
(defn migrate
|
||||||
|
"Migrates database up for all outstanding migrations."
|
||||||
|
[]
|
||||||
|
(migrations/migrate ["migrate"] (select-keys env [:database-url])))
|
||||||
|
|
||||||
|
(defn rollback
|
||||||
|
"Rollback latest database migration."
|
||||||
|
[]
|
||||||
|
(migrations/migrate ["rollback"] (select-keys env [:database-url])))
|
||||||
|
|
||||||
|
(defn create-migration
|
||||||
|
"Create a new up and down migration file with a generated timestamp and `name`."
|
||||||
|
[name]
|
||||||
|
(migrations/create name (select-keys env [:database-url])))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
</appender>
|
</appender>
|
||||||
<logger name="org.apache.http" level="warn" />
|
<logger name="org.apache.http" level="warn" />
|
||||||
<logger name="org.xnio.nio" level="warn" />
|
<logger name="org.xnio.nio" level="warn" />
|
||||||
|
<logger name="com.zaxxer.hikari" level="warn" />
|
||||||
<logger name="org.eclipse.jetty" level="warn" />
|
<logger name="org.eclipse.jetty" level="warn" />
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
</appender>
|
</appender>
|
||||||
<logger name="org.apache.http" level="warn" />
|
<logger name="org.apache.http" level="warn" />
|
||||||
<logger name="org.xnio.nio" level="warn" />
|
<logger name="org.xnio.nio" level="warn" />
|
||||||
|
<logger name="com.zaxxer.hikari" level="warn" />
|
||||||
<logger name="org.eclipse.jetty" level="warn" />
|
<logger name="org.eclipse.jetty" level="warn" />
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="FILE" />
|
<appender-ref ref="FILE" />
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
</appender>
|
</appender>
|
||||||
<logger name="org.apache.http" level="warn" />
|
<logger name="org.apache.http" level="warn" />
|
||||||
<logger name="org.xnio.nio" level="warn" />
|
<logger name="org.xnio.nio" level="warn" />
|
||||||
|
<logger name="com.zaxxer.hikari" level="warn" />
|
||||||
<logger name="org.eclipse.jetty" level="warn" />
|
<logger name="org.eclipse.jetty" level="warn" />
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
:dependencies [[ch.qos.logback/logback-classic "1.2.3"]
|
:dependencies [[ch.qos.logback/logback-classic "1.2.3"]
|
||||||
[cheshire "5.9.0"]
|
[cheshire "5.9.0"]
|
||||||
[clojure.java-time "0.3.2"]
|
[clojure.java-time "0.3.2"]
|
||||||
|
[conman "0.8.4"]
|
||||||
[cprop "0.1.15"]
|
[cprop "0.1.15"]
|
||||||
[expound "0.8.3"]
|
[expound "0.8.3"]
|
||||||
[funcool/struct "1.4.0"]
|
[funcool/struct "1.4.0"]
|
||||||
[luminus-jetty "0.1.7"]
|
[luminus-jetty "0.1.7"]
|
||||||
|
[luminus-migrations "0.6.6"]
|
||||||
[luminus-transit "0.1.2"]
|
[luminus-transit "0.1.2"]
|
||||||
[luminus/ring-ttl-session "0.3.3"]
|
[luminus/ring-ttl-session "0.3.3"]
|
||||||
[markdown-clj "1.10.1"]
|
[markdown-clj "1.10.1"]
|
||||||
|
@ -21,6 +23,7 @@
|
||||||
[org.clojure/clojure "1.10.1"]
|
[org.clojure/clojure "1.10.1"]
|
||||||
[org.clojure/tools.cli "0.4.2"]
|
[org.clojure/tools.cli "0.4.2"]
|
||||||
[org.clojure/tools.logging "0.5.0"]
|
[org.clojure/tools.logging "0.5.0"]
|
||||||
|
[org.postgresql/postgresql "42.2.9"]
|
||||||
[org.webjars.npm/bulma "0.8.0"]
|
[org.webjars.npm/bulma "0.8.0"]
|
||||||
[org.webjars.npm/material-icons "0.3.1"]
|
[org.webjars.npm/material-icons "0.3.1"]
|
||||||
[org.webjars/webjars-locator "0.38"]
|
[org.webjars/webjars-locator "0.38"]
|
||||||
|
|
|
@ -91,6 +91,21 @@ the `env/dev/clj/` source path.
|
||||||
|
|
||||||
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/middleware.md">learn more about middleware »</a>
|
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/middleware.md">learn more about middleware »</a>
|
||||||
|
|
||||||
|
<div class="bs-callout bs-callout-danger">
|
||||||
|
|
||||||
|
#### Database configuration is required
|
||||||
|
|
||||||
|
If you haven't already, then please follow the steps below to configure your database connection and run the necessary migrations.
|
||||||
|
|
||||||
|
* Create the database for your application.
|
||||||
|
* Update the connection URL in the `dev-config.edn` and `test-config.edn` files with your database name and login credentials.
|
||||||
|
* Run `lein run migrate` in the root of the project to create the tables.
|
||||||
|
* Let `mount` know to start the database connection by `require`-ing `shapey-shifty.db.core` in some other namespace.
|
||||||
|
* Restart the application.
|
||||||
|
|
||||||
|
<a class="btn btn-primary" href="http://www.luminusweb.net/docs/database.md">learn more about database access »</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE users;
|
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE users
|
||||||
|
(id VARCHAR(20) PRIMARY KEY,
|
||||||
|
first_name VARCHAR(30),
|
||||||
|
last_name VARCHAR(30),
|
||||||
|
email VARCHAR(30),
|
||||||
|
admin BOOLEAN,
|
||||||
|
last_login TIMESTAMP,
|
||||||
|
is_active BOOLEAN,
|
||||||
|
pass VARCHAR(300));
|
|
@ -0,0 +1,21 @@
|
||||||
|
-- :name create-user! :! :n
|
||||||
|
-- :doc creates a new user record
|
||||||
|
INSERT INTO users
|
||||||
|
(id, first_name, last_name, email, pass)
|
||||||
|
VALUES (:id, :first_name, :last_name, :email, :pass)
|
||||||
|
|
||||||
|
-- :name update-user! :! :n
|
||||||
|
-- :doc updates an existing user record
|
||||||
|
UPDATE users
|
||||||
|
SET first_name = :first_name, last_name = :last_name, email = :email
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name get-user :? :1
|
||||||
|
-- :doc retrieves a user record given the id
|
||||||
|
SELECT * FROM users
|
||||||
|
WHERE id = :id
|
||||||
|
|
||||||
|
-- :name delete-user! :! :n
|
||||||
|
-- :doc deletes a user record given the id
|
||||||
|
DELETE FROM users
|
||||||
|
WHERE id = :id
|
|
@ -3,6 +3,7 @@
|
||||||
[shapey-shifty.handler :as handler]
|
[shapey-shifty.handler :as handler]
|
||||||
[shapey-shifty.nrepl :as nrepl]
|
[shapey-shifty.nrepl :as nrepl]
|
||||||
[luminus.http-server :as http]
|
[luminus.http-server :as http]
|
||||||
|
[luminus-migrations.core :as migrations]
|
||||||
[shapey-shifty.config :refer [env]]
|
[shapey-shifty.config :refer [env]]
|
||||||
[clojure.tools.cli :refer [parse-opts]]
|
[clojure.tools.cli :refer [parse-opts]]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
|
@ -55,4 +56,20 @@
|
||||||
(.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)))
|
(.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)))
|
||||||
|
|
||||||
(defn -main [& args]
|
(defn -main [& args]
|
||||||
(start-app args))
|
(mount/start #'shapey-shifty.config/env)
|
||||||
|
(cond
|
||||||
|
(nil? (:database-url env))
|
||||||
|
(do
|
||||||
|
(log/error "Database configuration not found, :database-url environment variable must be set before running")
|
||||||
|
(System/exit 1))
|
||||||
|
(some #{"init"} args)
|
||||||
|
(do
|
||||||
|
(migrations/init (select-keys env [:database-url :init-script]))
|
||||||
|
(System/exit 0))
|
||||||
|
(migrations/migration? args)
|
||||||
|
(do
|
||||||
|
(migrations/migrate args (select-keys env [:database-url]))
|
||||||
|
(System/exit 0))
|
||||||
|
:else
|
||||||
|
(start-app args)))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
(ns shapey-shifty.db.core
|
||||||
|
(:require
|
||||||
|
[cheshire.core :refer [generate-string parse-string]]
|
||||||
|
[clojure.java.jdbc :as jdbc]
|
||||||
|
[clojure.tools.logging :as log]
|
||||||
|
[conman.core :as conman]
|
||||||
|
[java-time :as jt]
|
||||||
|
[java-time.pre-java8]
|
||||||
|
[shapey-shifty.config :refer [env]]
|
||||||
|
[mount.core :refer [defstate]])
|
||||||
|
(:import org.postgresql.util.PGobject
|
||||||
|
java.sql.Array
|
||||||
|
clojure.lang.IPersistentMap
|
||||||
|
clojure.lang.IPersistentVector
|
||||||
|
[java.sql
|
||||||
|
BatchUpdateException
|
||||||
|
PreparedStatement]))
|
||||||
|
(defstate ^:dynamic *db*
|
||||||
|
:start (if-let [jdbc-url (env :database-url)]
|
||||||
|
(conman/connect! {:jdbc-url jdbc-url})
|
||||||
|
(do
|
||||||
|
(log/warn "database connection URL was not found, please set :database-url in your config, e.g: dev-config.edn")
|
||||||
|
*db*))
|
||||||
|
:stop (conman/disconnect! *db*))
|
||||||
|
|
||||||
|
(conman/bind-connection *db* "sql/queries.sql")
|
||||||
|
|
||||||
|
|
||||||
|
(extend-protocol jdbc/IResultSetReadColumn
|
||||||
|
java.sql.Timestamp
|
||||||
|
(result-set-read-column [v _2 _3]
|
||||||
|
(.toLocalDateTime v))
|
||||||
|
java.sql.Date
|
||||||
|
(result-set-read-column [v _2 _3]
|
||||||
|
(.toLocalDate v))
|
||||||
|
java.sql.Time
|
||||||
|
(result-set-read-column [v _2 _3]
|
||||||
|
(.toLocalTime v))
|
||||||
|
Array
|
||||||
|
(result-set-read-column [v _ _] (vec (.getArray v)))
|
||||||
|
PGobject
|
||||||
|
(result-set-read-column [pgobj _metadata _index]
|
||||||
|
(let [type (.getType pgobj)
|
||||||
|
value (.getValue pgobj)]
|
||||||
|
(case type
|
||||||
|
"json" (parse-string value true)
|
||||||
|
"jsonb" (parse-string value true)
|
||||||
|
"citext" (str value)
|
||||||
|
value))))
|
||||||
|
|
||||||
|
(defn to-pg-json [value]
|
||||||
|
(doto (PGobject.)
|
||||||
|
(.setType "jsonb")
|
||||||
|
(.setValue (generate-string value))))
|
||||||
|
|
||||||
|
(extend-type clojure.lang.IPersistentVector
|
||||||
|
jdbc/ISQLParameter
|
||||||
|
(set-parameter [v ^java.sql.PreparedStatement stmt ^long idx]
|
||||||
|
(let [conn (.getConnection stmt)
|
||||||
|
meta (.getParameterMetaData stmt)
|
||||||
|
type-name (.getParameterTypeName meta idx)]
|
||||||
|
(if-let [elem-type (when (= (first type-name) \_) (apply str (rest type-name)))]
|
||||||
|
(.setObject stmt idx (.createArrayOf conn elem-type (to-array v)))
|
||||||
|
(.setObject stmt idx (to-pg-json v))))))
|
||||||
|
|
||||||
|
(extend-protocol jdbc/ISQLValue
|
||||||
|
java.util.Date
|
||||||
|
(sql-value [v]
|
||||||
|
(java.sql.Timestamp. (.getTime v)))
|
||||||
|
java.time.LocalTime
|
||||||
|
(sql-value [v]
|
||||||
|
(jt/sql-time v))
|
||||||
|
java.time.LocalDate
|
||||||
|
(sql-value [v]
|
||||||
|
(jt/sql-date v))
|
||||||
|
java.time.LocalDateTime
|
||||||
|
(sql-value [v]
|
||||||
|
(jt/sql-timestamp v))
|
||||||
|
java.time.ZonedDateTime
|
||||||
|
(sql-value [v]
|
||||||
|
(jt/sql-timestamp v))
|
||||||
|
IPersistentMap
|
||||||
|
(sql-value [value] (to-pg-json value))
|
||||||
|
IPersistentVector
|
||||||
|
(sql-value [value] (to-pg-json value)))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns shapey-shifty.routes.home
|
(ns shapey-shifty.routes.home
|
||||||
(:require
|
(:require
|
||||||
[shapey-shifty.layout :as layout]
|
[shapey-shifty.layout :as layout]
|
||||||
|
[shapey-shifty.db.core :as db]
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
[shapey-shifty.middleware :as middleware]
|
[shapey-shifty.middleware :as middleware]
|
||||||
[ring.util.response]
|
[ring.util.response]
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
(ns shapey-shifty.test.db.core
|
||||||
|
(:require
|
||||||
|
[shapey-shifty.db.core :refer [*db*] :as db]
|
||||||
|
[java-time.pre-java8]
|
||||||
|
[luminus-migrations.core :as migrations]
|
||||||
|
[clojure.test :refer :all]
|
||||||
|
[clojure.java.jdbc :as jdbc]
|
||||||
|
[shapey-shifty.config :refer [env]]
|
||||||
|
[mount.core :as mount]))
|
||||||
|
|
||||||
|
(use-fixtures
|
||||||
|
:once
|
||||||
|
(fn [f]
|
||||||
|
(mount/start
|
||||||
|
#'shapey-shifty.config/env
|
||||||
|
#'shapey-shifty.db.core/*db*)
|
||||||
|
(migrations/migrate ["migrate"] (select-keys env [:database-url]))
|
||||||
|
(f)))
|
||||||
|
|
||||||
|
(deftest test-users
|
||||||
|
(jdbc/with-db-transaction [t-conn *db*]
|
||||||
|
(jdbc/db-set-rollback-only! t-conn)
|
||||||
|
(is (= 1 (db/create-user!
|
||||||
|
t-conn
|
||||||
|
{:id "1"
|
||||||
|
:first_name "Sam"
|
||||||
|
:last_name "Smith"
|
||||||
|
:email "sam.smith@example.com"
|
||||||
|
:pass "pass"})))
|
||||||
|
(is (= {:id "1"
|
||||||
|
:first_name "Sam"
|
||||||
|
:last_name "Smith"
|
||||||
|
:email "sam.smith@example.com"
|
||||||
|
:pass "pass"
|
||||||
|
:admin nil
|
||||||
|
:last_login nil
|
||||||
|
:is_active nil}
|
||||||
|
(db/get-user t-conn {:id "1"})))))
|
Loading…
Reference in New Issue