diff --git a/project.clj b/project.clj index ddac984e104eb5adfa55e639bd4d98a9749ecf75..66d8abedfde41e0df795021ac633c487b375ee74 100644 --- a/project.clj +++ b/project.clj @@ -18,6 +18,9 @@ [environ "0.5.0"] ; easy environment management [expectations "2.0.12"] ; unit tests [korma "0.4.0"] ; SQL lib + [org.clojure/java.jdbc "0.3.6"] ; basic jdbc access from clojure + [org.liquibase/liquibase-core "3.3.2"] ; migration management (Java lib) + [org.yaml/snakeyaml "1.14"] ; YAML parser (required by liquibase) [log4j/log4j "1.2.17" :exclusions [javax.mail/mail javax.jms/jms @@ -34,6 +37,7 @@ [lein-marginalia "LATEST"] ; generate documentation with 'lein marg' [lein-ring "0.8.10"] ; start the HTTP server with 'lein ring server' ] + :java-source-paths ["src/java"] :main ^:skip-aot metabase.core :target-path "target/%s" :ring {:handler metabase.core/app} diff --git a/resources/migrations/liquibase.json b/resources/migrations/liquibase.json new file mode 100644 index 0000000000000000000000000000000000000000..9b888d6bd622dd863b5c043d7156e83a53eac5c5 --- /dev/null +++ b/resources/migrations/liquibase.json @@ -0,0 +1,5 @@ +{ + "databaseChangeLog": [ + {"include": {"file": "migrations/liquibase_initial.json"}} + ] +} \ No newline at end of file diff --git a/resources/migrations/liquibase_initial.json b/resources/migrations/liquibase_initial.json new file mode 100644 index 0000000000000000000000000000000000000000..5dadff9beee740fab98ace673cff0d2d484d0939 --- /dev/null +++ b/resources/migrations/liquibase_initial.json @@ -0,0 +1,152 @@ +{ + "databaseChangeLog": [ + { + "changeSet": { + "id": "1", + "author": "agilliland", + "changes": [ + { + "createTable": { + "tableName": "CORE_ORGANIZATION", + "columns": [ + { + "column": { + "name": "ID", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "SLUG", + "type": "varchar(254)", + "constraints": { + "unique": true, + "nullable": false + } + } + }, + { + "column": { + "name": "NAME", + "type": "varchar(254)", + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "DESCRIPTION", + "type": "varchar(254)" + } + }, + { + "column": { + "name": "LOGO_URL", + "type": "varchar(254)" + } + }, + { + "column": { + "name": "INHERITS", + "type": "boolean", + "defaultValue": false, + "constraints": {"nullable": false} + } + } + ] + } + }, + { + "createTable": { + "tableName": "CORE_USER", + "columns": [ + { + "column": { + "name": "ID", + "type": "int", + "autoIncrement": true, + "constraints": { + "primaryKey": true, + "nullable": false + } + } + }, + { + "column": { + "name": "EMAIL", + "type": "varchar(254)", + "constraints": { + "unique": true, + "nullable": false + } + } + }, + { + "column": { + "name": "FIRST_NAME", + "type": "varchar(254)", + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "LAST_NAME", + "type": "varchar(254)" + } + }, + { + "column": { + "name": "PASSWORD", + "type": "varchar(254)", + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "DATE_JOINED", + "type": "DATETIME", + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "LAST_LOGIN", + "type": "DATETIME" + } + }, + { + "column": { + "name": "IS_STAFF", + "type": "boolean", + "defaultValue": false, + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "IS_SUPERUSER", + "type": "boolean", + "defaultValue": false, + "constraints": {"nullable": false} + } + }, + { + "column": { + "name": "IS_ACTIVE", + "type": "boolean", + "defaultValue": true, + "constraints": {"nullable": false} + } + } + ] + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/java/com/metabase/corvus/migrations/LiquibaseMigrations.java b/src/java/com/metabase/corvus/migrations/LiquibaseMigrations.java new file mode 100644 index 0000000000000000000000000000000000000000..35269baf490ffbe099e9918618776c5e6d290095 --- /dev/null +++ b/src/java/com/metabase/corvus/migrations/LiquibaseMigrations.java @@ -0,0 +1,55 @@ +package com.metabase.corvus.migrations; + +import java.sql.SQLException; +import liquibase.Liquibase; +import liquibase.database.DatabaseFactory; +import liquibase.database.Database; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.resource.ClassLoaderResourceAccessor; + + +public class LiquibaseMigrations { + + private static final String LIQUIBASE_CHANGELOG = "migrations/liquibase.json"; + + + public static final void setupDatabase(java.sql.Connection dbConnection) throws Exception { + try { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dbConnection)); + Liquibase liquibase = new Liquibase(LIQUIBASE_CHANGELOG, new ClassLoaderResourceAccessor(), database); + liquibase.update(""); + } catch (Exception e) { + throw new DatabaseException(e); + } finally { + if (dbConnection != null) { + try { + dbConnection.rollback(); + dbConnection.close(); + } catch (SQLException e){ + //nothing to do + } + } + } + } + + public static final void teardownDatabase(java.sql.Connection dbConnection) throws Exception { + try { + Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dbConnection)); + Liquibase liquibase = new Liquibase(LIQUIBASE_CHANGELOG, new ClassLoaderResourceAccessor(), database); + liquibase.rollback(10000, ""); + } catch (Exception e) { + throw new DatabaseException(e); + } finally { + if (dbConnection != null) { + try { + dbConnection.rollback(); + dbConnection.close(); + } catch (SQLException e){ + //nothing to do + } + } + } + } + +} diff --git a/src/metabase/db.clj b/src/metabase/db.clj index 5bcafbb1eef9ab036682a8876558744da51b2cda..ba5b3e06947c7e4336dae4b4d4307273f69cb49d 100644 --- a/src/metabase/db.clj +++ b/src/metabase/db.clj @@ -1,5 +1,6 @@ (ns metabase.db (:require [clojure.tools.logging :as log] + [clojure.string :as str] [environ.core :refer [env]] [korma.core :refer :all] [korma.db :refer :all] @@ -9,9 +10,11 @@ (defn get-db-file "Check config/environment to determine the path to the h2 db file we want to use" [] - (or (env :database-file) (get app-defaults :database-file))) + (str "file:" (or (env :database-file) (get app-defaults :database-file)))) (defdb db (do (log/info (str "Using H2 database file: " (get-db-file))) - (h2 {:db (get-db-file)}))) + (h2 {:db (get-db-file) + :naming {:keys str/lower-case + :fields str/upper-case}}))) diff --git a/src/metabase/models/org.clj b/src/metabase/models/org.clj new file mode 100644 index 0000000000000000000000000000000000000000..fd32e7c15b6955f812a950ec14974b0172c5f282 --- /dev/null +++ b/src/metabase/models/org.clj @@ -0,0 +1,14 @@ +(ns metabase.models.org + (:use korma.core + [metabase.models.org-perm :only (OrgPerm)])) + +(defentity Org + (table :core_organization) +; (entity-fields +; :id +; :name +; :description +; :logo_url +; :inherits +; :slug) + (has-many OrgPerm {:fk :organization_id})) diff --git a/test/metabase/models/org_test.clj b/test/metabase/models/org_test.clj new file mode 100644 index 0000000000000000000000000000000000000000..3df7e3eeb438734a46b38bed1d143f771a03a400 --- /dev/null +++ b/test/metabase/models/org_test.clj @@ -0,0 +1,38 @@ +(ns metabase.models.org-test + (:require [clojure.tools.logging :as log] + [clojure.java.jdbc :as jdbc] + [metabase.db :refer :all] + [metabase.config :refer [app-defaults]] + [metabase.models.org :refer [Org]] + [korma.core :refer :all] + [midje.sweet :refer :all])) + + +(defn liquibase-up [] + (let [conn (jdbc/get-connection {:subprotocol "h2" + :subname (get-db-file)})] + (com.metabase.corvus.migrations.LiquibaseMigrations/setupDatabase conn))) + +(defn liquibase-down [] + (let [conn (jdbc/get-connection {:subprotocol "h2" + :subname (get-db-file)})] + (com.metabase.corvus.migrations.LiquibaseMigrations/teardownDatabase conn))) + + +(defn count-orgs [] + (get-in (first (select Org (aggregate (count :*) :cnt))) [:cnt])) + + +(facts "about Core model" + (with-state-changes [(before :facts (liquibase-up)) + (after :facts (liquibase-down))] + (fact "starts with 0 entries" + (count-orgs) => 0) + (fact "can insert new entries" + (let [result (insert Org (values {:name "test" + :slug "test" + :inherits false}))] + (nil? result) => false + (> 0 (or (get-in result [:generated_key]) (get-in result [:scope_identity()]) -1))) + (count-orgs) => 1) + ))