diff --git a/.gitignore b/.gitignore index d3da4e0..8f3df91 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,5 @@ pom.xml.asc .hgignore .hg/ .cpcache -resources/dev.db +resources/**/dev.db log/ diff --git a/README.md b/README.md index 56a2895..d64165e 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,9 @@ Ubiq is an experimental way to build GraphQL apis with minimal code. -### Introduction - The central idea is to define all resolvers as a series of interceptors. These interceptors are invoked in order, can pass data down the next interceptor and can exit in case of errors. All interceptors are defined statically in `resolvers.edn` file. -Ubiq also provides components that help you tie GraphQL queries and mutations directly to SQL statements via HugSQL. +Ubiq also provides components that help you tie GraphQL queries and mutations directly to SQL statements via HugSQL and an authentication mechanism to speed up development of new projects. ### Getting started diff --git a/dev/user.clj b/dev/user.clj index 1de0b6f..bc4943d 100644 --- a/dev/user.clj +++ b/dev/user.clj @@ -4,18 +4,18 @@ [integrant.core :as ig] [integrant.repl :refer [clear go halt prep init reset reset-all]] [aero.core :refer [read-config reader]] - [components.graphql-server] - [components.migrator] - [components.domain] - [components.seeder] - [components.resolver])) + [ubiq.components.graphql-server] + [ubiq.components.migrator] + [ubiq.components.domain] + [ubiq.components.seeder] + [ubiq.components.resolver])) (defmethod reader 'ig/ref [_ _ value] (ig/ref value)) (def config - (read-config (io/resource "config.edn") {:profile :dev})) + (read-config (io/resource "erp_app/config.edn") {:profile :dev})) (integrant.repl/set-prep! (constantly (:system config))) diff --git a/resources/config.edn b/resources/config.edn deleted file mode 100644 index 534711b..0000000 --- a/resources/config.edn +++ /dev/null @@ -1,23 +0,0 @@ -{:sqlite-config - #profile - {:dev {:classname "org.sqlite.JDBC" - :subprotocol "sqlite" - :subname "resources/dev.db"}} - - :seed-data? - #profile - {:default false - :dev true} - - :system - {:graphql-server {:enable-graphiql? true - :port 8000 - :resolver #ig/ref :resolver - :domain #ig/ref :domain} - :migrator {:db #ref [:sqlite-config]} - :resolver {:domain #ig/ref :domain} - :domain {:db #ref [:sqlite-config]} - :seeder {:seed-count {:parties 10} - :seed-data? #ref [:seed-data?] - :domain #ig/ref :domain - :migrator #ig/ref :migrator}}} diff --git a/resources/erp_app/config.edn b/resources/erp_app/config.edn new file mode 100644 index 0000000..d89ef77 --- /dev/null +++ b/resources/erp_app/config.edn @@ -0,0 +1,28 @@ +{:app-folder "erp_app" + :sqlite-config + #profile + {:dev {:classname "org.sqlite.JDBC" + :subprotocol "sqlite" + :subname #join ["resources/" #ref [:app-folder] "/dev.db"]}} + + :seed-data? + #profile + {:default false + :dev true} + + :system + {:graphql-server {:enable-graphiql? true + :port 8000 + :resolver #ig/ref :resolver + :domain #ig/ref :domain + :schema-resource #join [#ref [:app-folder] "/schema.edn"]} + :migrator {:db #ref [:sqlite-config] + :migrations-folder #join [#ref [:app-folder] "/migrations"]} + :resolver {:domain #ig/ref :domain + :resolver-config-resource #join [#ref [:app-folder] "/resolvers.edn"]} + :domain {:db #ref [:sqlite-config] + :sql-folder #join ["src/" #ref [:app-folder] "/sql"]} + :seeder {:seed-count {:parties 10} + :seed-data? #ref [:seed-data?] + :domain #ig/ref :domain + :migrator #ig/ref :migrator}}} diff --git a/resources/migrations/001-create-parties-challans-and-products-table.edn b/resources/erp_app/migrations/001-create-parties-challans-and-products-table.edn similarity index 100% rename from resources/migrations/001-create-parties-challans-and-products-table.edn rename to resources/erp_app/migrations/001-create-parties-challans-and-products-table.edn diff --git a/resources/resolvers.edn b/resources/erp_app/resolvers.edn similarity index 100% rename from resources/resolvers.edn rename to resources/erp_app/resolvers.edn diff --git a/resources/schema.edn b/resources/erp_app/schema.edn similarity index 100% rename from resources/schema.edn rename to resources/erp_app/schema.edn diff --git a/resources/logback.xml b/resources/logback.xml deleted file mode 100644 index 04d7eb9..0000000 --- a/resources/logback.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - kermit - true - true - DEBUG - - - - - - - UTF-8 - %date[ISO8601] [%thread] %-5level %logger{36} - %mdc %msg%n - - - - - - - - - - - - - d5a15b83e7d04fb8b53a23d00d30b1ea - ${environment} - - ERROR - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/components/domain.clj b/src/components/domain.clj deleted file mode 100644 index bd03ef2..0000000 --- a/src/components/domain.clj +++ /dev/null @@ -1,35 +0,0 @@ -(ns components.domain - (:require [clojure.java.io :as io] - [clojure.string :as str] - [hugsql.core :as hugsql] - [integrant.core :as ig])) - -(defn- get-db-fns-map [file-name-dot-sql] - (hugsql/map-of-db-fns - (str "sql/" file-name-dot-sql))) - -(defn- read-file-tree - "Given a directory path, recursively read all files - and return a vector containing path of all files." - [path] - (->> path - io/file - file-seq - (filter #(.isFile %)) - (map #(.getPath %)))) - -(defn- inject-db [db db-fns-map] - (into {} (map (fn [[k v]] - [k (partial (:fn v) db)]) - db-fns-map))) - -(defn- path->file [p] - (last (str/split p #"/"))) - -(defmethod ig/init-key :domain [_ {:keys [db]}] - (let [sql-file-paths (read-file-tree "src/sql") ;; read all files in src/sql folder returns ("src/sql/products.sql" "src/sql/parties.sql") - sql-files (map path->file sql-file-paths)] ;; figure out the name of sql file (sql folder cannot have sub folders with this setup) - (into {} (map (fn [file-name-dot-sql] - (let [domain-entity-keyword (keyword (str/replace file-name-dot-sql #".sql" ""))] - [domain-entity-keyword (inject-db db (get-db-fns-map file-name-dot-sql))])) - sql-files)))) diff --git a/src/sql/parties.sql b/src/erp_app/sql/parties.sql similarity index 100% rename from src/sql/parties.sql rename to src/erp_app/sql/parties.sql diff --git a/src/sql/products.sql b/src/erp_app/sql/products.sql similarity index 100% rename from src/sql/products.sql rename to src/erp_app/sql/products.sql diff --git a/src/ubiq/components/domain.clj b/src/ubiq/components/domain.clj new file mode 100644 index 0000000..a8f02f4 --- /dev/null +++ b/src/ubiq/components/domain.clj @@ -0,0 +1,35 @@ +(ns ubiq.components.domain + (:require [clojure.java.io :as io] + [clojure.string :as str] + [hugsql.core :as hugsql] + [integrant.core :as ig])) + +(defn- get-db-fns-map [file-path-dot-sql] + (hugsql/map-of-db-fns file-path-dot-sql)) + +(defn- read-file-tree + "Given a directory path, recursively read all files + and return a vector containing path of all files." + [path] + (->> path + io/file + file-seq + (filter #(.isFile %)) + (map #(.getPath %)))) + +(defn- inject-db [db db-fns-map] + (into {} (map (fn [[k v]] + [k (partial (:fn v) db)]) + db-fns-map))) + +(defn- path->file [p] + (last (str/split p #"/"))) + +(defmethod ig/init-key :domain [_ {:keys [db sql-folder]}] + (let [sql-file-paths (read-file-tree sql-folder) ; read all files in src/sql folder returns ("src/erp_app/sql/products.sql" "src/erp_app/sql/parties.sql") + sql-files (map path->file sql-file-paths) ; figure out the name of sql file (sql folder cannot have sub folders with this setup) + sql-folder-without-src (str/replace sql-folder #"src/" "")] + (into {} (map (fn [file-name-dot-sql] + [(keyword (str/replace file-name-dot-sql #".sql" "")) + (inject-db db (get-db-fns-map (str sql-folder-without-src "/" file-name-dot-sql)))]) + sql-files)))) diff --git a/src/components/graphql_server.clj b/src/ubiq/components/graphql_server.clj similarity index 89% rename from src/components/graphql_server.clj rename to src/ubiq/components/graphql_server.clj index d37f1bc..78dcd80 100644 --- a/src/components/graphql_server.clj +++ b/src/ubiq/components/graphql_server.clj @@ -1,4 +1,4 @@ -(ns components.graphql-server +(ns ubiq.components.graphql-server (:require [clojure.java.io :as io] [clojure.edn :as edn] [integrant.core :as ig] @@ -7,8 +7,8 @@ [com.walmartlabs.lacinia.schema :as schema] [io.pedestal.http :as http])) -(defmethod ig/init-key :graphql-server [_ {:keys [enable-graphiql? port resolver domain]}] - (-> "schema.edn" +(defmethod ig/init-key :graphql-server [_ {:keys [enable-graphiql? port resolver domain schema-resource]}] + (-> schema-resource io/resource slurp edn/read-string diff --git a/src/components/migrator.clj b/src/ubiq/components/migrator.clj similarity index 61% rename from src/components/migrator.clj rename to src/ubiq/components/migrator.clj index 6e5427f..f23b4c4 100644 --- a/src/components/migrator.clj +++ b/src/ubiq/components/migrator.clj @@ -1,11 +1,11 @@ -(ns components.migrator +(ns ubiq.components.migrator (:require [ragtime.jdbc :as jdbc] [ragtime.repl :as repl] [integrant.core :as ig])) -(defmethod ig/init-key :migrator [_ {:keys [db]}] +(defmethod ig/init-key :migrator [_ {:keys [db migrations-folder]}] (let [config {:datastore (jdbc/sql-database db) - :migrations (jdbc/load-resources "migrations")}] + :migrations (jdbc/load-resources migrations-folder)}] (repl/migrate config) config)) diff --git a/src/components/resolver.clj b/src/ubiq/components/resolver.clj similarity index 82% rename from src/components/resolver.clj rename to src/ubiq/components/resolver.clj index 1217261..6e683bd 100644 --- a/src/components/resolver.clj +++ b/src/ubiq/components/resolver.clj @@ -1,9 +1,9 @@ -(ns components.resolver +(ns ubiq.components.resolver (:require [clojure.java.io :as io] [clojure.edn :as edn] [integrant.core :as ig] - [intercepts.auth :as auth] - [intercepts.domain :as domain])) + [ubiq.intercepts.auth :as auth] + [ubiq.intercepts.domain :as domain])) (defn- handle-exit-wrapper [{:keys [i-fn i-args]} ctx] (let [ctx-with-i-args (assoc-in ctx [:interceptor-args] i-args)] @@ -14,7 +14,7 @@ (defn- resolve-interceptor-fn-symbol [s] ;; assuming that all intercepts will be under intercepts. namespace ;; and will be imported in this domain! (Can improve DX here by checking that ns exists) - (resolve (symbol (str "intercepts." (namespace s) "/" (name s))))) + (resolve (symbol (str "ubiq.intercepts." (namespace s) "/" (name s))))) (defn- intercept->interceptor-fn [intercept] ;; allow for different kinds of interceptor config and normalise it here @@ -46,14 +46,14 @@ :args args :value value})))) -(defn get-resolvers-config [] - (-> "resolvers.edn" +(defn get-resolvers-config [resolver-config-resource] + (-> resolver-config-resource io/resource slurp edn/read-string)) -(defmethod ig/init-key :resolver [_ {:keys []}] - (let [resolvers-config (get-resolvers-config)] +(defmethod ig/init-key :resolver [_ {:keys [resolver-config-resource]}] + (let [resolvers-config (get-resolvers-config resolver-config-resource)] (zipmap (keys resolvers-config) (map compile-intercepts (vals resolvers-config))))) diff --git a/src/components/seeder.clj b/src/ubiq/components/seeder.clj similarity index 97% rename from src/components/seeder.clj rename to src/ubiq/components/seeder.clj index 439c5cd..8471e00 100644 --- a/src/components/seeder.clj +++ b/src/ubiq/components/seeder.clj @@ -1,4 +1,4 @@ -(ns components.seeder +(ns ubiq.components.seeder (:require [clojure.tools.logging :as log] [integrant.core :as ig] [hugsql.core :as hugsql] diff --git a/src/intercepts/auth.clj b/src/ubiq/intercepts/auth.clj similarity index 92% rename from src/intercepts/auth.clj rename to src/ubiq/intercepts/auth.clj index 2c7a16e..db8d07d 100644 --- a/src/intercepts/auth.clj +++ b/src/ubiq/intercepts/auth.clj @@ -1,4 +1,4 @@ -(ns intercepts.auth) +(ns ubiq.intercepts.auth) (defn- is-logged-in? [_] true) diff --git a/src/intercepts/domain.clj b/src/ubiq/intercepts/domain.clj similarity index 91% rename from src/intercepts/domain.clj rename to src/ubiq/intercepts/domain.clj index 36e44e1..f5e7bd4 100644 --- a/src/intercepts/domain.clj +++ b/src/ubiq/intercepts/domain.clj @@ -1,4 +1,4 @@ -(ns intercepts.domain) +(ns ubiq.intercepts.domain) (defn executor [{:keys [lacinia-ctx args value interceptor-args] :as ctx}] (let [domain (:domain lacinia-ctx)