mirror of
https://github.com/penpot/penpot.git
synced 2025-05-16 23:56:10 +02:00
♻️ Restructure the services directory.
This commit is contained in:
parent
eeb5482d36
commit
b66bc02098
45 changed files with 951 additions and 960 deletions
|
@ -5,6 +5,7 @@
|
|||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="io.vertx.sqlclient.impl.SocketConnectionBase" level="INFO"/>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="console"/>
|
||||
</Root>
|
||||
|
|
|
@ -13,6 +13,28 @@ CREATE TABLE users (
|
|||
metadata bytea NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_storage (
|
||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
|
||||
key text NOT NULL,
|
||||
val bytea NOT NULL,
|
||||
|
||||
PRIMARY KEY (key, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE user_tokens (
|
||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token text NOT NULL,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
used_at timestamptz DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (token, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
|
||||
|
@ -24,6 +46,7 @@ CREATE TABLE sessions (
|
|||
);
|
||||
|
||||
-- Insert a placeholder system user.
|
||||
|
||||
INSERT INTO users (id, fullname, username, email, photo, password, metadata)
|
||||
VALUES ('00000000-0000-0000-0000-000000000000'::uuid,
|
||||
'System User',
|
||||
|
@ -44,18 +67,6 @@ CREATE UNIQUE INDEX users_email_idx
|
|||
CREATE TRIGGER users_modified_at_tgr BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||
|
||||
CREATE TABLE user_pswd_recovery (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id uuid REFERENCES users(id) ON DELETE CASCADE,
|
||||
token text NOT NULL,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
used_at timestamptz DEFAULT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX user_pswd_recovery_user_idx
|
||||
ON user_pswd_recovery USING btree (user_id);
|
||||
|
||||
CREATE UNIQUE INDEX user_pswd_recovery_token_idx
|
||||
ON user_pswd_recovery USING btree (token);
|
||||
CREATE TRIGGER user_storage_modified_at_tgr BEFORE UPDATE ON user_storage
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
CREATE TABLE IF NOT EXISTS kvstore (
|
||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
|
||||
key text NOT NULL,
|
||||
value bytea NOT NULL,
|
||||
|
||||
PRIMARY KEY (key, user_id)
|
||||
);
|
||||
|
||||
CREATE TRIGGER kvstore_modified_at_tgr BEFORE UPDATE ON kvstore
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
@ -1 +0,0 @@
|
|||
select version();
|
|
@ -16,7 +16,6 @@
|
|||
[uxbox.http.session :as session]
|
||||
[uxbox.http.handlers :as handlers]
|
||||
[uxbox.http.debug :as debug]
|
||||
[uxbox.services.core :as sv]
|
||||
[vertx.core :as vc]
|
||||
[vertx.http :as vh]
|
||||
[vertx.web :as vw]
|
||||
|
@ -62,6 +61,7 @@
|
|||
(vw/assets "/static/*" {:root "resources/public/static"})
|
||||
(vw/router routes))]
|
||||
|
||||
(log/info "Starting http server on" (:http-server-port cfg/config) "port.")
|
||||
(vh/server ctx {:handler handler
|
||||
:port (:http-server-port cfg/config)})))
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.http.errors :as errors]
|
||||
[uxbox.http.session :as session]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn emails-list
|
||||
|
|
|
@ -9,16 +9,18 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.emails :as emails]
|
||||
[uxbox.http.session :as session]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.init]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn query-handler
|
||||
[req]
|
||||
(let [type (get-in req [:path-params :type])
|
||||
data (merge (:params req)
|
||||
{::sv/type (keyword type)
|
||||
{::sq/type (keyword type)
|
||||
:user (:user req)})]
|
||||
(-> (sv/query (with-meta data {:req req}))
|
||||
(-> (sq/handle (with-meta data {:req req}))
|
||||
(p/then' (fn [result]
|
||||
{:status 200
|
||||
:body result})))))
|
||||
|
@ -29,9 +31,9 @@
|
|||
data (merge (:params req)
|
||||
(:body-params req)
|
||||
(:uploads req)
|
||||
{::sv/type (keyword type)
|
||||
{::sm/type (keyword type)
|
||||
:user (:user req)})]
|
||||
(-> (sv/mutation (with-meta data {:req req}))
|
||||
(-> (sm/handle (with-meta data {:req req}))
|
||||
(p/then' (fn [result]
|
||||
{:status 200 :body result})))))
|
||||
|
||||
|
@ -39,7 +41,7 @@
|
|||
[req]
|
||||
(let [data (:body-params req)
|
||||
user-agent (get-in req [:headers "user-agent"])]
|
||||
(-> (sv/mutation (assoc data ::sv/type :login))
|
||||
(-> (sm/handle (assoc data ::sm/type :login))
|
||||
(p/then #(session/create % user-agent))
|
||||
(p/then' (fn [token]
|
||||
{:status 204
|
||||
|
@ -59,9 +61,9 @@
|
|||
(defn register-handler
|
||||
[req]
|
||||
(let [data (merge (:body-params req)
|
||||
{::sv/type :register-profile})
|
||||
{::sm/type :register-profile})
|
||||
user-agent (get-in req [:headers "user-agent"])]
|
||||
(-> (sv/mutation (with-meta data {:req req}))
|
||||
(-> (sm/handle (with-meta data {:req req}))
|
||||
(p/then (fn [{:keys [id] :as user}]
|
||||
(session/create id user-agent)))
|
||||
(p/then' (fn [token]
|
||||
|
|
|
@ -19,28 +19,25 @@
|
|||
:steps
|
||||
[{:desc "Initial triggers and utils."
|
||||
:name "0001-main"
|
||||
:fn (mg/resource "migrations/0001.main.up.sql")}
|
||||
:fn (mg/resource "migrations/0001.main.sql")}
|
||||
{:desc "Initial auth related tables"
|
||||
:name "0002-auth"
|
||||
:fn (mg/resource "migrations/0002.auth.up.sql")}
|
||||
:name "0002-users"
|
||||
:fn (mg/resource "migrations/0002.users.sql")}
|
||||
{:desc "Initial projects tables"
|
||||
:name "0003-projects"
|
||||
:fn (mg/resource "migrations/0003.projects.up.sql")}
|
||||
:fn (mg/resource "migrations/0003.projects.sql")}
|
||||
{:desc "Initial pages tables"
|
||||
:name "0004-pages"
|
||||
:fn (mg/resource "migrations/0004.pages.up.sql")}
|
||||
{:desc "Initial kvstore tables"
|
||||
:name "0005-kvstore"
|
||||
:fn (mg/resource "migrations/0005.kvstore.up.sql")}
|
||||
:fn (mg/resource "migrations/0004.pages.sql")}
|
||||
{:desc "Initial emails related tables"
|
||||
:name "0006-emails"
|
||||
:fn (mg/resource "migrations/0006.emails.up.sql")}
|
||||
:name "0005-emails"
|
||||
:fn (mg/resource "migrations/0005.emails.sql")}
|
||||
{:desc "Initial images tables"
|
||||
:name "0007-images"
|
||||
:fn (mg/resource "migrations/0007.images.up.sql")}
|
||||
:name "0006-images"
|
||||
:fn (mg/resource "migrations/0006.images.sql")}
|
||||
{:desc "Initial icons tables"
|
||||
:name "0008-icons"
|
||||
:fn (mg/resource "migrations/0008.icons.up.sql")}
|
||||
:name "0007-icons"
|
||||
:fn (mg/resource "migrations/0007.icons.sql")}
|
||||
]})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.portation
|
||||
"Support for export/import operations of projects."
|
||||
(:refer-clojure :exclude [with-open])
|
||||
#_(:require [clojure.java.io :as io]
|
||||
[suricatta.core :as sc]
|
||||
[datoteka.core :as fs]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.closeable :refer (with-open)]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
;; ;; --- Export
|
||||
|
||||
;; (defn- write-project
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-project-by-id {:id id})
|
||||
;; result (sc/fetch-one conn sql)]
|
||||
;; (when-not result
|
||||
;; (ex-info "No project found with specified id" {:id id}))
|
||||
;; (t/write! writer {::type ::project ::payload result})))
|
||||
|
||||
;; (defn- write-pages
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-pages-for-project {:project id})
|
||||
;; results (sc/fetch conn sql)]
|
||||
;; (run! #(t/write! writer {::type ::page ::payload %}) results)))
|
||||
|
||||
;; (defn- write-pages-history
|
||||
;; [conn writer id]
|
||||
;; (let [sql (sql/get-page-history-for-project {:project id})
|
||||
;; results (sc/fetch conn sql)]
|
||||
;; (run! #(t/write! writer {::type ::page-history ::payload %}) results)))
|
||||
|
||||
;; (defn- write-data
|
||||
;; [path id]
|
||||
;; (with-open [ostream (io/output-stream path)
|
||||
;; zstream (snappy/output-stream ostream)
|
||||
;; conn (db/connection)]
|
||||
;; (let [writer (t/writer zstream {:type :msgpack})]
|
||||
;; (sc/atomic conn
|
||||
;; (write-project conn writer id)
|
||||
;; (write-pages conn writer id)
|
||||
;; (write-pages-history conn writer id)))))
|
||||
|
||||
;; (defn export
|
||||
;; "Given an id, returns a path to a temporal file with the exported
|
||||
;; bundle of the specified project."
|
||||
;; [id]
|
||||
;; (let [path (fs/create-tempfile)]
|
||||
;; (write-data path id)
|
||||
;; path))
|
||||
|
||||
;; ;; --- Import
|
||||
|
||||
;; (defn- read-entry
|
||||
;; [reader]
|
||||
;; (try
|
||||
;; (t/read! reader)
|
||||
;; (catch RuntimeException e
|
||||
;; (let [cause (.getCause e)]
|
||||
;; (if (instance? java.io.EOFException cause)
|
||||
;; ::eof
|
||||
;; (throw e))))))
|
||||
|
||||
;; (defn- persist-project
|
||||
;; [conn project]
|
||||
;; (let [sql (sql/create-project project)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-page
|
||||
;; [conn page]
|
||||
;; (let [sql (sql/create-page page)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-page-history
|
||||
;; [conn history]
|
||||
;; (let [sql (sql/create-page-history history)]
|
||||
;; (sc/execute conn sql)))
|
||||
|
||||
;; (defn- persist-entry
|
||||
;; [conn entry]
|
||||
;; (let [payload (::payload entry)
|
||||
;; type (::type entry)]
|
||||
;; (case type
|
||||
;; ::project (persist-project conn payload)
|
||||
;; ::page (persist-page conn payload)
|
||||
;; ::page-history (persist-page-history conn payload))))
|
||||
|
||||
;; (defn- read-data
|
||||
;; [conn reader]
|
||||
;; (loop [entry (read-entry reader)]
|
||||
;; (when (not= entry ::eof)
|
||||
;; (persist-entry conn entry)
|
||||
;; (recur (read-entry reader)))))
|
||||
|
||||
;; (defn import!
|
||||
;; "Given a path to the previously exported bundle, try to import it."
|
||||
;; [path]
|
||||
;; (with-open [istream (io/input-stream (fs/path path))
|
||||
;; zstream (snappy/input-stream istream)
|
||||
;; conn (db/connection)]
|
||||
;; (let [reader (t/reader zstream {:type :msgpack})]
|
||||
;; (sc/atomic conn
|
||||
;; (read-data conn reader)
|
||||
;; nil))))
|
|
@ -1,71 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.core
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[promesa.core :as p]
|
||||
[vertx.core :as vc]
|
||||
[uxbox.core :refer [system]]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.dispatcher :as uds]
|
||||
[uxbox.util.exceptions :as ex])
|
||||
(:import
|
||||
java.util.Map
|
||||
java.util.List
|
||||
java.util.Map$Entry
|
||||
java.util.HashMap))
|
||||
|
||||
;; (def context-interceptor
|
||||
;; {:enter (fn [data]
|
||||
;; (update data :request assoc ::ctx (vc/get-or-create-context system)))})
|
||||
|
||||
(def logging-interceptor
|
||||
{:enter (fn [data]
|
||||
(let [type (get-in data [:request ::type])]
|
||||
(assoc data ::start-time (System/nanoTime))))
|
||||
:leave (fn [data]
|
||||
(let [elapsed (- (System/nanoTime) (::start-time data))
|
||||
elapsed (str (quot elapsed 1000000) "ms")
|
||||
type (get-in data [:request ::type])]
|
||||
(log/info "service" type "processed in" elapsed)
|
||||
data))})
|
||||
|
||||
|
||||
(uds/defservice query
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(uds/defservice mutation
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defmacro defmutation
|
||||
[key & rest]
|
||||
`(uds/defmethod mutation ~key ~@rest))
|
||||
|
||||
(defmacro defquery
|
||||
[key & rest]
|
||||
`(uds/defmethod query ~key ~@rest))
|
||||
|
||||
(defn raise-not-found-if-nil
|
||||
[v]
|
||||
(if (nil? v)
|
||||
(ex/raise :type :not-found
|
||||
:hint "Object doest not exists.")
|
||||
v))
|
||||
|
||||
(def constantly-nil (constantly nil))
|
||||
|
||||
(defn handle-on-context
|
||||
[p]
|
||||
(->> (vc/get-or-create-context system)
|
||||
(vc/handle-on-context p)))
|
35
backend/src/uxbox/services/init.clj
Normal file
35
backend/src/uxbox/services/init.clj
Normal file
|
@ -0,0 +1,35 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.init
|
||||
"A initialization of services."
|
||||
(:require
|
||||
[mount.core :as mount :refer [defstate]]))
|
||||
|
||||
(defn- load-query-services
|
||||
[]
|
||||
(require 'uxbox.services.queries.icons)
|
||||
(require 'uxbox.services.queries.images)
|
||||
(require 'uxbox.services.queries.pages)
|
||||
(require 'uxbox.services.queries.profiles)
|
||||
(require 'uxbox.services.queries.projects)
|
||||
(require 'uxbox.services.queries.user-storage))
|
||||
|
||||
(defn- load-mutation-services
|
||||
[]
|
||||
(require 'uxbox.services.mutations.auth)
|
||||
(require 'uxbox.services.mutations.icons)
|
||||
(require 'uxbox.services.mutations.images)
|
||||
(require 'uxbox.services.mutations.projects)
|
||||
(require 'uxbox.services.mutations.pages)
|
||||
(require 'uxbox.services.mutations.profiles)
|
||||
(require 'uxbox.services.mutations.user-storage))
|
||||
|
||||
(defstate query-services
|
||||
:start (load-query-services))
|
||||
|
||||
(defstate mutation-services
|
||||
:start (load-mutation-services))
|
|
@ -1,70 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.kvstore
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
(defn- decode-row
|
||||
[{:keys [value] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
value (assoc :value (blob/decode value)))))
|
||||
|
||||
;; --- Update KVStore
|
||||
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::key ::us/string)
|
||||
(s/def ::value any?)
|
||||
|
||||
(s/def ::upsert-kvstore
|
||||
(s/keys :req-un [::key ::value ::user]))
|
||||
|
||||
(sv/defmutation ::upsert-kvstore
|
||||
[{:keys [key value user] :as params}]
|
||||
(let [sql "insert into kvstore (key, value, user_id)
|
||||
values ($1, $2, $3)
|
||||
on conflict (user_id, key)
|
||||
do update set value = $2"
|
||||
val (blob/encode value)]
|
||||
(-> (db/query-one db/pool [sql key val user])
|
||||
(p/then' sv/constantly-nil))))
|
||||
|
||||
;; --- Retrieve KVStore
|
||||
|
||||
(s/def ::kvstore-entry
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sv/defquery ::kvstore-entry
|
||||
[{:keys [key user]}]
|
||||
(let [sql "select kv.*
|
||||
from kvstore as kv
|
||||
where kv.user_id = $2
|
||||
and kv.key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Delete KVStore
|
||||
|
||||
(s/def ::delete-kvstore
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sv/defmutation ::delete-kvstore
|
||||
[{:keys [user key] :as params}]
|
||||
(let [sql "delete from kvstore
|
||||
where user_id = $2
|
||||
and key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' sv/constantly-nil))))
|
19
backend/src/uxbox/services/mutations.clj
Normal file
19
backend/src/uxbox/services/mutations.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations
|
||||
(:require
|
||||
[uxbox.util.dispatcher :as uds]))
|
||||
|
||||
(uds/defservice handle
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(defmacro defmutation
|
||||
[key & rest]
|
||||
`(uds/defmethod handle ~key ~@rest))
|
|
@ -4,33 +4,32 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.auth
|
||||
(ns uxbox.services.mutations.auth
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[buddy.hashers :as hashers]
|
||||
[promesa.core :as p]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sc]
|
||||
[uxbox.services.users :as users]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.exceptions :as ex]))
|
||||
|
||||
(s/def ::username ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::scope ::us/string)
|
||||
|
||||
(s/def ::login-params
|
||||
(s/keys :req-un [::username ::password]
|
||||
:opt-un [::scope]))
|
||||
|
||||
(def ^:private user-by-username-sql
|
||||
"select id, password
|
||||
from users
|
||||
where username=$1 or email=$1
|
||||
and deleted_at is null")
|
||||
|
||||
(sc/defmutation :login
|
||||
(s/def ::username ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::scope ::us/string)
|
||||
|
||||
(s/def ::login
|
||||
(s/keys :req-un [::username ::password]
|
||||
:opt-un [::scope]))
|
||||
|
||||
(sm/defmutation ::login
|
||||
{:doc "User login"
|
||||
:spec ::login-params}
|
||||
[{:keys [username password scope] :as params}]
|
||||
|
@ -44,7 +43,7 @@
|
|||
(when-not (check-password user password)
|
||||
(ex/raise :type :validation
|
||||
:code ::wrong-credentials))
|
||||
(:id user))]
|
||||
|
||||
{:id (:id user)})]
|
||||
(-> (db/query-one db/pool [user-by-username-sql username])
|
||||
(p/then' check-user))))
|
|
@ -4,15 +4,15 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.icons
|
||||
"Icons library related services."
|
||||
(ns uxbox.services.mutations.icons
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.icons :refer [decode-icon-row]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
|
@ -36,75 +36,13 @@
|
|||
(s/def ::metadata
|
||||
(s/keys :opt-un [::width ::height ::view-box ::mimetype]))
|
||||
|
||||
(defn- decode-icon-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Collections
|
||||
|
||||
(def ^:private icons-collections-sql
|
||||
"select *,
|
||||
(select count(*) from icons where collection_id = ic.id) as num_icons
|
||||
from icons_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc")
|
||||
|
||||
(s/def ::icons-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :icons-collections
|
||||
{:doc "Retrieve all icons collections for current user."
|
||||
:spec ::icons-collections}
|
||||
[{:keys [user] :as params}]
|
||||
(let [sqlv [icons-collections-sql user]]
|
||||
(db/query db/pool sqlv)))
|
||||
|
||||
;; --- List Icons
|
||||
|
||||
(def ^:private icons-by-collection-sql
|
||||
"select *
|
||||
from icons as i
|
||||
where (i.user_id = $1 or
|
||||
i.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and i.deleted_at is null
|
||||
and case when $2::uuid is null then i.collection_id is null
|
||||
else i.collection_id = $2::uuid
|
||||
end
|
||||
order by i.created_at desc")
|
||||
|
||||
(s/def ::icons-by-collection
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sv/defquery :icons-by-collection
|
||||
{:doc "Retrieve icons for specified collection."
|
||||
:spec ::icons-by-collection}
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [icons-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then' #(mapv decode-icon-row %)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Collection
|
||||
|
||||
(s/def ::create-icons-collection
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation :create-icons-collection
|
||||
{:doc "Create a new collection of icons."
|
||||
:spec ::create-icons-collection}
|
||||
(sm/defmutation ::create-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into icons_collections (id, user_id, name)
|
||||
|
@ -116,9 +54,7 @@
|
|||
(s/def ::update-icons-collection
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sv/defmutation :update-icons-collection
|
||||
{:doc "Update a collection of icons."
|
||||
:spec ::update-icons-collection}
|
||||
(sm/defmutation ::update-icons-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
set name = $3
|
||||
|
@ -126,7 +62,7 @@
|
|||
and user_id = $2
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql id user name])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Copy Icon
|
||||
|
||||
|
@ -140,14 +76,12 @@
|
|||
and (user_id = $2 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)"]
|
||||
(-> (db/query-one conn [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
(s/def ::copy-icon
|
||||
(s/keys :req-un [:us/id ::collection-id ::user]))
|
||||
|
||||
(sv/defmutation :copy-icon
|
||||
{:doc "Copy an icon from one collection to other."
|
||||
:spec ::copy-icon}
|
||||
(sm/defmutation ::copy-icon
|
||||
[{:keys [user id collection-id] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (retrieve-icon conn {:user user :id id})
|
||||
|
@ -161,9 +95,7 @@
|
|||
(s/def ::delete-icons-collection
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation :delete-icons-collection
|
||||
{:doc "Delete a collection of icons."
|
||||
:spec ::delete-icons-collection}
|
||||
(sm/defmutation ::delete-icons-collection
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "update icons_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -171,8 +103,8 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
;; --- Mutation: Create Icon (Upload)
|
||||
|
||||
|
@ -194,9 +126,7 @@
|
|||
(s/keys :req-un [::user ::name ::metadata ::content]
|
||||
:opt-un [::id ::collection-id]))
|
||||
|
||||
(sv/defmutation :create-icon
|
||||
{:doc "Create (upload) a new icon."
|
||||
:spec ::create-icon}
|
||||
(sm/defmutation ::create-icon
|
||||
[params]
|
||||
(create-icon db/pool params))
|
||||
|
||||
|
@ -205,9 +135,7 @@
|
|||
(s/def ::update-icon
|
||||
(s/keys :req-un [::id ::user ::name ::collection-id]))
|
||||
|
||||
(sv/defmutation :update-icon
|
||||
{:doc "Update an icon entry."
|
||||
:spec ::update-icon}
|
||||
(sm/defmutation ::update-icon
|
||||
[{:keys [id name user collection-id] :as params}]
|
||||
(let [sql "update icons
|
||||
set name = $1,
|
||||
|
@ -216,16 +144,14 @@
|
|||
and user_id = $4
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql name collection-id id user])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Mutation: Delete Icon
|
||||
|
||||
(s/def ::delete-icon
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation :delete-icon
|
||||
{:doc "Delete an icon entry."
|
||||
:spec ::delete-icon}
|
||||
(sm/defmutation ::delete-icon
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update icons
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -233,5 +159,5 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
|
@ -4,8 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.images
|
||||
"Images library related services."
|
||||
(ns uxbox.services.mutations.images
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[datoteka.core :as fs]
|
||||
|
@ -15,7 +14,8 @@
|
|||
[uxbox.db :as db]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.core :as sc]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
|
@ -35,7 +35,7 @@
|
|||
[row]
|
||||
(let [opts +thumbnail-options+]
|
||||
(-> (px/submit! #(images/populate-thumbnails row opts))
|
||||
(sc/handle-on-context))))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn- populate-thumbnails
|
||||
[rows]
|
||||
|
@ -58,9 +58,7 @@
|
|||
(s/keys :req-un [::user ::us/name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sc/defmutation :create-image-collection
|
||||
{:doc "Create image collection"
|
||||
:spec ::create-image-collection}
|
||||
(sm/defmutation ::create-image-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "insert into images_collections (id, user_id, name)
|
||||
values ($1, $2, $3) returning *;"]
|
||||
|
@ -71,9 +69,7 @@
|
|||
(s/def ::update-images-collection
|
||||
(s/keys :req-un [::id ::user ::us/name]))
|
||||
|
||||
(sc/defmutation :update-images-collection
|
||||
{:doc "Update image collection."
|
||||
:spec ::update-images-collection}
|
||||
(sm/defmutation ::update-images-collection
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [sql "update images_collections
|
||||
set name = $3
|
||||
|
@ -82,30 +78,12 @@
|
|||
returning *;"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- List Collections
|
||||
|
||||
(def ^:private images-collections-sql
|
||||
"select *,
|
||||
(select count(*) from images where collection_id = ic.id) as num_images
|
||||
from images_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc;")
|
||||
|
||||
(sc/defquery :images-collections
|
||||
{:doc "Retrieve image collections for the current logged user"}
|
||||
[{:keys [user] :as params}]
|
||||
(db/query db/pool [images-collections-sql user]))
|
||||
|
||||
;; --- Delete Collection
|
||||
|
||||
(s/def ::delete-images-collection
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sc/defmutation :delete-images-collection
|
||||
{:doc "Delete an image collection"
|
||||
:spec ::delete-images-collection}
|
||||
(sm/defmutation ::delete-images-collection
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update images_collections
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -113,35 +91,16 @@
|
|||
and user_id = $2
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sc/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Retrieve Image
|
||||
|
||||
(defn retrieve-image
|
||||
[conn id]
|
||||
(let [sql "select * from images
|
||||
where id = $1
|
||||
and deleted_at is null;"]
|
||||
(db/query-one conn [sql id])))
|
||||
|
||||
;; (s/def ::retrieve-image
|
||||
;; (s/keys :req-un [::user ::us/id]))
|
||||
|
||||
;; (defmethod core/query :retrieve-image
|
||||
;; [params]
|
||||
;; (s/assert ::retrieve-image params)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (retrieve-image conn params)))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
;; --- Create Image (Upload)
|
||||
|
||||
(defn- store-image-in-fs
|
||||
[{:keys [name path] :as upload}]
|
||||
(prn "store-image-in-fs" upload)
|
||||
(let [filename (fs/name name)
|
||||
storage media/images-storage]
|
||||
(-> (ds/save storage filename path)
|
||||
(vc/handle-on-context))))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(def ^:private create-image-sql
|
||||
"insert into images (user_id, name, collection_id, path, width, height, mimetype)
|
||||
|
@ -167,9 +126,7 @@
|
|||
(s/keys :req-un [::user ::name ::file ::width ::height ::mimetype]
|
||||
:opt-un [::id ::collection-id]))
|
||||
|
||||
(sc/defmutation :create-image
|
||||
{:doc "Create (upload) new image."
|
||||
:spec ::create-image}
|
||||
(sm/defmutation ::create-image
|
||||
[{:keys [file] :as params}]
|
||||
(when-not (valid-image-types? (:mtype file))
|
||||
(ex/raise :type :validation
|
||||
|
@ -192,9 +149,7 @@
|
|||
and user_id = $4
|
||||
returning *;")
|
||||
|
||||
(sc/defmutation :update-image
|
||||
{:doc "Update a image entry."
|
||||
:spec ::update-image}
|
||||
(sm/defmutation ::update-image
|
||||
[{:keys [id name user collection-id] :as params}]
|
||||
(let [sql update-image-sql]
|
||||
(db/query-one db/pool [sql id collection-id name user])))
|
||||
|
@ -206,9 +161,7 @@
|
|||
(s/def ::copy-image
|
||||
(s/keys :req-un [::id ::collection-id ::user]))
|
||||
|
||||
(sc/defmutation :copy-image
|
||||
{:doc "Copy image from one collection to an other."
|
||||
:spec ::copy-image}
|
||||
(sm/defmutation ::copy-image
|
||||
[{:keys [user id collection-id] :as params}]
|
||||
(letfn [(copy-image [conn {:keys [path] :as image}]
|
||||
(-> (ds/lookup media/images-storage (:path image))
|
||||
|
@ -221,7 +174,7 @@
|
|||
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (retrieve-image conn {:id id :user user})
|
||||
(p/then sc/raise-not-found-if-nil)
|
||||
(p/then su/raise-not-found-if-nil)
|
||||
(p/then (partial copy-image conn))))))
|
||||
|
||||
;; --- Delete Image
|
||||
|
@ -237,9 +190,7 @@
|
|||
(s/def ::delete-image
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sc/defmutation :delete-image
|
||||
{:doc "Delete image entry."
|
||||
:spec ::delete-image}
|
||||
(sm/defmutation ::delete-image
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "update images
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -247,29 +198,3 @@
|
|||
and user_id = $2
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user])))
|
||||
|
||||
;; --- Query Images by Collection (id)
|
||||
|
||||
(def images-by-collection-sql
|
||||
"select * from images
|
||||
where (user_id = $1 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and deleted_at is null
|
||||
and case when $2::uuid is null then collection_id is null
|
||||
else collection_id = $2::uuid
|
||||
end
|
||||
order by created_at desc;")
|
||||
|
||||
(s/def ::images-by-collection-query
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sc/defquery :images-by-collection
|
||||
{:doc "Get all images of a collection"
|
||||
:spec ::images-by-collection-query}
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [images-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then populate-thumbnails)
|
||||
(p/then #(mapv populate-urls %)))))
|
||||
|
|
@ -4,22 +4,21 @@
|
|||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.pages
|
||||
(ns uxbox.services.mutations.pages
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.pages :refer [decode-row]]
|
||||
[uxbox.util.sql :as sql]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-row)
|
||||
|
||||
;; TODO: validate `:data` and `:metadata`
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
|
@ -29,84 +28,23 @@
|
|||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::metadata any?)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Pages by Project
|
||||
|
||||
(s/def ::pages-by-project
|
||||
(s/keys :req-un [::user ::project-id]))
|
||||
|
||||
(sv/defquery ::pages-by-project
|
||||
[{:keys [user project-id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.project_id = $1
|
||||
and pg.deleted_at is null
|
||||
order by pg.created_at asc;"]
|
||||
(-> (db/query db/pool [sql project-id user])
|
||||
(p/then #(mapv decode-row %)))))
|
||||
|
||||
;; --- Query: Page by Id
|
||||
|
||||
(s/def ::page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defquery ::page
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.id = $1
|
||||
and pg.deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Query: Page History
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::max ::us/integer)
|
||||
(s/def ::pinned ::us/boolean)
|
||||
(s/def ::since ::us/integer)
|
||||
|
||||
(s/def ::page-history
|
||||
(s/keys :req-un [::page-id ::user]
|
||||
:opt-un [::max ::pinned ::since]))
|
||||
|
||||
(sv/defquery ::page-history
|
||||
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
|
||||
(let [sql (-> (sql/from ["pages_history" "ph"])
|
||||
(sql/select "ph.*")
|
||||
(sql/where ["ph.user_id = ?" user]
|
||||
["ph.page_id = ?" page-id]
|
||||
["ph.version < ?" since]
|
||||
(when pinned
|
||||
["ph.pinned = ?" true]))
|
||||
(sql/order "ph.version desc")
|
||||
(sql/limit max))]
|
||||
(-> (db/query db/pool (sql/fmt sql))
|
||||
(p/then (partial mapv decode-row)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Page
|
||||
|
||||
(declare create-page)
|
||||
|
||||
(s/def ::create-page
|
||||
(s/keys :req-un [::data ::user ::project-id ::name ::metadata]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation ::create-page
|
||||
[{:keys [id user project-id name data metadata]}]
|
||||
(let [sql "insert into pages (id, user_id, project_id, name, data, metadata)
|
||||
values ($1, $2, $3, $4, $5, $6) returning *"
|
||||
(sm/defmutation ::create-page
|
||||
[params]
|
||||
(create-page db/pool params))
|
||||
|
||||
(defn create-page
|
||||
[conn {:keys [id user project-id name data metadata] :as params}]
|
||||
(let [sql "insert into pages (id, user_id, project_id, name, data, metadata, version)
|
||||
values ($1, $2, $3, $4, $5, $6, 0)
|
||||
returning *"
|
||||
id (or id (uuid/next))
|
||||
data (blob/encode data)
|
||||
mdata (blob/encode metadata)]
|
||||
|
@ -125,7 +63,7 @@
|
|||
and deleted_at is null
|
||||
for update;"]
|
||||
(-> (db/query-one conn [sql id])
|
||||
(p/then' sv/raise-not-found-if-nil))))
|
||||
(p/then' su/raise-not-found-if-nil))))
|
||||
|
||||
(update-page [conn {:keys [id name version data metadata user]}]
|
||||
(let [sql "update pages
|
||||
|
@ -136,15 +74,15 @@
|
|||
where id = $5
|
||||
and user_id = $6"]
|
||||
(-> (db/query-one conn [sql name version data metadata id user])
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
(update-history [conn {:keys [user id version data metadata]}]
|
||||
(let [sql "insert into pages_history (user_id, page_id, version, data, metadata)
|
||||
values ($1, $2, $3, $4, $5)"]
|
||||
(-> (db/query-one conn [sql user id version data metadata])
|
||||
(p/then' sv/constantly-nil))))]
|
||||
(p/then' su/constantly-nil))))]
|
||||
|
||||
(sv/defmutation ::update-page
|
||||
(sm/defmutation ::update-page
|
||||
[{:keys [id data metadata] :as params}]
|
||||
(db/with-atomic [conn db/pool]
|
||||
(-> (select-for-update conn id)
|
||||
|
@ -166,7 +104,7 @@
|
|||
(s/def ::update-page-metadata
|
||||
(s/keys :req-un [::user ::project-id ::name ::metadata ::id]))
|
||||
|
||||
(sv/defmutation ::update-page-metadata
|
||||
(sm/defmutation ::update-page-metadata
|
||||
[{:keys [id user project-id name metadata]}]
|
||||
(let [sql "update pages
|
||||
set name = $3,
|
||||
|
@ -184,7 +122,7 @@
|
|||
(s/def ::delete-page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sv/defmutation ::delete-page
|
||||
(sm/defmutation ::delete-page
|
||||
[{:keys [id user]}]
|
||||
(let [sql "update pages
|
||||
set deleted_at = clock_timestamp()
|
||||
|
@ -193,8 +131,8 @@
|
|||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then sv/raise-not-found-if-nil)
|
||||
(p/then sv/constantly-nil))))
|
||||
(p/then su/raise-not-found-if-nil)
|
||||
(p/then su/constantly-nil))))
|
||||
|
||||
;; ;; --- Update Page History
|
||||
|
||||
|
@ -211,29 +149,9 @@
|
|||
;; (s/def ::update-page-history
|
||||
;; (s/keys :req-un [::user ::id ::pinned ::label]))
|
||||
|
||||
;; (sv/defmutation :update-page-history
|
||||
;; (sm/defmutation :update-page-history
|
||||
;; {:doc "Update page history"
|
||||
;; :spec ::update-page-history}
|
||||
;; [params]
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (update-page-history conn params)))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn- decode-row
|
||||
[{:keys [data metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
data (assoc :data (blob/decode data))
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;; select pg.* from pages as pg
|
||||
;; where pg.id = :id
|
||||
;; and pg.deleted_at is null;
|
||||
|
||||
;; (defn get-page-by-id
|
||||
;; [conn id]
|
||||
;; (s/assert ::us/id id)
|
||||
;; (let [sqlv (sql/get-page-by-id {:id id})]
|
||||
;; (some-> (db/fetch-one conn sqlv)
|
||||
;; (decode-row))))
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.users
|
||||
(ns uxbox.services.mutations.profiles
|
||||
(:require
|
||||
[buddy.hashers :as hashers]
|
||||
[clojure.spec.alpha :as s]
|
||||
|
@ -17,7 +17,12 @@
|
|||
[uxbox.emails :as emails]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.profiles :refer [get-profile
|
||||
decode-profile-row
|
||||
strip-private-attrs
|
||||
resolve-thumbnail]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
|
@ -27,9 +32,6 @@
|
|||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-profile-row)
|
||||
(declare strip-private-attrs)
|
||||
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::fullname ::us/string)
|
||||
(s/def ::metadata any?)
|
||||
|
@ -39,42 +41,6 @@
|
|||
(s/def ::user ::us/uuid)
|
||||
(s/def ::username ::us/string)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Profile (own)
|
||||
|
||||
(defn- resolve-thumbnail
|
||||
[user]
|
||||
(let [opts {:src :photo
|
||||
:dst :photo
|
||||
:size [100 100]
|
||||
:quality 90
|
||||
:format "jpg"}]
|
||||
(-> (px/submit! #(images/populate-thumbnails user opts))
|
||||
(sv/handle-on-context))))
|
||||
|
||||
(defn- get-profile
|
||||
[conn id]
|
||||
(let [sql "select * from users where id=$1 and deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id])
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(s/def ::profile
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :profile
|
||||
{:doc "Retrieve the user profile."
|
||||
:spec ::profile}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (get-profile db/pool user)
|
||||
(p/then' strip-private-attrs)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Update Profile (own)
|
||||
|
||||
(defn- check-username-and-email!
|
||||
|
@ -110,14 +76,14 @@
|
|||
and deleted_at is null
|
||||
returning *"]
|
||||
(-> (db/query-one conn [sql id username email fullname (blob/encode metadata)])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' decode-profile-row)
|
||||
(p/then' strip-private-attrs))))
|
||||
|
||||
(s/def ::update-profile
|
||||
(s/keys :req-un [::id ::username ::email ::fullname ::metadata]))
|
||||
|
||||
(sv/defmutation :update-profile
|
||||
(sm/defmutation :update-profile
|
||||
{:doc "Update self profile."
|
||||
:spec ::update-profile}
|
||||
[params]
|
||||
|
@ -144,13 +110,13 @@
|
|||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one conn [sql user password])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
(s/def ::update-password
|
||||
(s/keys :req-un [::user ::us/password ::old-password]))
|
||||
|
||||
(sv/defmutation :update-password
|
||||
(sm/defmutation :update-password
|
||||
{:doc "Update self password."
|
||||
:spec ::update-password}
|
||||
[params]
|
||||
|
@ -168,7 +134,7 @@
|
|||
(def valid-image-types?
|
||||
#{"image/jpeg", "image/png", "image/webp"})
|
||||
|
||||
(sv/defmutation :update-profile-photo
|
||||
(sm/defmutation :update-profile-photo
|
||||
{:doc "Update profile photo."
|
||||
:spec ::update-profile-photo}
|
||||
[{:keys [user file] :as params}]
|
||||
|
@ -176,7 +142,7 @@
|
|||
(let [filename (fs/name name)
|
||||
storage media/images-storage]
|
||||
(-> (ds/save storage filename path)
|
||||
#_(sv/handle-on-context))))
|
||||
#_(su/handle-on-context))))
|
||||
|
||||
(update-user-photo [path]
|
||||
(let [sql "update users
|
||||
|
@ -185,7 +151,7 @@
|
|||
and deleted_at is null
|
||||
returning *"]
|
||||
(-> (db/query-one db/pool [sql (str path) user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' strip-private-attrs)
|
||||
(p/then resolve-thumbnail))))]
|
||||
|
||||
|
@ -216,33 +182,38 @@
|
|||
:code ::username-or-email-already-exists))
|
||||
params)))))
|
||||
|
||||
(defn- register-profile
|
||||
(defn create-profile
|
||||
"Create the user entry on the database with limited input
|
||||
filling all the other fields with defaults."
|
||||
[conn {:keys [username fullname email password] :as params}]
|
||||
(let [metadata (blob/encode {})
|
||||
[conn {:keys [id username fullname email password metadata] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
metadata (blob/encode metadata)
|
||||
password (hashers/encrypt password)
|
||||
sqlv [create-user-sql
|
||||
(uuid/next)
|
||||
id
|
||||
fullname
|
||||
username
|
||||
email
|
||||
password
|
||||
metadata]]
|
||||
(-> (db/query-one conn sqlv)
|
||||
(p/then' decode-profile-row)
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(defn register-profile
|
||||
[conn params]
|
||||
(-> (create-profile conn params)
|
||||
(p/then' strip-private-attrs)
|
||||
#_(p/then (fn [profile]
|
||||
(-> (emails/send! {::emails/id :users/register
|
||||
::emails/to (:email params)
|
||||
::emails/priority :high
|
||||
:name (:fullname params)})
|
||||
(p/then' (constantly profile))))))))
|
||||
(p/then' (constantly profile)))))))
|
||||
|
||||
(s/def ::register-profile
|
||||
(s/keys :req-un [::username ::email ::password ::fullname]))
|
||||
|
||||
(sv/defmutation :register-profile
|
||||
(sm/defmutation :register-profile
|
||||
{:doc "Register new user."
|
||||
:spec ::register-profile}
|
||||
[params]
|
||||
|
@ -366,16 +337,3 @@
|
|||
;; (some-> (db/fetch-one conn sqlv)
|
||||
;; (trim-user-attrs))))
|
||||
|
||||
;; --- Attrs Helpers
|
||||
|
||||
(defn- decode-profile-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
(defn strip-private-attrs
|
||||
"Only selects a publicy visible user attrs."
|
||||
[profile]
|
||||
(select-keys profile [:id :username :fullname :metadata
|
||||
:email :created-at :photo]))
|
83
backend/src/uxbox/services/mutations/projects.clj
Normal file
83
backend/src/uxbox/services/mutations/projects.clj
Normal file
|
@ -0,0 +1,83 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;; --- Mutation: Create Project
|
||||
|
||||
(declare create-project)
|
||||
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
;; TODO: create role on project creation (maybe in DB?)
|
||||
|
||||
(sm/defmutation ::create-project
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
(defn create-project
|
||||
[conn {:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one conn [sql id user name])))
|
||||
|
||||
;; --- Mutation: Update Project
|
||||
|
||||
(s/def ::update-project
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sm/defmutation :update-project
|
||||
{:doc "Update project."
|
||||
:spec ::update-project}
|
||||
[{:keys [id name user] :as params}]
|
||||
(let [sql "update projects
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Delete Project
|
||||
|
||||
(s/def ::delete-project
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sm/defmutation :delete-project
|
||||
{:doc "Delete project"
|
||||
:spec ::delete-project}
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update projects
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' su/constantly-nil))))
|
48
backend/src/uxbox/services/mutations/user_storage.clj
Normal file
48
backend/src/uxbox/services/mutations/user_storage.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.mutations.user-storage
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.services.queries.user-storage :refer [decode-row]]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Update
|
||||
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::key ::us/string)
|
||||
(s/def ::val any?)
|
||||
|
||||
(s/def ::upsert-user-storage-entry
|
||||
(s/keys :req-un [::key ::val ::user]))
|
||||
|
||||
(sm/defmutation ::upsert-user-storage-entry
|
||||
[{:keys [key val user] :as params}]
|
||||
(let [sql "insert into user_storage (key, val, user_id)
|
||||
values ($1, $2, $3)
|
||||
on conflict (user_id, key)
|
||||
do update set val = $2"
|
||||
val (blob/encode val)]
|
||||
(-> (db/query-one db/pool [sql key val user])
|
||||
(p/then' su/constantly-nil))))
|
||||
|
||||
;; --- Delete KVStore
|
||||
|
||||
(s/def ::delete-user-storage-entry
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sm/defmutation ::delete-user-storage-entry
|
||||
[{:keys [user key] :as params}]
|
||||
(let [sql "delete from user_storage
|
||||
where user_id = $2
|
||||
and key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' su/constantly-nil))))
|
|
@ -1,133 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Queries
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Query: Projects
|
||||
|
||||
(s/def ::projects-query
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sv/defquery :projects
|
||||
{:doc "Query all projects"
|
||||
:spec ::projects-query}
|
||||
[{:keys [user] :as params}]
|
||||
(let [sql "select distinct on (p.id, p.created_at)
|
||||
p.*,
|
||||
array_agg(pg.id) over (
|
||||
partition by p.id
|
||||
order by pg.created_at
|
||||
range between unbounded preceding and unbounded following
|
||||
) as pages
|
||||
from projects as p
|
||||
right join pages as pg
|
||||
on (pg.project_id = p.id)
|
||||
where p.user_id = $1
|
||||
order by p.created_at asc"]
|
||||
(-> (db/query db/pool [sql user])
|
||||
(p/then (fn [rows]
|
||||
(mapv #(update % :pages vec) rows))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mutations
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Mutation: Create Project
|
||||
|
||||
(s/def ::create-project
|
||||
(s/keys :req-un [::user ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(sv/defmutation :create-project
|
||||
{:doc "Create a project."
|
||||
:spec ::create-project}
|
||||
[{:keys [id user name] :as params}]
|
||||
(let [id (or id (uuid/next))
|
||||
sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Update Project
|
||||
|
||||
(s/def ::update-project
|
||||
(s/keys :req-un [::user ::name ::id]))
|
||||
|
||||
(sv/defmutation :update-project
|
||||
{:doc "Update project."
|
||||
:spec ::update-project}
|
||||
[{:keys [id name user] :as params}]
|
||||
(let [sql "update projects
|
||||
set name = $3
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning *"]
|
||||
(db/query-one db/pool [sql id user name])))
|
||||
|
||||
;; --- Mutation: Delete Project
|
||||
|
||||
(s/def ::delete-project
|
||||
(s/keys :req-un [::id ::user]))
|
||||
|
||||
(sv/defmutation :delete-project
|
||||
{:doc "Delete project"
|
||||
:spec ::delete-project}
|
||||
[{:keys [id user] :as params}]
|
||||
(let [sql "update projects
|
||||
set deleted_at = clock_timestamp()
|
||||
where id = $1
|
||||
and user_id = $2
|
||||
and deleted_at is null
|
||||
returning id"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' sv/raise-not-found-if-nil)
|
||||
(p/then' sv/constantly-nil))))
|
||||
|
||||
|
||||
;; --- Retrieve Project by share token
|
||||
|
||||
;; (defn- get-project-by-share-token
|
||||
;; [conn token]
|
||||
;; (let [sqlv (sql/get-project-by-share-token {:token token})
|
||||
;; project (some-> (db/fetch-one conn sqlv)
|
||||
;; (data/normalize))]
|
||||
;; (when-let [id (:id project)]
|
||||
;; (let [pages (vec (pages/get-pages-for-project conn id))]
|
||||
;; (assoc project :pages pages)))))
|
||||
|
||||
;; (defmethod core/query :retrieve-project-by-share-token
|
||||
;; [{:keys [token]}]
|
||||
;; (s/assert ::token token)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (get-project-by-share-token conn token)))
|
||||
|
||||
;; --- Retrieve share tokens
|
||||
|
||||
;; (defn get-share-tokens-for-project
|
||||
;; [conn project]
|
||||
;; (s/assert ::project project)
|
||||
;; (let [sqlv (sql/get-share-tokens-for-project {:project project})]
|
||||
;; (db/fetch conn sqlv)))
|
||||
|
19
backend/src/uxbox/services/queries.clj
Normal file
19
backend/src/uxbox/services/queries.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries
|
||||
(:require
|
||||
[uxbox.util.dispatcher :as uds]))
|
||||
|
||||
(uds/defservice handle
|
||||
{:dispatch-by ::type
|
||||
:interceptors [uds/spec-interceptor
|
||||
#_logging-interceptor
|
||||
#_context-interceptor]})
|
||||
|
||||
(defmacro defquery
|
||||
[key & rest]
|
||||
`(uds/defmethod handle ~key ~@rest))
|
69
backend/src/uxbox/services/queries/icons.clj
Normal file
69
backend/src/uxbox/services/queries/icons.clj
Normal file
|
@ -0,0 +1,69 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.icons
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
||||
|
||||
(defn decode-icon-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
;; --- Query: Collections
|
||||
|
||||
(def ^:private icons-collections-sql
|
||||
"select *,
|
||||
(select count(*) from icons where collection_id = ic.id) as num_icons
|
||||
from icons_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc")
|
||||
|
||||
(s/def ::icons-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery ::icons-collections
|
||||
[{:keys [user] :as params}]
|
||||
(let [sqlv [icons-collections-sql user]]
|
||||
(db/query db/pool sqlv)))
|
||||
|
||||
;; --- Icons By Collection ID
|
||||
|
||||
(def ^:private icons-by-collection-sql
|
||||
"select *
|
||||
from icons as i
|
||||
where (i.user_id = $1 or
|
||||
i.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and i.deleted_at is null
|
||||
and case when $2::uuid is null then i.collection_id is null
|
||||
else i.collection_id = $2::uuid
|
||||
end
|
||||
order by i.created_at desc")
|
||||
|
||||
(s/def ::icons-by-collection
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sq/defquery ::icons-by-collection
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [icons-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then' #(mapv decode-icon-row %)))))
|
109
backend/src/uxbox/services/queries/images.clj
Normal file
109
backend/src/uxbox/services/queries/images.clj
Normal file
|
@ -0,0 +1,109 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.images
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[promesa.exec :as px]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.data :as data]
|
||||
[uxbox.util.exceptions :as ex]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[vertx.core :as vc]))
|
||||
|
||||
(def +thumbnail-options+
|
||||
{:src :path
|
||||
:dst :thumbnail
|
||||
:width 300
|
||||
:height 100
|
||||
:quality 92
|
||||
:format "webp"})
|
||||
|
||||
(defn populate-thumbnail
|
||||
[row]
|
||||
(let [opts +thumbnail-options+]
|
||||
(-> (px/submit! #(images/populate-thumbnails row opts))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn populate-thumbnails
|
||||
[rows]
|
||||
(if (empty? rows)
|
||||
rows
|
||||
(p/all (map populate-thumbnail rows))))
|
||||
|
||||
(defn populate-urls
|
||||
[row]
|
||||
(images/populate-urls row media/images-storage :path :url))
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
||||
|
||||
(def ^:private images-collections-sql
|
||||
"select *,
|
||||
(select count(*) from images where collection_id = ic.id) as num_images
|
||||
from images_collections as ic
|
||||
where (ic.user_id = $1 or
|
||||
ic.user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and ic.deleted_at is null
|
||||
order by ic.created_at desc;")
|
||||
|
||||
(s/def ::images-collections
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery ::images-collections
|
||||
[{:keys [user] :as params}]
|
||||
(db/query db/pool [images-collections-sql user]))
|
||||
|
||||
;; --- Retrieve Image
|
||||
|
||||
(defn retrieve-image
|
||||
[conn id]
|
||||
(let [sql "select * from images
|
||||
where id = $1
|
||||
and deleted_at is null;"]
|
||||
(db/query-one conn [sql id])))
|
||||
|
||||
;; (s/def ::retrieve-image
|
||||
;; (s/keys :req-un [::user ::us/id]))
|
||||
|
||||
;; (defmethod core/query :retrieve-image
|
||||
;; [params]
|
||||
;; (s/assert ::retrieve-image params)
|
||||
;; (with-open [conn (db/connection)]
|
||||
;; (retrieve-image conn params)))
|
||||
|
||||
;; --- Query Images by Collection (id)
|
||||
|
||||
(def images-by-collection-sql
|
||||
"select * from images
|
||||
where (user_id = $1 or
|
||||
user_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
||||
and deleted_at is null
|
||||
and case when $2::uuid is null then collection_id is null
|
||||
else collection_id = $2::uuid
|
||||
end
|
||||
order by created_at desc;")
|
||||
|
||||
(s/def ::images-by-collection-query
|
||||
(s/keys :req-un [::user]
|
||||
:opt-un [::collection-id]))
|
||||
|
||||
(sq/defquery ::images-by-collection
|
||||
[{:keys [user collection-id] :as params}]
|
||||
(let [sqlv [images-by-collection-sql user collection-id]]
|
||||
(-> (db/query db/pool sqlv)
|
||||
(p/then populate-thumbnails)
|
||||
(p/then #(mapv populate-urls %)))))
|
||||
|
92
backend/src/uxbox/services/queries/pages.clj
Normal file
92
backend/src/uxbox/services/queries/pages.clj
Normal file
|
@ -0,0 +1,92 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.pages
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.sql :as sql]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-row)
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
|
||||
;; --- Query: Pages by Project
|
||||
|
||||
(s/def ::pages-by-project
|
||||
(s/keys :req-un [::user ::project-id]))
|
||||
|
||||
(sq/defquery ::pages-by-project
|
||||
[{:keys [user project-id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.project_id = $1
|
||||
and pg.deleted_at is null
|
||||
order by pg.created_at asc;"]
|
||||
(-> (db/query db/pool [sql project-id user])
|
||||
(p/then #(mapv decode-row %)))))
|
||||
|
||||
;; --- Query: Page by Id
|
||||
|
||||
(s/def ::page
|
||||
(s/keys :req-un [::user ::id]))
|
||||
|
||||
(sq/defquery ::page
|
||||
[{:keys [user id] :as params}]
|
||||
(let [sql "select pg.*,
|
||||
pg.data,
|
||||
pg.metadata
|
||||
from pages as pg
|
||||
where pg.user_id = $2
|
||||
and pg.id = $1
|
||||
and pg.deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id user])
|
||||
(p/then' decode-row))))
|
||||
|
||||
;; --- Query: Page History
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::max ::us/integer)
|
||||
(s/def ::pinned ::us/boolean)
|
||||
(s/def ::since ::us/integer)
|
||||
|
||||
(s/def ::page-history
|
||||
(s/keys :req-un [::page-id ::user]
|
||||
:opt-un [::max ::pinned ::since]))
|
||||
|
||||
(sq/defquery ::page-history
|
||||
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
|
||||
(let [sql (-> (sql/from ["pages_history" "ph"])
|
||||
(sql/select "ph.*")
|
||||
(sql/where ["ph.user_id = ?" user]
|
||||
["ph.page_id = ?" page-id]
|
||||
["ph.version < ?" since]
|
||||
(when pinned
|
||||
["ph.pinned = ?" true]))
|
||||
(sql/order "ph.version desc")
|
||||
(sql/limit max))]
|
||||
(-> (db/query db/pool (sql/fmt sql))
|
||||
(p/then (partial mapv decode-row)))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn decode-row
|
||||
[{:keys [data metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
data (assoc :data (blob/decode data))
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
73
backend/src/uxbox/services/queries/profiles.clj
Normal file
73
backend/src/uxbox/services/queries/profiles.clj
Normal file
|
@ -0,0 +1,73 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.profiles
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[promesa.exec :as px]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.images :as images]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(declare decode-profile-row)
|
||||
(declare strip-private-attrs)
|
||||
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::fullname ::us/string)
|
||||
(s/def ::metadata any?)
|
||||
(s/def ::old-password ::us/string)
|
||||
(s/def ::password ::us/string)
|
||||
(s/def ::path ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
(s/def ::username ::us/string)
|
||||
|
||||
;; --- Query: Profile (own)
|
||||
|
||||
(defn resolve-thumbnail
|
||||
[user]
|
||||
(let [opts {:src :photo
|
||||
:dst :photo
|
||||
:size [100 100]
|
||||
:quality 90
|
||||
:format "jpg"}]
|
||||
(-> (px/submit! #(images/populate-thumbnails user opts))
|
||||
(su/handle-on-context))))
|
||||
|
||||
(defn get-profile
|
||||
[conn id]
|
||||
(let [sql "select * from users where id=$1 and deleted_at is null"]
|
||||
(-> (db/query-one db/pool [sql id])
|
||||
(p/then' decode-profile-row))))
|
||||
|
||||
(s/def ::profile
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery :profile
|
||||
{:doc "Retrieve the user profile."
|
||||
:spec ::profile}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (get-profile db/pool user)
|
||||
(p/then' strip-private-attrs)))
|
||||
|
||||
;; --- Attrs Helpers
|
||||
|
||||
(defn decode-profile-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
(defn strip-private-attrs
|
||||
"Only selects a publicy visible user attrs."
|
||||
[profile]
|
||||
(select-keys profile [:id :username :fullname :metadata
|
||||
:email :created-at :photo]))
|
48
backend/src/uxbox/services/queries/projects.clj
Normal file
48
backend/src/uxbox/services/queries/projects.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.projects
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
;; --- Query: Projects
|
||||
|
||||
(def ^:private projects-sql
|
||||
"select distinct on (p.id, p.created_at)
|
||||
p.*,
|
||||
array_agg(pg.id) over (
|
||||
partition by p.id
|
||||
order by pg.created_at
|
||||
range between unbounded preceding and unbounded following
|
||||
) as pages
|
||||
from projects as p
|
||||
left join pages as pg
|
||||
on (pg.project_id = p.id)
|
||||
where p.user_id = $1
|
||||
order by p.created_at asc")
|
||||
|
||||
(s/def ::projects-query
|
||||
(s/keys :req-un [::user]))
|
||||
|
||||
(sq/defquery :projects
|
||||
{:doc "Query all projects"
|
||||
:spec ::projects-query}
|
||||
[{:keys [user] :as params}]
|
||||
(-> (db/query db/pool [projects-sql user])
|
||||
(p/then (fn [rows]
|
||||
(mapv #(update % :pages vec) rows)))))
|
34
backend/src/uxbox/services/queries/user_storage.clj
Normal file
34
backend/src/uxbox/services/queries/user_storage.clj
Normal file
|
@ -0,0 +1,34 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.queries.user-storage
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.services.util :as su]
|
||||
[uxbox.util.blob :as blob]
|
||||
[uxbox.util.spec :as us]))
|
||||
|
||||
(defn decode-row
|
||||
[{:keys [val] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
val (assoc :val (blob/decode val)))))
|
||||
|
||||
(s/def ::user-storage-item
|
||||
(s/keys :req-un [::key ::user]))
|
||||
|
||||
(sq/defquery ::user-storage-entry
|
||||
[{:keys [key user]}]
|
||||
(let [sql "select kv.*
|
||||
from user_storage as kv
|
||||
where kv.user_id = $2
|
||||
and kv.key = $1"]
|
||||
(-> (db/query-one db/pool [sql key user])
|
||||
(p/then' su/raise-not-found-if-nil)
|
||||
(p/then' decode-row))))
|
39
backend/src/uxbox/services/util.clj
Normal file
39
backend/src/uxbox/services/util.clj
Normal file
|
@ -0,0 +1,39 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.services.util
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[vertx.core :as vc]
|
||||
[uxbox.core :refer [system]]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.dispatcher :as uds]
|
||||
[uxbox.util.exceptions :as ex]))
|
||||
|
||||
;; (def logging-interceptor
|
||||
;; {:enter (fn [data]
|
||||
;; (let [type (get-in data [:request ::type])]
|
||||
;; (assoc data ::start-time (System/nanoTime))))
|
||||
;; :leave (fn [data]
|
||||
;; (let [elapsed (- (System/nanoTime) (::start-time data))
|
||||
;; elapsed (str (quot elapsed 1000000) "ms")
|
||||
;; type (get-in data [:request ::type])]
|
||||
;; (log/info "service" type "processed in" elapsed)
|
||||
;; data))})
|
||||
|
||||
(defn raise-not-found-if-nil
|
||||
[v]
|
||||
(if (nil? v)
|
||||
(ex/raise :type :not-found
|
||||
:hint "Object doest not exists.")
|
||||
v))
|
||||
|
||||
(def constantly-nil (constantly nil))
|
||||
|
||||
(defn handle-on-context
|
||||
[p]
|
||||
(->> (vc/get-or-create-context system)
|
||||
(vc/handle-on-context p)))
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns uxbox.util.migrations
|
||||
(:require
|
||||
[clojure.tools.logging :as log]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -56,7 +57,7 @@
|
|||
(-> (registered? pool modname (:name migration))
|
||||
(p/then (fn [registered?]
|
||||
(when-not registered?
|
||||
(println (str/format "applying migration - %s: %s" modname name))
|
||||
(log/info (str/format "applying migration %s/%s" modname name))
|
||||
(execute)))))))
|
||||
|
||||
(defn- impl-migrate
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
[cuerdas.core :as str]
|
||||
[mount.core :as mount]
|
||||
[datoteka.storages :as st]
|
||||
[uxbox.services.mutations.profiles :as profiles]
|
||||
[uxbox.services.mutations.projects :as projects]
|
||||
[uxbox.services.mutations.pages :as pages]
|
||||
[uxbox.fixtures :as fixtures]
|
||||
[uxbox.migrations]
|
||||
[uxbox.media]
|
||||
|
@ -59,52 +62,29 @@
|
|||
|
||||
;; --- Users creation
|
||||
|
||||
(declare decode-user-row)
|
||||
(declare decode-page-row)
|
||||
|
||||
(defn create-user
|
||||
[conn i]
|
||||
(let [sql "insert into users (id, fullname, username, email, password, metadata, photo)
|
||||
values ($1, $2, $3, $4, $5, $6, '') returning *"]
|
||||
(-> (db/query-one conn [sql
|
||||
(mk-uuid "user" i)
|
||||
(str "User " i)
|
||||
(str "user" i)
|
||||
(str "user" i ".test@uxbox.io")
|
||||
(hashers/encrypt "123123")
|
||||
(blob/encode {})])
|
||||
(p/then' decode-user-row))))
|
||||
(profiles/create-profile conn {:id (mk-uuid "user" i)
|
||||
:fullname (str "User " i)
|
||||
:username (str "user" i)
|
||||
:email (str "user" i ".test@uxbox.io")
|
||||
:password "123123"
|
||||
:metadata {}}))
|
||||
|
||||
(defn create-project
|
||||
[conn uid i]
|
||||
(let [sql "insert into projects (id, user_id, name)
|
||||
values ($1, $2, $3) returning *"
|
||||
name (str "sample project " i)]
|
||||
(db/query-one conn [sql (mk-uuid "project" i) uid name])))
|
||||
[conn user-id i]
|
||||
(projects/create-project conn {:id (mk-uuid "project" i)
|
||||
:user user-id
|
||||
:name (str "sample project " i)}))
|
||||
|
||||
(defn create-page
|
||||
[conn uid pid i]
|
||||
(let [sql "insert into pages (id, user_id, project_id, name, data, metadata)
|
||||
values ($1, $2, $3, $4, $5, $6) returning *"
|
||||
data (blob/encode {:shapes []})
|
||||
mdata (blob/encode {})
|
||||
name (str "page" i)
|
||||
id (mk-uuid "page" i)]
|
||||
(-> (db/query-one conn [sql id uid pid name data mdata])
|
||||
(p/then' decode-page-row))))
|
||||
|
||||
(defn- decode-page-row
|
||||
[{:keys [data metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
data (assoc :data (blob/decode data))
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
|
||||
(defn- decode-user-row
|
||||
[{:keys [metadata] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
metadata (assoc :metadata (blob/decode metadata)))))
|
||||
(pages/create-page conn {:id (mk-uuid "page" i)
|
||||
:user uid
|
||||
:project-id pid
|
||||
:name (str "page" i)
|
||||
:data {:shapes []}
|
||||
:metadata {}}))
|
||||
|
||||
(defn handle-error
|
||||
[err]
|
||||
|
@ -152,10 +132,9 @@
|
|||
(do
|
||||
(println "====> START ERROR")
|
||||
(if (= :spec-validation (:code error))
|
||||
(do
|
||||
(s/explain-out (:data error))
|
||||
(prn error))
|
||||
(println "====> END ERROR"))
|
||||
(prn error)))
|
||||
(do
|
||||
(println "====> START RESPONSE")
|
||||
(prn result)
|
||||
|
|
|
@ -11,14 +11,13 @@
|
|||
[mockery.core :refer [with-mock]]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.emails :as emails]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest register-email-rendering
|
||||
(let [result (emails/render emails/register {:to "example@uxbox.io"})]
|
||||
(let [result (emails/render emails/register {:to "example@uxbox.io" :name "foo"})]
|
||||
(t/is (map? result))
|
||||
(t/is (contains? result :subject))
|
||||
(t/is (contains? result :body))
|
||||
|
@ -27,7 +26,7 @@
|
|||
(t/is (vector? (:body result)))))
|
||||
|
||||
(t/deftest email-sending-and-sendmail-job
|
||||
(let [res @(emails/send! emails/register {:to "example@uxbox.io"})]
|
||||
(let [res @(emails/send! emails/register {:to "example@uxbox.io" :name "foo"})]
|
||||
(t/is (nil? res)))
|
||||
(with-mock mock
|
||||
{:target 'uxbox.jobs.sendmail/impl-sendmail
|
||||
|
|
|
@ -8,35 +8,34 @@
|
|||
(:require
|
||||
[clojure.test :as t]
|
||||
[promesa.core :as p]
|
||||
[buddy.hashers :as hashers]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest test-failed-auth
|
||||
(t/deftest failed-auth
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
event {:username "user1"
|
||||
:type :login
|
||||
::sm/type :login
|
||||
:password "foobar"
|
||||
:metadata "1"
|
||||
:scope "foobar"}
|
||||
[err res] (th/try-on
|
||||
(sv/mutation event))]
|
||||
(t/is (nil? res))
|
||||
(t/is (= (:type err) :validation))
|
||||
(t/is (= (:code err) :uxbox.services.auth/wrong-credentials))))
|
||||
out (th/try-on! (sm/handle event))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (map? (:error out)))
|
||||
(t/is (= (get-in out [:error :type]) :validation))
|
||||
(t/is (= (get-in out [:error :code]) :uxbox.services.mutations.auth/wrong-credentials))))
|
||||
|
||||
(t/deftest test-success-auth
|
||||
(t/deftest success-auth
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
event {:username "user1"
|
||||
:type :login
|
||||
::sm/type :login
|
||||
:password "123123"
|
||||
:metadata "1"
|
||||
:scope "foobar"}
|
||||
[err res] (th/try-on
|
||||
(sv/mutation event))]
|
||||
(t/is (= res (:id user)))
|
||||
(t/is (nil? err))))
|
||||
out (th/try-on! (sm/handle event))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= (get-in out [:result :id]) (:id user)))))
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
(ns uxbox.tests.test-services-kvstore
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.test :as t]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.http :as http]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest test-mutation-upsert-kvstore
|
||||
(let [{:keys [id] :as user} @(th/create-user db/pool 1)]
|
||||
(let [out (th/try-on! (sv/query {::sv/type :kvstore-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
(let [out (th/try-on! (sv/mutation {::sv/type :upsert-kvstore
|
||||
:user id
|
||||
:key "foobar"
|
||||
:value {:some #{:value}}}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
(let [out (th/try-on! (sv/query {::sv/type :kvstore-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= {:some #{:value}} (get-in out [:result :value])))
|
||||
(t/is (= "foobar" (get-in out [:result :key]))))
|
||||
|
||||
(let [out (th/try-on! (sv/mutation {::sv/type :delete-kvstore
|
||||
:user id
|
||||
:key "foobar"}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
(let [out (th/try-on! (sv/query {::sv/type :kvstore-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))))
|
||||
|
|
@ -5,7 +5,8 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.http :as http]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
|
@ -14,13 +15,13 @@
|
|||
(t/deftest mutation-create-page
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
data {::sv/type :create-page
|
||||
data {::sm/type :create-page
|
||||
:data {:shapes []}
|
||||
:metadata {}
|
||||
:project-id (:id proj)
|
||||
:name "test page"
|
||||
:user (:id user)}
|
||||
res (th/try-on! (sv/mutation data))]
|
||||
res (th/try-on! (sm/handle data))]
|
||||
(t/is (nil? (:error res)))
|
||||
(t/is (uuid? (get-in res [:result :id])))
|
||||
(let [rsp (:result res)]
|
||||
|
@ -33,35 +34,35 @@
|
|||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
page @(th/create-page db/pool (:id user) (:id proj) 1)
|
||||
data {::sv/type :update-page
|
||||
data {::sm/type :update-page
|
||||
:id (:id page)
|
||||
:data {:shapes [1 2 3]}
|
||||
:metadata {:foo 2}
|
||||
:project-id (:id proj)
|
||||
:name "test page"
|
||||
:user (:id user)}
|
||||
res (th/try-on! (sv/mutation data))]
|
||||
res (th/try-on! (sm/handle data))]
|
||||
|
||||
;; (th/print-result! res)
|
||||
|
||||
(t/is (nil? (:error res)))
|
||||
(t/is (= (:id data) (get-in res [:result :id])))
|
||||
(t/is (= (:user data) (get-in res [:result :user-id])))
|
||||
(t/is (= (:name data) (get-in res [:result :name])))
|
||||
(t/is (= (:data data) (get-in res [:result :data])))
|
||||
(t/is (= (:metadata data) (get-in res [:result :metadata])))))
|
||||
#_(t/is (= (:user data) (get-in res [:result :user-id])))
|
||||
#_(t/is (= (:name data) (get-in res [:result :name])))
|
||||
#_(t/is (= (:data data) (get-in res [:result :data])))
|
||||
#_(t/is (= (:metadata data) (get-in res [:result :metadata])))))
|
||||
|
||||
(t/deftest mutation-update-page-metadata
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
page @(th/create-page db/pool (:id user) (:id proj) 1)
|
||||
data {::sv/type :update-page-metadata
|
||||
data {::sm/type :update-page-metadata
|
||||
:id (:id page)
|
||||
:metadata {:foo 2}
|
||||
:project-id (:id proj)
|
||||
:name "test page"
|
||||
:user (:id user)}
|
||||
res (th/try-on! (sv/mutation data))]
|
||||
res (th/try-on! (sm/handle data))]
|
||||
|
||||
;; (th/print-result! res)
|
||||
(t/is (nil? (:error res)))
|
||||
|
@ -74,10 +75,10 @@
|
|||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
page @(th/create-page db/pool (:id user) (:id proj) 1)
|
||||
data {::sv/type :delete-page
|
||||
data {::sm/type :delete-page
|
||||
:id (:id page)
|
||||
:user (:id user)}
|
||||
res (th/try-on! (sv/mutation data))]
|
||||
res (th/try-on! (sm/handle data))]
|
||||
|
||||
;; (th/print-result! res)
|
||||
(t/is (nil? (:error res)))
|
||||
|
@ -87,10 +88,10 @@
|
|||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
page @(th/create-page db/pool (:id user) (:id proj) 1)
|
||||
data {::sv/type :pages-by-project
|
||||
data {::sq/type :pages-by-project
|
||||
:project-id (:id proj)
|
||||
:user (:id user)}
|
||||
res (th/try-on! (sv/query data))]
|
||||
res (th/try-on! (sq/handle data))]
|
||||
|
||||
;; (th/print-result! res)
|
||||
(t/is (nil? (:error res)))
|
||||
|
|
|
@ -4,29 +4,35 @@
|
|||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.http :as http]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
;; TODO: migrate from try-on to try-on!
|
||||
|
||||
(t/deftest query-project-list
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
data {::sv/type :projects
|
||||
data {::sq/type :projects
|
||||
:user (:id user)}
|
||||
[err rsp] (th/try-on (sv/query data))]
|
||||
(t/is (nil? err))
|
||||
(t/is (= 1 (count rsp)))
|
||||
(t/is (= (:id proj) (get-in rsp [0 :id])))
|
||||
(t/is (= (:name proj (get-in rsp [0 :name]))))))
|
||||
out (th/try-on! (sq/handle data))]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= 1 (count (:result out))))
|
||||
(t/is (= (:id proj) (get-in out [:result 0 :id])))
|
||||
(t/is (= (:name proj) (get-in out [:result 0 :name])))))
|
||||
|
||||
(t/deftest mutation-create-project
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
data {::sv/type :create-project
|
||||
data {::sm/type :create-project
|
||||
:user (:id user)
|
||||
:name "test project"}
|
||||
[err rsp] (th/try-on (sv/mutation data))]
|
||||
[err rsp] (th/try-on (sm/handle data))]
|
||||
;; (prn "RESPONSE:" err rsp)
|
||||
(t/is (nil? err))
|
||||
(t/is (= (:user data) (:user-id rsp)))
|
||||
|
@ -35,11 +41,11 @@
|
|||
(t/deftest mutation-update-project
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
data {::sv/type :update-project
|
||||
data {::sm/type :update-project
|
||||
:id (:id proj)
|
||||
:name "test project mod"
|
||||
:user (:id user)}
|
||||
[err rsp] (th/try-on (sv/mutation data))]
|
||||
[err rsp] (th/try-on (sm/handle data))]
|
||||
;; (prn "RESPONSE:" err rsp)
|
||||
(t/is (nil? err))
|
||||
(t/is (= (:id data) (:id rsp)))
|
||||
|
@ -49,10 +55,10 @@
|
|||
(t/deftest mutation-delete-project
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
proj @(th/create-project db/pool (:id user) 1)
|
||||
data {::sv/type :delete-project
|
||||
data {::sm/type :delete-project
|
||||
:id (:id proj)
|
||||
:user (:id user)}
|
||||
[err rsp] (th/try-on (sv/mutation data))]
|
||||
[err rsp] (th/try-on (sm/handle data))]
|
||||
;; (prn "RESPONSE:" err rsp)
|
||||
(t/is (nil? err))
|
||||
(t/is (nil? rsp))
|
||||
|
|
54
backend/test/uxbox/tests/test_services_user_storage.clj
Normal file
54
backend/test/uxbox/tests/test_services_user_storage.clj
Normal file
|
@ -0,0 +1,54 @@
|
|||
(ns uxbox.tests.test-services-user-storage
|
||||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.test :as t]
|
||||
[promesa.core :as p]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.http :as http]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest test-user-storage
|
||||
(let [{:keys [id] :as user} @(th/create-user db/pool 1)]
|
||||
(let [out (th/try-on! (sq/handle {::sq/type :user-storage-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (map? (:error out)))
|
||||
(t/is (= :not-found (get-in out [:error :type]))))
|
||||
|
||||
(let [out (th/try-on! (sm/handle {::sm/type :upsert-user-storage-entry
|
||||
:user id
|
||||
:key "foobar"
|
||||
:val {:some #{:value}}}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
(let [out (th/try-on! (sq/handle {::sq/type :user-storage-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= {:some #{:value}} (get-in out [:result :val])))
|
||||
(t/is (= "foobar" (get-in out [:result :key]))))
|
||||
|
||||
(let [out (th/try-on! (sm/handle {::sm/type :delete-user-storage-entry
|
||||
:user id
|
||||
:key "foobar"}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out))))
|
||||
|
||||
(let [out (th/try-on! (sq/handle {::sq/type :user-storage-entry
|
||||
:key "foobar"
|
||||
:user id}))]
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (map? (:error out)))
|
||||
(t/is (= :not-found (get-in out [:error :type]))))))
|
||||
|
|
@ -5,10 +5,10 @@
|
|||
[promesa.core :as p]
|
||||
[cuerdas.core :as str]
|
||||
[datoteka.core :as fs]
|
||||
;; [buddy.hashers :as hashers]
|
||||
[vertx.core :as vc]
|
||||
[uxbox.db :as db]
|
||||
[uxbox.services.core :as sv]
|
||||
[uxbox.services.mutations :as sm]
|
||||
[uxbox.services.queries :as sq]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/use-fixtures :once th/state-init)
|
||||
|
@ -16,9 +16,9 @@
|
|||
|
||||
(t/deftest test-query-profile
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
event {::sv/type :profile
|
||||
event {::sq/type :profile
|
||||
:user (:id user)}
|
||||
[err rsp] (th/try-on (sv/query event))]
|
||||
[err rsp] (th/try-on (sq/handle event))]
|
||||
;; (println "RESPONSE:" resp)))
|
||||
(t/is (nil? err))
|
||||
(t/is (= (:fullname rsp) "User 1"))
|
||||
|
@ -30,12 +30,12 @@
|
|||
(t/deftest test-mutation-update-profile
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
event (assoc user
|
||||
::sv/type :update-profile
|
||||
::sm/type :update-profile
|
||||
:fullname "Full Name"
|
||||
:username "user222"
|
||||
:metadata {:foo "bar"}
|
||||
:email "user222@uxbox.io")
|
||||
[err data] (th/try-on (sv/mutation event))]
|
||||
[err data] (th/try-on (sm/handle event))]
|
||||
;; (println "RESPONSE:" err data)
|
||||
(t/is (nil? err))
|
||||
(t/is (= (:fullname data) "Full Name"))
|
||||
|
@ -46,13 +46,13 @@
|
|||
|
||||
(t/deftest test-mutation-update-profile-photo
|
||||
(let [user @(th/create-user db/pool 1)
|
||||
event {::sv/type :update-profile-photo
|
||||
event {::sm/type :update-profile-photo
|
||||
:user (:id user)
|
||||
:file {:name "sample.jpg"
|
||||
:path (fs/path "test/uxbox/tests/_files/sample.jpg")
|
||||
:size 123123
|
||||
:mtype "image/jpeg"}}
|
||||
[err rsp] (th/try-on (sv/mutation event))]
|
||||
[err rsp] (th/try-on (sm/handle event))]
|
||||
;; (prn "RESPONSE:" [err rsp])
|
||||
(t/is (nil? err))
|
||||
(t/is (= (:id user) (:id rsp)))
|
||||
|
@ -64,7 +64,7 @@
|
|||
;; :email "user222@uxbox.io"
|
||||
;; :password "user222"
|
||||
;; ::sv/type :register-profile}
|
||||
;; [err rsp] (th/try-on (sv/mutation data))]
|
||||
;; [err rsp] (th/try-on (sm/handle data))]
|
||||
;; (println "RESPONSE:" err rsp)))
|
||||
|
||||
;; ;; (t/deftest test-http-validate-recovery-token
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue