From bd7114182f2b3ab073f094400a48e59cdf407023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Wed, 5 Aug 2020 14:31:08 +0200 Subject: [PATCH] :recycle: The big media refactor (backend) --- .../0014-refactor-media-storage.sql | 46 ++ backend/src/uxbox/emails.clj | 1 - backend/src/uxbox/fixtures.clj | 1 - backend/src/uxbox/images.clj | 218 ---------- backend/src/uxbox/main.clj | 2 +- backend/src/uxbox/media.clj | 218 +++++++++- backend/src/uxbox/media_loader.clj | 28 +- backend/src/uxbox/media_storage.clj | 36 ++ backend/src/uxbox/migrations.clj | 6 +- backend/src/uxbox/services/init.clj | 8 +- .../src/uxbox/services/mutations/colors.clj | 183 ++++---- .../src/uxbox/services/mutations/files.clj | 280 ++++++------- .../src/uxbox/services/mutations/icons.clj | 394 +++++++++--------- .../src/uxbox/services/mutations/images.clj | 273 ------------ .../src/uxbox/services/mutations/media.clj | 283 +++++++++++++ .../src/uxbox/services/mutations/profile.clj | 20 +- backend/src/uxbox/services/queries/colors.clj | 126 +++--- backend/src/uxbox/services/queries/files.clj | 56 +-- backend/src/uxbox/services/queries/icons.clj | 292 ++++++------- backend/src/uxbox/services/queries/images.clj | 144 ------- backend/src/uxbox/services/queries/media.clj | 154 +++++++ .../src/uxbox/services/queries/profile.clj | 4 +- backend/src/uxbox/services/queries/viewer.clj | 5 +- backend/src/uxbox/tasks/gc.clj | 6 +- backend/src/uxbox/tasks/remove_media.clj | 6 +- backend/tests/uxbox/tests/helpers.clj | 51 ++- .../uxbox/tests/test_services_colors.clj | 127 +++--- .../tests/uxbox/tests/test_services_files.clj | 200 ++++----- .../tests/uxbox/tests/test_services_icons.clj | 318 +++++++------- .../uxbox/tests/test_services_images.clj | 237 ----------- .../tests/uxbox/tests/test_services_media.clj | 242 +++++++++++ .../tests/uxbox/tests/test_services_pages.clj | 6 +- .../uxbox/tests/test_services_viewer.clj | 3 +- 33 files changed, 2037 insertions(+), 1937 deletions(-) create mode 100644 backend/resources/migrations/0014-refactor-media-storage.sql delete mode 100644 backend/src/uxbox/images.clj create mode 100644 backend/src/uxbox/media_storage.clj delete mode 100644 backend/src/uxbox/services/mutations/images.clj create mode 100644 backend/src/uxbox/services/mutations/media.clj delete mode 100644 backend/src/uxbox/services/queries/images.clj create mode 100644 backend/src/uxbox/services/queries/media.clj delete mode 100644 backend/tests/uxbox/tests/test_services_images.clj create mode 100644 backend/tests/uxbox/tests/test_services_media.clj diff --git a/backend/resources/migrations/0014-refactor-media-storage.sql b/backend/resources/migrations/0014-refactor-media-storage.sql new file mode 100644 index 000000000..3d531b407 --- /dev/null +++ b/backend/resources/migrations/0014-refactor-media-storage.sql @@ -0,0 +1,46 @@ +ALTER TABLE image + RENAME TO media_object; + +ALTER TABLE media_object + ADD COLUMN is_local boolean NOT NULL DEFAULT false; + +INSERT INTO media_object + (id, file_id, created_at, modified_at, deleted_at, name, path, + width, height, mtype, thumb_path, thumb_width, thumb_height, + thumb_quality, thumb_mtype, is_local) + (SELECT id, file_id, created_at, modified_at, deleted_at, name, path, + width, height, mtype, thumb_path, thumb_width, thumb_height, + thumb_quality, thumb_mtype, true + FROM file_image); + +CREATE TABLE media_thumbnail ( + id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + media_object_id uuid NOT NULL REFERENCES media_object(id) ON DELETE CASCADE, + mtype text NOT NULL, + path text NOT NULL, + width int NOT NULL, + height int NOT NULL, + quality int NOT NULL +); + +CREATE INDEX media_thumbnail__media_object_id__idx + ON media_thumbnail(media_object_id); + +INSERT INTO media_thumbnail + (media_object_id, mtype, path, width, height, quality) + (SELECT id, thumb_mtype, thumb_path, thumb_width, thumb_height, thumb_quality + FROM media_object); + +ALTER TABLE media_object + DROP COLUMN thumb_mtype, + DROP COLUMN thumb_path, + DROP COLUMN thumb_width, + DROP COLUMN thumb_height, + DROP COLUMN thumb_quality; + +DROP TABLE color_library; +DROP TABLE icon; +DROP TABLE icon_library; +DROP TABLE image_library; +DROP TABLE file_image; + diff --git a/backend/src/uxbox/emails.clj b/backend/src/uxbox/emails.clj index 5b48d5eb1..b276ca901 100644 --- a/backend/src/uxbox/emails.clj +++ b/backend/src/uxbox/emails.clj @@ -17,7 +17,6 @@ [uxbox.common.spec :as us] [uxbox.db :as db] [uxbox.tasks :as tasks] - [uxbox.media :as media] [uxbox.util.emails :as emails])) ;; --- Defaults diff --git a/backend/src/uxbox/fixtures.clj b/backend/src/uxbox/fixtures.clj index a928c27c8..3d6dcd5dc 100644 --- a/backend/src/uxbox/fixtures.clj +++ b/backend/src/uxbox/fixtures.clj @@ -15,7 +15,6 @@ [uxbox.common.uuid :as uuid] [uxbox.config :as cfg] [uxbox.db :as db] - [uxbox.media :as media] [uxbox.migrations] [uxbox.services.mutations.profile :as profile] [uxbox.util.blob :as blob])) diff --git a/backend/src/uxbox/images.clj b/backend/src/uxbox/images.clj deleted file mode 100644 index 7b211debf..000000000 --- a/backend/src/uxbox/images.clj +++ /dev/null @@ -1,218 +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/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2020 UXBOX Labs SL - -(ns uxbox.images - "Image postprocessing." - (:require - [clojure.core.async :as a] - [clojure.java.io :as io] - [clojure.spec.alpha :as s] - [datoteka.core :as fs] - [mount.core :refer [defstate]] - [uxbox.config :as cfg] - [uxbox.common.data :as d] - [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.media :as media] - [uxbox.util.storage :as ust] - [uxbox.util.http :as http]) - (:import - java.io.ByteArrayInputStream - java.io.InputStream - java.util.concurrent.Semaphore - org.im4java.core.ConvertCmd - org.im4java.core.Info - org.im4java.core.IMOperation)) - -(defstate semaphore - :start (Semaphore. (:image-process-max-threads cfg/config 1))) - -;; --- Thumbnails Generation - -(s/def ::cmd keyword?) - -(s/def ::path (s/or :path fs/path? - :string string? - :file fs/file?)) -(s/def ::mtype string?) - -(s/def ::input - (s/keys :req-un [::path] - :opt-un [::mtype])) - -(s/def ::width integer?) -(s/def ::height integer?) -(s/def ::format #{:jpeg :webp :png :svg}) -(s/def ::quality #(< 0 % 101)) - -(s/def ::thumbnail-params - (s/keys :req-un [::cmd ::input ::format ::width ::height])) - -;; Related info on how thumbnails generation -;; http://www.imagemagick.org/Usage/thumbnails/ - -(defn format->extension - [format] - (case format - :png ".png" - :jpeg ".jpg" - :webp ".webp" - :svg ".svg")) - -(defn format->mtype - [format] - (case format - :png "image/png" - :jpeg "image/jpeg" - :webp "image/webp" - :svg "image/svg+xml")) - -(defn mtype->format - [mtype] - (case mtype - "image/png" :png - "image/jpeg" :jpeg - "image/webp" :webp - "image/svg+xml" :svg - nil)) - -(defn- generic-process - [{:keys [input format quality operation] :as params}] - (let [{:keys [path mtype]} input - format (or (mtype->format mtype) format) - ext (format->extension format) - tmp (fs/create-tempfile :suffix ext)] - - (doto (ConvertCmd.) - (.run operation (into-array (map str [path tmp])))) - - (let [thumbnail-data (fs/slurp-bytes tmp)] - (fs/delete tmp) - (assoc params - :format format - :mtype (format->mtype format) - :data (ByteArrayInputStream. thumbnail-data))))) - -(defmulti process :cmd) - -(defmethod process :generic-thumbnail - [{:keys [quality width height] :as params}] - (us/assert ::thumbnail-params params) - (let [op (doto (IMOperation.) - (.addImage) - (.autoOrient) - (.strip) - (.thumbnail (int width) (int height) ">") - (.quality (double quality)) - (.addImage))] - (generic-process (assoc params :operation op)))) - -(defmethod process :profile-thumbnail - [{:keys [quality width height] :as params}] - (us/assert ::thumbnail-params params) - (let [op (doto (IMOperation.) - (.addImage) - (.autoOrient) - (.strip) - (.thumbnail (int width) (int height) "^") - (.gravity "center") - (.extent (int width) (int height)) - (.quality (double quality)) - (.addImage))] - (generic-process (assoc params :operation op)))) - -(defmethod process :info - [{:keys [input] :as params}] - (us/assert ::input input) - (let [{:keys [path mtype]} input] - (if (= mtype "image/svg+xml") - {:width 100 - :height 100 - :mtype mtype} - (let [instance (Info. (str path)) - mtype' (.getProperty instance "Mime type")] - (when (and (string? mtype) - (not= mtype mtype')) - (ex/raise :type :validation - :code :image-type-mismatch - :hint "Seems like you are uploading a file whose content does not match the extension.")) - {:width (.getImageWidth instance) - :height (.getImageHeight instance) - :mtype mtype'})))) - -(defmethod process :default - [{:keys [cmd] :as params}] - (ex/raise :type :internal - :code :not-implemented - :hint (str "No impl found for process cmd:" cmd))) - -(defn run - [params] - (try - (.acquire semaphore) - (let [res (a/format content-type)] - (if (nil? format) - (ex/raise :type :validation - :code :image-type-not-allowed - :hint "Seems like the url points to an invalid image.") - (let [tempfile (fs/create-tempfile) - base-filename (first (fs/split-ext (fs/name tempfile))) - filename (str base-filename (format->extension format))] - (with-open [ostream (io/output-stream tempfile)] - (.write ostream data)) - {:filename filename - :size (count data) - :tempfile tempfile - :content-type content-type})))) - diff --git a/backend/src/uxbox/main.clj b/backend/src/uxbox/main.clj index e61e619b6..a41708647 100644 --- a/backend/src/uxbox/main.clj +++ b/backend/src/uxbox/main.clj @@ -28,7 +28,7 @@ [& args] (require 'uxbox.config 'uxbox.migrations - 'uxbox.images + 'uxbox.media 'uxbox.http 'uxbox.tasks) (mount/start)) diff --git a/backend/src/uxbox/media.clj b/backend/src/uxbox/media.clj index 8a6b3e630..e277c7499 100644 --- a/backend/src/uxbox/media.clj +++ b/backend/src/uxbox/media.clj @@ -5,32 +5,214 @@ ;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; defined by the Mozilla Public License, v. 2.0. ;; -;; Copyright (c) 2017-2020 Andrey Antukh +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.media - "A media storage impl for uxbox." + "Media postprocessing." (:require - [mount.core :refer [defstate]] + [clojure.core.async :as a] [clojure.java.io :as io] - [cuerdas.core :as str] + [clojure.spec.alpha :as s] [datoteka.core :as fs] + [mount.core :refer [defstate]] + [uxbox.config :as cfg] + [uxbox.common.data :as d] + [uxbox.common.exceptions :as ex] + [uxbox.common.spec :as us] + [uxbox.media-storage :as mst] [uxbox.util.storage :as ust] - [uxbox.config :refer [config]])) + [uxbox.util.http :as http]) + (:import + java.io.ByteArrayInputStream + java.io.InputStream + java.util.concurrent.Semaphore + org.im4java.core.ConvertCmd + org.im4java.core.Info + org.im4java.core.IMOperation)) -;; --- State +(defstate semaphore + :start (Semaphore. (:image-process-max-threads cfg/config 1))) -(defstate assets-storage - :start (ust/create {:base-path (:assets-directory config) - :base-uri (:assets-uri config)})) +;; --- Thumbnails Generation -(defstate media-storage - :start (ust/create {:base-path (:media-directory config) - :base-uri (:media-uri config) - :xf (comp ust/random-path - ust/slugify-filename)})) +(s/def ::cmd keyword?) -;; --- Public Api +(s/def ::path (s/or :path fs/path? + :string string? + :file fs/file?)) +(s/def ::mtype string?) + +(s/def ::input + (s/keys :req-un [::path] + :opt-un [::mtype])) + +(s/def ::width integer?) +(s/def ::height integer?) +(s/def ::format #{:jpeg :webp :png :svg}) +(s/def ::quality #(< 0 % 101)) + +(s/def ::thumbnail-params + (s/keys :req-un [::cmd ::input ::format ::width ::height])) + +;; Related info on how thumbnails generation +;; http://www.imagemagick.org/Usage/thumbnails/ + +(defn format->extension + [format] + (case format + :png ".png" + :jpeg ".jpg" + :webp ".webp" + :svg ".svg")) + +(defn format->mtype + [format] + (case format + :png "image/png" + :jpeg "image/jpeg" + :webp "image/webp" + :svg "image/svg+xml")) + +(defn mtype->format + [mtype] + (case mtype + "image/png" :png + "image/jpeg" :jpeg + "image/webp" :webp + "image/svg+xml" :svg + nil)) + +(defn- generic-process + [{:keys [input format quality operation] :as params}] + (let [{:keys [path mtype]} input + format (or (mtype->format mtype) format) + ext (format->extension format) + tmp (fs/create-tempfile :suffix ext)] + + (doto (ConvertCmd.) + (.run operation (into-array (map str [path tmp])))) + + (let [thumbnail-data (fs/slurp-bytes tmp)] + (fs/delete tmp) + (assoc params + :format format + :mtype (format->mtype format) + :data (ByteArrayInputStream. thumbnail-data))))) + +(defmulti process :cmd) + +(defmethod process :generic-thumbnail + [{:keys [quality width height] :as params}] + (us/assert ::thumbnail-params params) + (let [op (doto (IMOperation.) + (.addImage) + (.autoOrient) + (.strip) + (.thumbnail (int width) (int height) ">") + (.quality (double quality)) + (.addImage))] + (generic-process (assoc params :operation op)))) + +(defmethod process :profile-thumbnail + [{:keys [quality width height] :as params}] + (us/assert ::thumbnail-params params) + (let [op (doto (IMOperation.) + (.addImage) + (.autoOrient) + (.strip) + (.thumbnail (int width) (int height) "^") + (.gravity "center") + (.extent (int width) (int height)) + (.quality (double quality)) + (.addImage))] + (generic-process (assoc params :operation op)))) + +(defmethod process :info + [{:keys [input] :as params}] + (us/assert ::input input) + (let [{:keys [path mtype]} input] + (if (= mtype "image/svg+xml") + {:width 100 + :height 100 + :mtype mtype} + (let [instance (Info. (str path)) + mtype' (.getProperty instance "Mime type")] + (when (and (string? mtype) + (not= mtype mtype')) + (ex/raise :type :validation + :code :image-type-mismatch + :hint "Seems like you are uploading a file whose content does not match the extension.")) + {:width (.getImageWidth instance) + :height (.getImageHeight instance) + :mtype mtype'})))) + +(defmethod process :default + [{:keys [cmd] :as params}] + (ex/raise :type :internal + :code :not-implemented + :hint (str "No impl found for process cmd:" cmd))) + +(defn run + [params] + (try + (.acquire semaphore) + (let [res (a/format content-type)] + (if (nil? format) + (ex/raise :type :validation + :code :media-type-not-allowed + :hint "Seems like the url points to an invalid media object.") + (let [tempfile (fs/create-tempfile) + base-filename (first (fs/split-ext (fs/name tempfile))) + filename (str base-filename (format->extension format))] + (with-open [ostream (io/output-stream tempfile)] + (.write ostream data)) + {:filename filename + :size (count data) + :tempfile tempfile + :content-type content-type})))) -(defn resolve-asset - [path] - (str (ust/public-uri assets-storage path))) diff --git a/backend/src/uxbox/media_loader.clj b/backend/src/uxbox/media_loader.clj index 5454862ae..02cb37ef1 100644 --- a/backend/src/uxbox/media_loader.clj +++ b/backend/src/uxbox/media_loader.clj @@ -23,7 +23,6 @@ [uxbox.db :as db] [uxbox.http] [uxbox.migrations] - [uxbox.media :as media] [uxbox.util.svg :as svg] [uxbox.util.transit :as t] [uxbox.util.blob :as blob] @@ -32,8 +31,7 @@ [uxbox.services.mutations.projects :as projects] [uxbox.services.mutations.files :as files] [uxbox.services.mutations.colors :as colors] - [uxbox.services.mutations.icons :as icons] - [uxbox.services.mutations.images :as images] + [uxbox.services.mutations.media :as media] [uxbox.util.storage :as ust]) (:import java.io.Reader @@ -164,7 +162,7 @@ ;; [conn {:keys [name] :as item}] ;; (let [id (uuid/namespaced +images-uuid-ns+ name)] ;; (log/info "Creating image library:" name) -;; (images/create-library conn {:id id +;; (media/create-library conn {:id id ;; :team-id uuid/zero ;; :name name}))) ;; @@ -188,7 +186,7 @@ ;; ".png" "image/png" ;; ".webp" "image/webp")] ;; (log/info "Creating image" filename image-id) -;; (images/create-image conn {:content {:tempfile localpath +;; (media/create-image conn {:content {:tempfile localpath ;; :filename filename ;; :content-type mtype ;; :size (.length file)} @@ -298,19 +296,19 @@ ".webp" "image/webp" ".svg" "image/svg+xml")] (log/info "Creating image" filename image-id) - (images/create-image conn {:content {:tempfile localpath - :filename filename - :content-type mtype - :size (.length file)} - :id image-id - :file-id file-id - :user uuid/zero - :name filename}))) + (media/create-media-object conn {:content {:tempfile localpath + :filename filename + :content-type mtype + :size (.length file)} + :id image-id + :file-id file-id + :name filename + :is-local false}))) (defn- image-exists? [conn id] (s/assert ::us/uuid id) - (let [row (db/get-by-id conn :image id)] + (let [row (db/get-by-id conn :media-object id)] (if row true false))) (defn- import-image-if-not-exists @@ -355,7 +353,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Libraries Importer +;; Library files Importer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn- library-file-exists? diff --git a/backend/src/uxbox/media_storage.clj b/backend/src/uxbox/media_storage.clj new file mode 100644 index 000000000..e260e98ff --- /dev/null +++ b/backend/src/uxbox/media_storage.clj @@ -0,0 +1,36 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2017-2020 Andrey Antukh + +(ns uxbox.media-storage + "A media storage impl for uxbox." + (:require + [mount.core :refer [defstate]] + [clojure.java.io :as io] + [cuerdas.core :as str] + [datoteka.core :as fs] + [uxbox.util.storage :as ust] + [uxbox.config :refer [config]])) + +;; --- State + +(defstate assets-storage + :start (ust/create {:base-path (:assets-directory config) + :base-uri (:assets-uri config)})) + +(defstate media-storage + :start (ust/create {:base-path (:media-directory config) + :base-uri (:media-uri config) + :xf (comp ust/random-path + ust/slugify-filename)})) + +;; --- Public Api + +(defn resolve-asset + [path] + (str (ust/public-uri assets-storage path))) diff --git a/backend/src/uxbox/migrations.clj b/backend/src/uxbox/migrations.clj index 687245115..4907a1dc0 100644 --- a/backend/src/uxbox/migrations.clj +++ b/backend/src/uxbox/migrations.clj @@ -67,7 +67,11 @@ {:desc "Mark files shareable" :name "0013-mark-files-shareable" - :fn (mg/resource "migrations/0013-mark-files-shareable.sql")}]}) + :fn (mg/resource "migrations/0013-mark-files-shareable.sql")} + + {:desc "Refactor media storage" + :name "0014-refactor-media-storage.sql" + :fn (mg/resource "migrations/0014-refactor-media-storage.sql")}]}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Entry point diff --git a/backend/src/uxbox/services/init.clj b/backend/src/uxbox/services/init.clj index c83b9e931..45321608e 100644 --- a/backend/src/uxbox/services/init.clj +++ b/backend/src/uxbox/services/init.clj @@ -14,8 +14,8 @@ (defn- load-query-services [] - (require 'uxbox.services.queries.icons) - (require 'uxbox.services.queries.images) + ;; (require 'uxbox.services.queries.icons) + (require 'uxbox.services.queries.media) (require 'uxbox.services.queries.colors) (require 'uxbox.services.queries.projects) (require 'uxbox.services.queries.files) @@ -28,8 +28,8 @@ (defn- load-mutation-services [] (require 'uxbox.services.mutations.demo) - (require 'uxbox.services.mutations.icons) - (require 'uxbox.services.mutations.images) + ;; (require 'uxbox.services.mutations.icons) + (require 'uxbox.services.mutations.media) (require 'uxbox.services.mutations.colors) (require 'uxbox.services.mutations.projects) (require 'uxbox.services.mutations.files) diff --git a/backend/src/uxbox/services/mutations/colors.clj b/backend/src/uxbox/services/mutations/colors.clj index ee6143027..f73c6bc5b 100644 --- a/backend/src/uxbox/services/mutations/colors.clj +++ b/backend/src/uxbox/services/mutations/colors.clj @@ -30,92 +30,93 @@ (s/def ::content ::us/string) -;; --- Mutation: Create Library +;; ;; --- Mutation: Create Library +;; +;; (declare create-library) +;; +;; (s/def ::create-color-library +;; (s/keys :req-un [::profile-id ::team-id ::name] +;; :opt-un [::id])) +;; +;; (sm/defmutation ::create-color-library +;; [{:keys [profile-id team-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-edition-permissions! conn profile-id team-id) +;; (create-library conn params))) +;; +;; (defn create-library +;; [conn {:keys [id team-id name]}] +;; (let [id (or id (uuid/next))] +;; (db/insert! conn :color-library +;; {:id id +;; :team-id team-id +;; :name name}))) +;; -(declare create-library) - -(s/def ::create-color-library - (s/keys :req-un [::profile-id ::team-id ::name] - :opt-un [::id])) - -(sm/defmutation ::create-color-library - [{:keys [profile-id team-id] :as params}] - (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) - (create-library conn params))) - -(defn create-library - [conn {:keys [id team-id name]}] - (let [id (or id (uuid/next))] - (db/insert! conn :color-library - {:id id - :team-id team-id - :name name}))) +;; ;; --- Mutation: Rename Library +;; +;; (declare select-library-for-update) +;; (declare rename-library) +;; +;; (s/def ::rename-color-library +;; (s/keys :req-un [::profile-id ::name ::id])) +;; +;; (sm/defmutation ::rename-color-library +;; [{:keys [id profile-id name] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-library-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; (rename-library conn id name)))) +;; +;; (def ^:private sql:select-library-for-update +;; "select l.* +;; from color_library as l +;; where l.id = $1 +;; for update") +;; +;; (def ^:private sql:rename-library +;; "update color_library +;; set name = $2 +;; where id = $1") +;; +;; (defn- select-library-for-update +;; [conn id] +;; (db/get-by-id conn :color-library id {:for-update true})) +;; +;; (defn- rename-library +;; [conn id name] +;; (db/update! conn :color-library +;; {:name name} +;; {:id id})) -;; --- Mutation: Rename Library - -(declare select-library-for-update) -(declare rename-library) - -(s/def ::rename-color-library - (s/keys :req-un [::profile-id ::name ::id])) - -(sm/defmutation ::rename-color-library - [{:keys [id profile-id name] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-library-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - (rename-library conn id name)))) - -(def ^:private sql:select-library-for-update - "select l.* - from color_library as l - where l.id = $1 - for update") - -(def ^:private sql:rename-library - "update color_library - set name = $2 - where id = $1") - -(defn- select-library-for-update - [conn id] - (db/get-by-id conn :color-library id {:for-update true})) - -(defn- rename-library - [conn id name] - (db/update! conn :color-library - {:name name} - {:id id})) +;; ;; --- Delete Library +;; +;; (declare delete-library) +;; +;; (s/def ::delete-color-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sm/defmutation ::delete-color-library +;; [{:keys [id profile-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-library-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; +;; ;; Schedule object deletion +;; (tasks/submit! conn {:name "delete-object" +;; :delay cfg/default-deletion-delay +;; :props {:id id :type :color-library}}) +;; +;; (db/update! conn :color-library +;; {:deleted-at (dt/now)} +;; {:id id}) +;; nil))) -;; --- Delete Library - -(declare delete-library) - -(s/def ::delete-color-library - (s/keys :req-un [::profile-id ::id])) - -(sm/defmutation ::delete-color-library - [{:keys [id profile-id] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-library-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id id :type :color-library}}) - - (db/update! conn :color-library - {:deleted-at (dt/now)} - {:id id}) - nil))) - - -;; --- Mutation: Create Color (Upload) +;; --- Mutation: Create Color +(declare select-file-for-update) (declare create-color) (s/def ::create-color @@ -125,10 +126,9 @@ (sm/defmutation ::create-color [{:keys [profile-id file-id] :as params}] (db/with-atomic [conn db/pool] - (create-color conn params))) - ;; (let [lib (select-library-for-update conn library-id)] - ;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) - ;; (create-color conn params)))) + (let [file (select-file-for-update conn file-id)] + (teams/check-edition-permissions! conn profile-id (:team-id file)) + (create-color conn params)))) (def ^:private sql:create-color "insert into color (id, name, file_id, content) @@ -142,6 +142,21 @@ :file-id file-id :content content}))) +(def ^:private sql:select-file-for-update + "select file.*, + project.team_id as team_id + from file + inner join project on (project.id = file.project_id) + where file.id = ? + for update of file") + +(defn- select-file-for-update + [conn id] + (let [row (db/exec-one! conn [sql:select-file-for-update id])] + (when-not row + (ex/raise :type :not-found)) + row)) + ;; --- Mutation: Rename Color diff --git a/backend/src/uxbox/services/mutations/files.clj b/backend/src/uxbox/services/mutations/files.clj index 2bef2d5a0..ec611a42d 100644 --- a/backend/src/uxbox/services/mutations/files.clj +++ b/backend/src/uxbox/services/mutations/files.clj @@ -18,10 +18,10 @@ [uxbox.common.uuid :as uuid] [uxbox.config :as cfg] [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.media :as media] + ;; [uxbox.images :as images] + ;; [uxbox.media :as media] [uxbox.services.mutations :as sm] - [uxbox.services.mutations.images :as imgs] + ;; [uxbox.services.mutations.images :as imgs] [uxbox.services.mutations.projects :as proj] [uxbox.services.queries.files :as files] [uxbox.tasks :as tasks] @@ -37,13 +37,14 @@ (s/def ::project-id ::us/uuid) (s/def ::url ::us/url) -;; --- Mutation: Create Project File +;; --- Mutation: Create File (declare create-file) (declare create-page) +(s/def ::is-shared boolean?) (s/def ::create-file - (s/keys :req-un [::profile-id ::name ::project-id] + (s/keys :req-un [::profile-id ::name ::project-id ::is-shared] :opt-un [::id])) (sm/defmutation ::create-file @@ -63,14 +64,13 @@ :can-edit true})) (defn create-file - [conn {:keys [id profile-id name project-id shared?] :as params}] + [conn {:keys [id profile-id name project-id is-shared] :as params}] (let [id (or id (uuid/next)) - shared? (or shared? false) file (db/insert! conn :file {:id id :project-id project-id :name name - :is-shared shared?})] + :is-shared is-shared})] (->> (assoc params :file-id id) (create-file-profile conn)) file)) @@ -153,135 +153,135 @@ nil) -;; --- Mutations: Create File Image (Upload and create from url) - -(declare create-file-image) - -(s/def ::file-id ::us/uuid) -(s/def ::image-id ::us/uuid) -(s/def ::content ::imgs/upload) - -(s/def ::add-file-image-from-url - (s/keys :req-un [::profile-id ::file-id ::url] - :opt-un [::id])) - -(s/def ::upload-file-image - (s/keys :req-un [::profile-id ::file-id ::name ::content] - :opt-un [::id])) - -(sm/defmutation ::add-file-image-from-url - [{:keys [profile-id file-id url] :as params}] - (db/with-atomic [conn db/pool] - (files/check-edition-permissions! conn profile-id file-id) - (let [content (images/download-image url) - params' (merge params {:content content - :name (:filename content)})] - (create-file-image conn params')))) - -(sm/defmutation ::upload-file-image - [{:keys [profile-id file-id] :as params}] - (db/with-atomic [conn db/pool] - (files/check-edition-permissions! conn profile-id file-id) - (create-file-image conn params))) - -(defn- create-file-image - [conn {:keys [content file-id name] :as params}] - (when-not (imgs/valid-image-types? (:content-type content)) - (ex/raise :type :validation - :code :image-type-not-allowed - :hint "Seems like you are uploading an invalid image.")) - - (let [info (images/run {:cmd :info :input {:path (:tempfile content) - :mtype (:content-type content)}}) - path (imgs/persist-image-on-fs content) - opts (assoc imgs/thumbnail-options - :input {:mtype (:mtype info) - :path path}) - thumb (if-not (= (:mtype info) "image/svg+xml") - (imgs/persist-image-thumbnail-on-fs opts) - (assoc info - :path path - :quality 0))] - - (-> (db/insert! conn :file-image - {:file-id file-id - :name name - :path (str path) - :width (:width info) - :height (:height info) - :mtype (:mtype info) - :thumb-path (str (:path thumb)) - :thumb-width (:width thumb) - :thumb-height (:height thumb) - :thumb-quality (:quality thumb) - :thumb-mtype (:mtype thumb)}) - (images/resolve-urls :path :uri) - (images/resolve-urls :thumb-path :thumb-uri)))) - - -;; --- Mutation: Delete File Image - -(declare mark-file-image-deleted) - -(s/def ::delete-file-image - (s/keys :req-un [::file-id ::image-id ::profile-id])) - -(sm/defmutation ::delete-file-image - [{:keys [file-id image-id profile-id] :as params}] - (db/with-atomic [conn db/pool] - (files/check-edition-permissions! conn profile-id file-id) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id image-id :type :file-image}}) - - (mark-file-image-deleted conn params))) - -(defn mark-file-image-deleted - [conn {:keys [image-id] :as params}] - (db/update! conn :file-image - {:deleted-at (dt/now)} - {:id image-id}) - nil) - - -;; --- Mutation: Import from collection - -(declare copy-image) -(declare import-image-to-file) - -(s/def ::import-image-to-file - (s/keys :req-un [::image-id ::file-id ::profile-id])) - -(sm/defmutation ::import-image-to-file - [{:keys [image-id file-id profile-id] :as params}] - (db/with-atomic [conn db/pool] - (files/check-edition-permissions! conn profile-id file-id) - (import-image-to-file conn params))) - -(defn- import-image-to-file - [conn {:keys [image-id file-id] :as params}] - (let [image (db/get-by-id conn :image image-id) - image-path (copy-image (:path image)) - thumb-path (copy-image (:thumb-path image))] - - (-> (db/insert! conn :file-image - {:file-id file-id - :name (:name image) - :path (str image-path) - :width (:width image) - :height (:height image) - :mtype (:mtype image) - :thumb-path (str thumb-path) - :thumb-width (:thumb-width image) - :thumb-height (:thumb-height image) - :thumb-quality (:thumb-quality image) - :thumb-mtype (:thumb-mtype image)}) - (images/resolve-urls :path :uri) - (images/resolve-urls :thumb-path :thumb-uri)))) - -(defn- copy-image - [path] - (let [image-path (ust/lookup media/media-storage path)] - (ust/save! media/media-storage (fs/name image-path) image-path))) +;; ;; --- Mutations: Create File Image (Upload and create from url) +;; +;; (declare create-file-image) +;; +;; (s/def ::file-id ::us/uuid) +;; (s/def ::image-id ::us/uuid) +;; (s/def ::content ::imgs/upload) +;; +;; (s/def ::add-file-image-from-url +;; (s/keys :req-un [::profile-id ::file-id ::url] +;; :opt-un [::id])) +;; +;; (s/def ::upload-file-image +;; (s/keys :req-un [::profile-id ::file-id ::name ::content] +;; :opt-un [::id])) +;; +;; (sm/defmutation ::add-file-image-from-url +;; [{:keys [profile-id file-id url] :as params}] +;; (db/with-atomic [conn db/pool] +;; (files/check-edition-permissions! conn profile-id file-id) +;; (let [content (images/download-image url) +;; params' (merge params {:content content +;; :name (:filename content)})] +;; (create-file-image conn params')))) +;; +;; (sm/defmutation ::upload-file-image +;; [{:keys [profile-id file-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (files/check-edition-permissions! conn profile-id file-id) +;; (create-file-image conn params))) +;; +;; (defn- create-file-image +;; [conn {:keys [content file-id name] :as params}] +;; (when-not (imgs/valid-image-types? (:content-type content)) +;; (ex/raise :type :validation +;; :code :image-type-not-allowed +;; :hint "Seems like you are uploading an invalid image.")) +;; +;; (let [info (images/run {:cmd :info :input {:path (:tempfile content) +;; :mtype (:content-type content)}}) +;; path (imgs/persist-image-on-fs content) +;; opts (assoc imgs/thumbnail-options +;; :input {:mtype (:mtype info) +;; :path path}) +;; thumb (if-not (= (:mtype info) "image/svg+xml") +;; (imgs/persist-image-thumbnail-on-fs opts) +;; (assoc info +;; :path path +;; :quality 0))] +;; +;; (-> (db/insert! conn :file-image +;; {:file-id file-id +;; :name name +;; :path (str path) +;; :width (:width info) +;; :height (:height info) +;; :mtype (:mtype info) +;; :thumb-path (str (:path thumb)) +;; :thumb-width (:width thumb) +;; :thumb-height (:height thumb) +;; :thumb-quality (:quality thumb) +;; :thumb-mtype (:mtype thumb)}) +;; (images/resolve-urls :path :uri) +;; (images/resolve-urls :thumb-path :thumb-uri)))) +;; +;; +;; ;; --- Mutation: Delete File Image +;; +;; (declare mark-file-image-deleted) +;; +;; (s/def ::delete-file-image +;; (s/keys :req-un [::file-id ::image-id ::profile-id])) +;; +;; (sm/defmutation ::delete-file-image +;; [{:keys [file-id image-id profile-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (files/check-edition-permissions! conn profile-id file-id) +;; +;; ;; Schedule object deletion +;; (tasks/submit! conn {:name "delete-object" +;; :delay cfg/default-deletion-delay +;; :props {:id image-id :type :file-image}}) +;; +;; (mark-file-image-deleted conn params))) +;; +;; (defn mark-file-image-deleted +;; [conn {:keys [image-id] :as params}] +;; (db/update! conn :file-image +;; {:deleted-at (dt/now)} +;; {:id image-id}) +;; nil) +;; +;; +;; ;; --- Mutation: Import from collection +;; +;; (declare copy-image) +;; (declare import-image-to-file) +;; +;; (s/def ::import-image-to-file +;; (s/keys :req-un [::image-id ::file-id ::profile-id])) +;; +;; (sm/defmutation ::import-image-to-file +;; [{:keys [image-id file-id profile-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (files/check-edition-permissions! conn profile-id file-id) +;; (import-image-to-file conn params))) +;; +;; (defn- import-image-to-file +;; [conn {:keys [image-id file-id] :as params}] +;; (let [image (db/get-by-id conn :image image-id) +;; image-path (copy-image (:path image)) +;; thumb-path (copy-image (:thumb-path image))] +;; +;; (-> (db/insert! conn :file-image +;; {:file-id file-id +;; :name (:name image) +;; :path (str image-path) +;; :width (:width image) +;; :height (:height image) +;; :mtype (:mtype image) +;; :thumb-path (str thumb-path) +;; :thumb-width (:thumb-width image) +;; :thumb-height (:thumb-height image) +;; :thumb-quality (:thumb-quality image) +;; :thumb-mtype (:thumb-mtype image)}) +;; (images/resolve-urls :path :uri) +;; (images/resolve-urls :thumb-path :thumb-uri)))) +;; +;; (defn- copy-image +;; [path] +;; (let [image-path (ust/lookup media/media-storage path)] +;; (ust/save! media/media-storage (fs/name image-path) image-path))) diff --git a/backend/src/uxbox/services/mutations/icons.clj b/backend/src/uxbox/services/mutations/icons.clj index 14e8c55f7..16f0d7cfc 100644 --- a/backend/src/uxbox/services/mutations/icons.clj +++ b/backend/src/uxbox/services/mutations/icons.clj @@ -7,200 +7,200 @@ ;; ;; Copyright (c) 2019-2020 Andrey Antukh -(ns uxbox.services.mutations.icons - (:require - [clojure.spec.alpha :as s] - [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.common.uuid :as uuid] - [uxbox.config :as cfg] - [uxbox.db :as db] - [uxbox.services.mutations :as sm] - [uxbox.services.queries.icons :refer [decode-row]] - [uxbox.services.queries.teams :as teams] - [uxbox.tasks :as tasks] - [uxbox.util.blob :as blob] - [uxbox.util.time :as dt])) - -;; --- Helpers & Specs - -(s/def ::height ::us/integer) -(s/def ::id ::us/uuid) -(s/def ::library-id ::us/uuid) -(s/def ::name ::us/string) -(s/def ::profile-id ::us/uuid) -(s/def ::team-id ::us/uuid) -(s/def ::width ::us/integer) - -(s/def ::view-box - (s/and (s/coll-of number?) - #(= 4 (count %)) - vector?)) - -(s/def ::content ::us/string) -(s/def ::mimetype ::us/string) - -(s/def ::metadata - (s/keys :opt-un [::width ::height ::view-box ::mimetype])) - - -;; --- Mutation: Create Library - -(declare create-library) - -(s/def ::create-icon-library - (s/keys :req-un [::profile-id ::team-id ::name] - :opt-un [::id])) - -(sm/defmutation ::create-icon-library - [{:keys [profile-id team-id id name] :as params}] - (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) - (create-library conn params))) - -(def ^:private sql:create-library - "insert into icon_library (id, team_id, name) - values ($1, $2, $3) - returning *;") - -(defn create-library - [conn {:keys [team-id id name] :as params}] - (let [id (or id (uuid/next))] - (db/insert! conn :icon-library - {:id id - :team-id team-id - :name name}))) - - -;; --- Mutation: Rename Library - -(declare select-library-for-update) -(declare rename-library) - -(s/def ::rename-icon-library - (s/keys :req-un [::profile-id ::name ::id])) - -(sm/defmutation ::rename-icon-library - [{:keys [id profile-id name] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-library-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - (rename-library conn id name)))) - -(defn- select-library-for-update - [conn id] - (db/get-by-id conn :icon-library id {:for-update true})) - -(defn- rename-library - [conn id name] - (db/update! conn :icon-library - {:name name} - {:id id})) - -;; --- Mutation: Delete Library - -(declare delete-library) - -(s/def ::delete-icon-library - (s/keys :req-un [::profile-id ::id])) - -(sm/defmutation ::delete-icon-library - [{:keys [profile-id id] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-library-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id id :type :icon-library}}) - - (db/update! conn :icon-library - {:deleted-at (dt/now)} - {:id id}) - nil))) - - -;; --- Mutation: Create Icon (Upload) - -(declare create-icon) - -(s/def ::create-icon - (s/keys :req-un [::profile-id ::name ::metadata ::content ::library-id] - :opt-un [::id])) - -(sm/defmutation ::create-icon - [{:keys [profile-id library-id] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-library-for-update conn library-id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - (create-icon conn params)))) - -(defn create-icon - [conn {:keys [id name library-id metadata content]}] - (let [id (or id (uuid/next))] - (-> (db/insert! conn :icon - {:id id - :name name - :library-id library-id - :content content - :metadata (blob/encode metadata)}) - (decode-row)))) - - -;; --- Mutation: Rename Icon - -(declare select-icon-for-update) -(declare rename-icon) - -(s/def ::rename-icon - (s/keys :req-un [::id ::profile-id ::name])) - -(sm/defmutation ::rename-icon - [{:keys [id profile-id name] :as params}] - (db/with-atomic [conn db/pool] - (let [icon (select-icon-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id icon)) - (db/update! conn :icon - {:name name} - {:id id})))) - -(def ^:private - sql:select-icon-for-update - "select i.*, - lib.team_id as team_id - from icon as i - inner join icon_library as lib on (lib.id = i.library_id) - where i.id = ? - for update") - -(defn- select-icon-for-update - [conn id] - (let [row (db/exec-one! conn [sql:select-icon-for-update id])] - (when-not row - (ex/raise :type :not-found)) - row)) - - -;; --- Mutation: Delete Icon - -(declare delete-icon) - -(s/def ::delete-icon - (s/keys :req-un [::profile-id ::id])) - -(sm/defmutation ::delete-icon - [{:keys [id profile-id] :as params}] - (db/with-atomic [conn db/pool] - (let [icn (select-icon-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id icn)) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id id :type :icon}}) - - (db/update! conn :icon - {:deleted-at (dt/now)} - {:id id}) - nil))) +;; (ns uxbox.services.mutations.icons +;; (:require +;; [clojure.spec.alpha :as s] +;; [uxbox.common.exceptions :as ex] +;; [uxbox.common.spec :as us] +;; [uxbox.common.uuid :as uuid] +;; [uxbox.config :as cfg] +;; [uxbox.db :as db] +;; [uxbox.services.mutations :as sm] +;; [uxbox.services.queries.icons :refer [decode-row]] +;; [uxbox.services.queries.teams :as teams] +;; [uxbox.tasks :as tasks] +;; [uxbox.util.blob :as blob] +;; [uxbox.util.time :as dt])) +;; +;; ;; --- Helpers & Specs +;; +;; (s/def ::height ::us/integer) +;; (s/def ::id ::us/uuid) +;; (s/def ::library-id ::us/uuid) +;; (s/def ::name ::us/string) +;; (s/def ::profile-id ::us/uuid) +;; (s/def ::team-id ::us/uuid) +;; (s/def ::width ::us/integer) +;; +;; (s/def ::view-box +;; (s/and (s/coll-of number?) +;; #(= 4 (count %)) +;; vector?)) +;; +;; (s/def ::content ::us/string) +;; (s/def ::mimetype ::us/string) +;; +;; (s/def ::metadata +;; (s/keys :opt-un [::width ::height ::view-box ::mimetype])) +;; +;; +;; ;; --- Mutation: Create Library +;; +;; (declare create-library) +;; +;; (s/def ::create-icon-library +;; (s/keys :req-un [::profile-id ::team-id ::name] +;; :opt-un [::id])) +;; +;; (sm/defmutation ::create-icon-library +;; [{:keys [profile-id team-id id name] :as params}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-edition-permissions! conn profile-id team-id) +;; (create-library conn params))) +;; +;; (def ^:private sql:create-library +;; "insert into icon_library (id, team_id, name) +;; values ($1, $2, $3) +;; returning *;") +;; +;; (defn create-library +;; [conn {:keys [team-id id name] :as params}] +;; (let [id (or id (uuid/next))] +;; (db/insert! conn :icon-library +;; {:id id +;; :team-id team-id +;; :name name}))) +;; +;; +;; ;; --- Mutation: Rename Library +;; +;; (declare select-library-for-update) +;; (declare rename-library) +;; +;; (s/def ::rename-icon-library +;; (s/keys :req-un [::profile-id ::name ::id])) +;; +;; (sm/defmutation ::rename-icon-library +;; [{:keys [id profile-id name] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-library-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; (rename-library conn id name)))) +;; +;; (defn- select-library-for-update +;; [conn id] +;; (db/get-by-id conn :icon-library id {:for-update true})) +;; +;; (defn- rename-library +;; [conn id name] +;; (db/update! conn :icon-library +;; {:name name} +;; {:id id})) +;; +;; ;; --- Mutation: Delete Library +;; +;; (declare delete-library) +;; +;; (s/def ::delete-icon-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sm/defmutation ::delete-icon-library +;; [{:keys [profile-id id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-library-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; +;; ;; Schedule object deletion +;; (tasks/submit! conn {:name "delete-object" +;; :delay cfg/default-deletion-delay +;; :props {:id id :type :icon-library}}) +;; +;; (db/update! conn :icon-library +;; {:deleted-at (dt/now)} +;; {:id id}) +;; nil))) +;; +;; +;; ;; --- Mutation: Create Icon (Upload) +;; +;; (declare create-icon) +;; +;; (s/def ::create-icon +;; (s/keys :req-un [::profile-id ::name ::metadata ::content ::library-id] +;; :opt-un [::id])) +;; +;; (sm/defmutation ::create-icon +;; [{:keys [profile-id library-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-library-for-update conn library-id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; (create-icon conn params)))) +;; +;; (defn create-icon +;; [conn {:keys [id name library-id metadata content]}] +;; (let [id (or id (uuid/next))] +;; (-> (db/insert! conn :icon +;; {:id id +;; :name name +;; :library-id library-id +;; :content content +;; :metadata (blob/encode metadata)}) +;; (decode-row)))) +;; +;; +;; ;; --- Mutation: Rename Icon +;; +;; (declare select-icon-for-update) +;; (declare rename-icon) +;; +;; (s/def ::rename-icon +;; (s/keys :req-un [::id ::profile-id ::name])) +;; +;; (sm/defmutation ::rename-icon +;; [{:keys [id profile-id name] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [icon (select-icon-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id icon)) +;; (db/update! conn :icon +;; {:name name} +;; {:id id})))) +;; +;; (def ^:private +;; sql:select-icon-for-update +;; "select i.*, +;; lib.team_id as team_id +;; from icon as i +;; inner join icon_library as lib on (lib.id = i.library_id) +;; where i.id = ? +;; for update") +;; +;; (defn- select-icon-for-update +;; [conn id] +;; (let [row (db/exec-one! conn [sql:select-icon-for-update id])] +;; (when-not row +;; (ex/raise :type :not-found)) +;; row)) +;; +;; +;; ;; --- Mutation: Delete Icon +;; +;; (declare delete-icon) +;; +;; (s/def ::delete-icon +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sm/defmutation ::delete-icon +;; [{:keys [id profile-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [icn (select-icon-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id icn)) +;; +;; ;; Schedule object deletion +;; (tasks/submit! conn {:name "delete-object" +;; :delay cfg/default-deletion-delay +;; :props {:id id :type :icon}}) +;; +;; (db/update! conn :icon +;; {:deleted-at (dt/now)} +;; {:id id}) +;; nil))) diff --git a/backend/src/uxbox/services/mutations/images.clj b/backend/src/uxbox/services/mutations/images.clj deleted file mode 100644 index 08ba7ccdd..000000000 --- a/backend/src/uxbox/services/mutations/images.clj +++ /dev/null @@ -1,273 +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/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2020 UXBOX Labs SL - -(ns uxbox.services.mutations.images - (:require - [clojure.spec.alpha :as s] - [datoteka.core :as fs] - [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.common.uuid :as uuid] - [uxbox.config :as cfg] - [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.media :as media] - [uxbox.services.mutations :as sm] - [uxbox.services.queries.teams :as teams] - [uxbox.tasks :as tasks] - [uxbox.util.storage :as ust] - [uxbox.util.time :as dt])) - -(def thumbnail-options - {:width 800 - :height 800 - :quality 85 - :format :jpeg}) - -(s/def ::id ::us/uuid) -(s/def ::name ::us/string) -(s/def ::profile-id ::us/uuid) -(s/def ::file-id ::us/uuid) -(s/def ::team-id ::us/uuid) -(s/def ::url ::us/url) - -;; --- Create Library - -(declare create-library) - -(s/def ::create-image-library - (s/keys :req-un [::profile-id ::team-id ::name] - :opt-un [::id])) - -(sm/defmutation ::create-image-library - [{:keys [profile-id team-id] :as params}] - (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) - (create-library conn params))) - -(defn create-library - [conn {:keys [id team-id name]}] - (let [id (or id (uuid/next))] - (db/insert! conn :image-library - {:id id - :team-id team-id - :name name}))) - - -;; --- Rename Library - -(declare select-file-for-update) - -(s/def ::rename-image-library - (s/keys :req-un [::id ::profile-id ::name])) - -(sm/defmutation ::rename-image-library - [{:keys [profile-id id name] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-file-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - (db/update! conn :image-library - {:name name} - {:id id})))) - -(def ^:private sql:select-file-for-update - "select file.*, - project.team_id as team_id - from file - inner join project on (project.id = file.project_id) - where file.id = ? - for update of file") - -(defn- select-file-for-update - [conn id] - (let [row (db/exec-one! conn [sql:select-file-for-update id])] - (when-not row - (ex/raise :type :not-found)) - row)) - - -;; --- Delete Library - -(declare delete-library) - -(s/def ::delete-image-library - (s/keys :req-un [::profile-id ::id])) - -(sm/defmutation ::delete-image-library - [{:keys [id profile-id] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (select-file-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id id :type :image-library}}) - - (db/update! conn :image-library - {:deleted-at (dt/now)} - {:id id}) - nil))) - - -;; --- Create Image (Upload and create from url) - -(declare create-image) -(declare persist-image-on-fs) -(declare persist-image-thumbnail-on-fs) - -(def valid-image-types? - #{"image/jpeg", "image/png", "image/webp", "image/svg+xml"}) - -(s/def :uxbox$upload/filename ::us/string) -(s/def :uxbox$upload/size ::us/integer) -(s/def :uxbox$upload/content-type valid-image-types?) -(s/def :uxbox$upload/tempfile any?) - -(s/def ::upload - (s/keys :req-un [:uxbox$upload/filename - :uxbox$upload/size - :uxbox$upload/tempfile - :uxbox$upload/content-type])) - -(s/def ::content ::upload) - -(s/def ::add-image-from-url - (s/keys :req-un [::profile-id ::file-id ::url] - :opt-un [::id])) - -(s/def ::upload-image - (s/keys :req-un [::profile-id ::file-id ::name ::content] - :opt-un [::id])) - -(sm/defmutation ::add-image-from-url - [{:keys [profile-id file-id url] :as params}] - (db/with-atomic [conn db/pool] - (let [file (select-file-for-update conn file-id)] - (teams/check-edition-permissions! conn profile-id (:team-id file)) - (let [content (images/download-image url) - params' (merge params {:content content - :name (:filename content)})] - (create-image conn params'))))) - -(sm/defmutation ::upload-image - [{:keys [profile-id file-id] :as params}] - (db/with-atomic [conn db/pool] - (let [file (select-file-for-update conn file-id)] - (teams/check-edition-permissions! conn profile-id (:team-id file)) - (create-image conn params)))) - -(defn create-image - [conn {:keys [id content file-id name]}] - (when-not (valid-image-types? (:content-type content)) - (ex/raise :type :validation - :code :image-type-not-allowed - :hint "Seems like you are uploading an invalid image.")) - - (let [info (images/run {:cmd :info :input {:path (:tempfile content) - :mtype (:content-type content)}}) - path (persist-image-on-fs content) - opts (assoc thumbnail-options - :input {:mtype (:mtype info) - :path path}) - thumb (if-not (= (:mtype info) "image/svg+xml") - (persist-image-thumbnail-on-fs opts) - (assoc info - :path path - :quality 0))] - - (-> (db/insert! conn :image - {:id (or id (uuid/next)) - :file-id file-id - :name name - :path (str path) - :width (:width info) - :height (:height info) - :mtype (:mtype info) - :thumb-path (str (:path thumb)) - :thumb-width (:width thumb) - :thumb-height (:height thumb) - :thumb-quality (:quality thumb) - :thumb-mtype (:mtype thumb)}) - (images/resolve-urls :path :uri) - (images/resolve-urls :thumb-path :thumb-uri)))) - -(defn persist-image-on-fs - [{:keys [filename tempfile]}] - (let [filename (fs/name filename)] - (ust/save! media/media-storage filename tempfile))) - -(defn persist-image-thumbnail-on-fs - [{:keys [input] :as params}] - (let [path (ust/lookup media/media-storage (:path input)) - thumb (images/run - (-> params - (assoc :cmd :generic-thumbnail) - (update :input assoc :path path))) - - name (str "thumbnail-" - (first (fs/split-ext (fs/name (:path input)))) - (images/format->extension (:format thumb))) - path (ust/save! media/media-storage name (:data thumb))] - - (-> thumb - (dissoc :data :input) - (assoc :path path)))) - -;; --- Mutation: Rename Image - -(declare select-image-for-update) - -(s/def ::rename-image - (s/keys :req-un [::id ::profile-id ::name])) - -(sm/defmutation ::rename-image - [{:keys [id profile-id name] :as params}] - (db/with-atomic [conn db/pool] - (let [img (select-image-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id img)) - (db/update! conn :image - {:name name} - {:id id})))) - -(def ^:private sql:select-image-for-update - "select img.*, - lib.team_id as team_id - from image as img - inner join image_library as lib on (lib.id = img.library_id) - where img.id = ? - for update of img") - -(defn- select-image-for-update - [conn id] - (let [row (db/exec-one! conn [sql:select-image-for-update id])] - (when-not row - (ex/raise :type :not-found)) - row)) - -;; --- Delete Image - -(s/def ::delete-image - (s/keys :req-un [::id ::profile-id])) - -(sm/defmutation ::delete-image - [{:keys [profile-id id] :as params}] - (db/with-atomic [conn db/pool] - (let [img (select-image-for-update conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id img)) - - ;; Schedule object deletion - (tasks/submit! conn {:name "delete-object" - :delay cfg/default-deletion-delay - :props {:id id :type :image}}) - - (db/update! conn :image - {:deleted-at (dt/now)} - {:id id}) - nil))) diff --git a/backend/src/uxbox/services/mutations/media.clj b/backend/src/uxbox/services/mutations/media.clj new file mode 100644 index 000000000..942a334ff --- /dev/null +++ b/backend/src/uxbox/services/mutations/media.clj @@ -0,0 +1,283 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns uxbox.services.mutations.media + (:require + [clojure.spec.alpha :as s] + [datoteka.core :as fs] + [uxbox.common.exceptions :as ex] + [uxbox.common.spec :as us] + [uxbox.common.uuid :as uuid] + [uxbox.config :as cfg] + [uxbox.db :as db] + [uxbox.media :as media] + [uxbox.services.mutations :as sm] + [uxbox.services.queries.teams :as teams] + [uxbox.tasks :as tasks] + [uxbox.media-storage :as mst] + [uxbox.util.storage :as ust] + [uxbox.util.time :as dt])) + +(def thumbnail-options + {:width 800 + :height 800 + :quality 85 + :format :jpeg}) + +(s/def ::id ::us/uuid) +(s/def ::name ::us/string) +(s/def ::profile-id ::us/uuid) +(s/def ::file-id ::us/uuid) +(s/def ::team-id ::us/uuid) +(s/def ::url ::us/url) + +;; ;; --- Create Library +;; +;; (declare create-library) +;; +;; (s/def ::create-media-object-library +;; (s/keys :req-un [::profile-id ::team-id ::name] +;; :opt-un [::id])) +;; +;; (sm/defmutation ::create-media-object-library +;; [{:keys [profile-id team-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-edition-permissions! conn profile-id team-id) +;; (create-library conn params))) +;; +;; (defn create-library +;; [conn {:keys [id team-id name]}] +;; (let [id (or id (uuid/next))] +;; (db/insert! conn :media-object-library +;; {:id id +;; :team-id team-id +;; :name name}))) +;; +;; +;; ;; --- Rename Library +;; +;; (s/def ::rename-media-object-library +;; (s/keys :req-un [::id ::profile-id ::name])) +;; +;; (sm/defmutation ::rename-media-object-library +;; [{:keys [profile-id id name] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-file-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; (db/update! conn :media-object-library +;; {:name name} +;; {:id id})))) +;; +;; +;; ;; --- Delete Library +;; +;; (declare delete-library) +;; +;; (s/def ::delete-media-object-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sm/defmutation ::delete-media-object-library +;; [{:keys [id profile-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (select-file-for-update conn id)] +;; (teams/check-edition-permissions! conn profile-id (:team-id lib)) +;; +;; ;; Schedule object deletion +;; (tasks/submit! conn {:name "delete-object" +;; :delay cfg/default-deletion-delay +;; :props {:id id :type :media-object-library}}) +;; +;; (db/update! conn :media-object-library +;; {:deleted-at (dt/now)} +;; {:id id}) +;; nil))) + + +;; --- Create Media object (Upload and create from url) + +(declare create-media-object) +(declare select-file-for-update) +(declare persist-media-object-on-fs) +(declare persist-media-thumbnail-on-fs) + +(def valid-media-object-types? + #{"image/jpeg", "image/png", "image/webp", "image/svg+xml"}) + +(s/def :uxbox$upload/filename ::us/string) +(s/def :uxbox$upload/size ::us/integer) +(s/def :uxbox$upload/content-type valid-media-object-types?) +(s/def :uxbox$upload/tempfile any?) + +(s/def ::upload + (s/keys :req-un [:uxbox$upload/filename + :uxbox$upload/size + :uxbox$upload/tempfile + :uxbox$upload/content-type])) + +(s/def ::content ::upload) + +(s/def ::is-local boolean?) + +(s/def ::add-media-object-from-url + (s/keys :req-un [::profile-id ::file-id ::url ::is-local] + :opt-un [::id])) + +(s/def ::upload-media-object + (s/keys :req-un [::profile-id ::file-id ::name ::content ::is-local] + :opt-un [::id])) + +(sm/defmutation ::add-media-object-from-url + [{:keys [profile-id file-id url] :as params}] + (db/with-atomic [conn db/pool] + (let [file (select-file-for-update conn file-id)] + (teams/check-edition-permissions! conn profile-id (:team-id file)) + (let [content (media/download-media-object url) + params' (merge params {:content content + :name (:filename content)})] + (create-media-object conn params'))))) + +(sm/defmutation ::upload-media-object + [{:keys [profile-id file-id] :as params}] + (db/with-atomic [conn db/pool] + (let [file (select-file-for-update conn file-id)] + (teams/check-edition-permissions! conn profile-id (:team-id file)) + (create-media-object conn params)))) + +(defn create-media-object + [conn {:keys [id content file-id name is-local]}] + (when-not (valid-media-object-types? (:content-type content)) + (ex/raise :type :validation + :code :media-type-not-allowed + :hint "Seems like you are uploading an invalid media object.")) + + (let [info (media/run {:cmd :info :input {:path (:tempfile content) + :mtype (:content-type content)}}) + path (persist-media-object-on-fs content) + opts (assoc thumbnail-options + :input {:mtype (:mtype info) + :path path}) + thumb (if-not (= (:mtype info) "image/svg+xml") + (persist-media-thumbnail-on-fs opts) + (assoc info + :path path + :quality 0)) + + media-object-id (or id (uuid/next)) + + media-object (-> (db/insert! conn :media-object + {:id media-object-id + :file-id file-id + :is-local is-local + :name name + :path (str path) + :width (:width info) + :height (:height info) + :mtype (:mtype info)}) + (media/resolve-urls :path :uri) + (media/resolve-urls :thumb-path :thumb-uri)) + + media-thumbnail (db/insert! conn :media-thumbnail + {:id (uuid/next) + :media-object-id media-object-id + :path (str (:path thumb)) + :width (:width thumb) + :height (:height thumb) + :quality (:quality thumb) + :mtype (:mtype thumb)})] + media-object)) + +(def ^:private sql:select-file-for-update + "select file.*, + project.team_id as team_id + from file + inner join project on (project.id = file.project_id) + where file.id = ? + for update of file") + +(defn- select-file-for-update + [conn id] + (let [row (db/exec-one! conn [sql:select-file-for-update id])] + (when-not row + (ex/raise :type :not-found)) + row)) + +(defn persist-media-object-on-fs + [{:keys [filename tempfile]}] + (let [filename (fs/name filename)] + (ust/save! mst/media-storage filename tempfile))) + +(defn persist-media-thumbnail-on-fs + [{:keys [input] :as params}] + (let [path (ust/lookup mst/media-storage (:path input)) + thumb (media/run + (-> params + (assoc :cmd :generic-thumbnail) + (update :input assoc :path path))) + + name (str "thumbnail-" + (first (fs/split-ext (fs/name (:path input)))) + (media/format->extension (:format thumb))) + path (ust/save! mst/media-storage name (:data thumb))] + + (-> thumb + (dissoc :data :input) + (assoc :path path)))) + +;; --- Mutation: Rename Media object + +(declare select-media-object-for-update) + +(s/def ::rename-media-object + (s/keys :req-un [::id ::profile-id ::name])) + +(sm/defmutation ::rename-media-object + [{:keys [id profile-id name] :as params}] + (db/with-atomic [conn db/pool] + (let [obj (select-media-object-for-update conn id)] + (teams/check-edition-permissions! conn profile-id (:team-id obj)) + (db/update! conn :media-object + {:name name} + {:id id})))) + +(def ^:private sql:select-media-object-for-update + "select obj.*, + p.team_id as team_id + from media_object as obj + inner join file as f on (f.id = obj.file_id) + inner join project as p on (p.id = f.project_id) + where obj.id = ? + for update of obj") + +(defn- select-media-object-for-update + [conn id] + (let [row (db/exec-one! conn [sql:select-media-object-for-update id])] + (when-not row + (ex/raise :type :not-found)) + row)) + +;; --- Delete Media object + +(s/def ::delete-media-object + (s/keys :req-un [::id ::profile-id])) + +(sm/defmutation ::delete-media-object + [{:keys [profile-id id] :as params}] + (db/with-atomic [conn db/pool] + (let [obj (select-media-object-for-update conn id)] + (teams/check-edition-permissions! conn profile-id (:team-id obj)) + + ;; Schedule object deletion + (tasks/submit! conn {:name "delete-object" + :delay cfg/default-deletion-delay + :props {:id id :type :media-object}}) + + (db/update! conn :media-object + {:deleted-at (dt/now)} + {:id id}) + nil))) diff --git a/backend/src/uxbox/services/mutations/profile.clj b/backend/src/uxbox/services/mutations/profile.clj index 36242ebcf..3392e7464 100644 --- a/backend/src/uxbox/services/mutations/profile.clj +++ b/backend/src/uxbox/services/mutations/profile.clj @@ -23,11 +23,11 @@ [uxbox.config :as cfg] [uxbox.db :as db] [uxbox.emails :as emails] - [uxbox.images :as images] [uxbox.media :as media] + [uxbox.media-storage :as mst] [uxbox.services.tokens :as tokens] [uxbox.services.mutations :as sm] - [uxbox.services.mutations.images :as imgs] + [uxbox.services.mutations.media :as media-mutations] [uxbox.services.mutations.projects :as projects] [uxbox.services.mutations.teams :as teams] [uxbox.services.queries.profile :as profile] @@ -266,20 +266,20 @@ (declare upload-photo) (declare update-profile-photo) -(s/def ::file ::imgs/upload) +(s/def ::file ::media-mutations/upload) (s/def ::update-profile-photo (s/keys :req-un [::profile-id ::file])) (sm/defmutation ::update-profile-photo [{:keys [profile-id file] :as params}] - (when-not (imgs/valid-image-types? (:content-type file)) + (when-not (media-mutations/valid-media-object-types? (:content-type file)) (ex/raise :type :validation - :code :image-type-not-allowed - :hint "Seems like you are uploading an invalid image.")) + :code :media-type-not-allowed + :hint "Seems like you are uploading an invalid media object")) (db/with-atomic [conn db/pool] (let [profile (profile/retrieve-profile conn profile-id) - _ (images/run {:cmd :info :input {:path (:tempfile file) + _ (media/run {:cmd :info :input {:path (:tempfile file) :mtype (:content-type file)}}) photo (upload-photo conn params)] @@ -295,7 +295,7 @@ [conn {:keys [file profile-id]}] (let [prefix (-> (sodi.prng/random-bytes 8) (sodi.util/bytes->b64s)) - thumb (images/run + thumb (media/run {:cmd :profile-thumbnail :format :jpeg :quality 85 @@ -303,8 +303,8 @@ :height 256 :input {:path (fs/path (:tempfile file)) :mtype (:content-type file)}}) - name (str prefix (images/format->extension (:format thumb)))] - (ust/save! media/media-storage name (:data thumb)))) + name (str prefix (media/format->extension (:format thumb)))] + (ust/save! mst/media-storage name (:data thumb)))) (defn- update-profile-photo [conn profile-id path] diff --git a/backend/src/uxbox/services/queries/colors.clj b/backend/src/uxbox/services/queries/colors.clj index 965e091b2..9d184e374 100644 --- a/backend/src/uxbox/services/queries/colors.clj +++ b/backend/src/uxbox/services/queries/colors.clj @@ -16,8 +16,6 @@ [uxbox.common.spec :as us] [uxbox.common.uuid :as uuid] [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.media :as media] [uxbox.services.queries :as sq] [uxbox.services.queries.teams :as teams] [uxbox.util.blob :as blob] @@ -29,59 +27,60 @@ (s/def ::profile-id ::us/uuid) (s/def ::team-id ::us/uuid) (s/def ::file-id ::us/uuid) -(s/def ::library-id (s/nilable ::us/uuid)) +;; (s/def ::library-id (s/nilable ::us/uuid)) -;; --- Query: Colors Libraries - -(def ^:private sql:libraries - "select lib.*, - (select count(*) from color where library_id = lib.id) as num_colors - from color_library as lib - where lib.team_id = ? - and lib.deleted_at is null - order by lib.created_at desc") - -(s/def ::color-libraries - (s/keys :req-un [::profile-id ::team-id])) - -(sq/defquery ::color-libraries - [{:keys [profile-id team-id]}] - (db/with-atomic [conn db/pool] - (teams/check-read-permissions! conn profile-id team-id) - (db/exec! conn [sql:libraries team-id]))) - - -;; --- Query: Color Library - -(declare retrieve-library) - -(s/def ::color-library - (s/keys :req-un [::profile-id ::id])) - -(sq/defquery ::color-library - [{:keys [profile-id id]}] - (db/with-atomic [conn db/pool] - (let [lib (retrieve-library conn id)] - (teams/check-read-permissions! conn profile-id (:team-id lib)) - lib))) - -(def ^:private sql:single-library - "select lib.*, - (select count(*) from color where library_id = lib.id) as num_colors - from color_library as lib - where lib.deleted_at is null - and lib.id = ?") - -(defn- retrieve-library - [conn id] - (let [row (db/exec-one! conn [sql:single-library id])] - (when-not row - (ex/raise :type :not-found)) - row)) +;; ;; --- Query: Colors Libraries +;; +;; (def ^:private sql:libraries +;; "select lib.*, +;; (select count(*) from color where library_id = lib.id) as num_colors +;; from color_library as lib +;; where lib.team_id = ? +;; and lib.deleted_at is null +;; order by lib.created_at desc") +;; +;; (s/def ::color-libraries +;; (s/keys :req-un [::profile-id ::team-id])) +;; +;; (sq/defquery ::color-libraries +;; [{:keys [profile-id team-id]}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-read-permissions! conn profile-id team-id) +;; (db/exec! conn [sql:libraries team-id]))) +;; +;; +;; ;; --- Query: Color Library +;; +;; (declare retrieve-library) +;; +;; (s/def ::color-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sq/defquery ::color-library +;; [{:keys [profile-id id]}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (retrieve-library conn id)] +;; (teams/check-read-permissions! conn profile-id (:team-id lib)) +;; lib))) +;; +;; (def ^:private sql:single-library +;; "select lib.*, +;; (select count(*) from color where library_id = lib.id) as num_colors +;; from color_library as lib +;; where lib.deleted_at is null +;; and lib.id = ?") +;; +;; (defn- retrieve-library +;; [conn id] +;; (let [row (db/exec-one! conn [sql:single-library id])] +;; (when-not row +;; (ex/raise :type :not-found)) +;; row)) ;; --- Query: Colors (by file) (declare retrieve-colors) +(declare retrieve-file) (s/def ::colors (s/keys :req-un [::profile-id ::file-id])) @@ -89,10 +88,9 @@ (sq/defquery ::colors [{:keys [profile-id file-id] :as params}] (db/with-atomic [conn db/pool] - (retrieve-colors conn file-id))) - ;; (let [lib (retrieve-library conn library-id)] - ;; (teams/check-read-permissions! conn profile-id (:team-id lib)) - ;; (retrieve-colors conn library-id)))) + (let [file (retrieve-file conn file-id)] + (teams/check-read-permissions! conn profile-id (:team-id file)) + (retrieve-colors conn file-id)))) (def ^:private sql:colors "select * @@ -105,6 +103,20 @@ [conn file-id] (db/exec! conn [sql:colors file-id])) +(def ^:private sql:retrieve-file + "select file.*, + project.team_id as team_id + from file + inner join project on (project.id = file.project_id) + where file.id = ?") + +(defn- retrieve-file + [conn id] + (let [row (db/exec-one! conn [sql:retrieve-file id])] + (when-not row + (ex/raise :type :not-found)) + row)) + ;; --- Query: Color (by ID) @@ -123,9 +135,10 @@ (def ^:private sql:single-color "select color.*, - lib.team_id as team_id + p.team_id as team_id from color as color - inner join color_library as lib on (lib.id = color.library_id) + inner join file as f on (color.file_id = f.id) + inner join project as p on (p.id = f.project_id) where color.deleted_at is null and color.id = ? order by created_at desc") @@ -136,3 +149,4 @@ (when-not row (ex/raise :type :not-found)) row)) + diff --git a/backend/src/uxbox/services/queries/files.clj b/backend/src/uxbox/services/queries/files.clj index e107e1b9d..1096af53a 100644 --- a/backend/src/uxbox/services/queries/files.clj +++ b/backend/src/uxbox/services/queries/files.clj @@ -14,7 +14,7 @@ [uxbox.common.exceptions :as ex] [uxbox.common.spec :as us] [uxbox.db :as db] - [uxbox.images :as images] + [uxbox.media :as media] [uxbox.services.queries :as sq] [uxbox.util.blob :as blob])) @@ -220,32 +220,32 @@ (ex/raise :type :validation :code :not-authorized)))) -;; --- Query: Images of the File - -(declare retrieve-file-images) - -(s/def ::file-images - (s/keys :req-un [::profile-id ::file-id])) - -(sq/defquery ::file-images - [{:keys [profile-id file-id] :as params}] - (db/with-atomic [conn db/pool] - (check-edition-permissions! conn profile-id file-id) - (retrieve-file-images conn params))) - -(def ^:private sql:file-images - "select fi.* - from file_image as fi - where fi.file_id = ? - and fi.deleted_at is null") - -(defn retrieve-file-images - [conn {:keys [file-id] :as params}] - (let [sqlv [sql:file-images file-id] - xf (comp (map #(images/resolve-urls % :path :uri)) - (map #(images/resolve-urls % :thumb-path :thumb-uri)))] - (->> (db/exec! conn sqlv) - (into [] xf)))) +;; ;; --- Query: Images of the File +;; +;; (declare retrieve-file-images) +;; +;; (s/def ::file-images +;; (s/keys :req-un [::profile-id ::file-id])) +;; +;; (sq/defquery ::file-images +;; [{:keys [profile-id file-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (check-edition-permissions! conn profile-id file-id) +;; (retrieve-file-images conn params))) +;; +;; (def ^:private sql:file-images +;; "select fi.* +;; from file_image as fi +;; where fi.file_id = ? +;; and fi.deleted_at is null") +;; +;; (defn retrieve-file-images +;; [conn {:keys [file-id] :as params}] +;; (let [sqlv [sql:file-images file-id] +;; xf (comp (map #(media/resolve-urls % :path :uri)) +;; (map #(media/resolve-urls % :thumb-path :thumb-uri)))] +;; (->> (db/exec! conn sqlv) +;; (into [] xf)))) ;; --- Query: File (By ID) @@ -284,7 +284,7 @@ (defn retrieve-file-users [conn id] (->> (db/exec! conn [sql:file-users id id]) - (mapv #(images/resolve-media-uris % [:photo :photo-uri])))) + (mapv #(media/resolve-media-uris % [:photo :photo-uri])))) (s/def ::file-users (s/keys :req-un [::profile-id ::id])) diff --git a/backend/src/uxbox/services/queries/icons.clj b/backend/src/uxbox/services/queries/icons.clj index 1ea653725..8a6cd0a35 100644 --- a/backend/src/uxbox/services/queries/icons.clj +++ b/backend/src/uxbox/services/queries/icons.clj @@ -1,148 +1,148 @@ -;; 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/. +;; ;; 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/. +;; ;; +;; ;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; ;; defined by the Mozilla Public License, v. 2.0. +;; ;; +;; ;; Copyright (c) 2019 Andrey Antukh ;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. +;; (ns uxbox.services.queries.icons +;; (:require +;; [clojure.spec.alpha :as s] +;; [uxbox.common.exceptions :as ex] +;; [uxbox.common.spec :as us] +;; [uxbox.common.uuid :as uuid] +;; [uxbox.db :as db] +;; [uxbox.images :as images] +;; [uxbox.media :as media] +;; [uxbox.services.queries :as sq] +;; [uxbox.services.queries.teams :as teams] +;; [uxbox.util.blob :as blob] +;; [uxbox.util.data :as data])) +;; +;; ;; --- Helpers & Specs +;; +;; (s/def ::id ::us/uuid) +;; (s/def ::name ::us/string) +;; (s/def ::profile-id ::us/uuid) +;; (s/def ::library-id ::us/uuid) +;; (s/def ::team-id ::us/uuid) +;; +;; (defn decode-row +;; [{:keys [metadata] :as row}] +;; (when row +;; (cond-> row +;; metadata (assoc :metadata (blob/decode metadata))))) +;; +;; ;; --- Query: Icons Librarys +;; +;; (def ^:private sql:libraries +;; "select lib.*, +;; (select count(*) from icon where library_id = lib.id) as num_icons +;; from icon_library as lib +;; where lib.team_id = ? +;; and lib.deleted_at is null +;; order by lib.created_at desc") +;; +;; (s/def ::icon-libraries +;; (s/keys :req-un [::profile-id ::team-id])) +;; +;; (sq/defquery ::icon-libraries +;; [{:keys [profile-id team-id]}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-read-permissions! conn profile-id team-id) +;; (db/exec! conn [sql:libraries team-id]))) +;; +;; +;; +;; ;; --- Query: Icon Library +;; +;; (declare retrieve-library) +;; +;; (s/def ::icon-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sq/defquery ::icon-library +;; [{:keys [profile-id id]}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (retrieve-library conn id)] +;; (teams/check-read-permissions! conn profile-id (:team-id lib)) +;; lib))) +;; +;; (def ^:private sql:single-library +;; "select lib.*, +;; (select count(*) from icon where library_id = lib.id) as num_icons +;; from icon_library as lib +;; where lib.deleted_at is null +;; and lib.id = ?") +;; +;; (defn- retrieve-library +;; [conn id] +;; (let [row (db/exec-one! conn [sql:single-library id])] +;; (when-not row +;; (ex/raise :type :not-found)) +;; row)) +;; +;; +;; +;; ;; --- Query: Icons (by library) +;; +;; (declare retrieve-icons) +;; +;; (s/def ::icons +;; (s/keys :req-un [::profile-id ::library-id])) +;; +;; (sq/defquery ::icons +;; [{:keys [profile-id library-id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (retrieve-library conn library-id)] +;; (teams/check-read-permissions! conn profile-id (:team-id lib)) +;; (->> (retrieve-icons conn library-id) +;; (mapv decode-row))))) +;; +;; (def ^:private sql:icons +;; "select icon.* +;; from icon as icon +;; inner join icon_library as lib on (lib.id = icon.library_id) +;; where icon.deleted_at is null +;; and icon.library_id = ? +;; order by created_at desc") +;; +;; (defn- retrieve-icons +;; [conn library-id] +;; (db/exec! conn [sql:icons library-id])) +;; +;; +;; +;; ;; --- Query: Icon (by ID) +;; +;; (declare retrieve-icon) +;; +;; (s/def ::id ::us/uuid) +;; (s/def ::icon +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sq/defquery ::icon +;; [{:keys [profile-id id] :as params}] +;; (db/with-atomic [conn db/pool] +;; (let [icon (retrieve-icon conn id)] +;; (teams/check-read-permissions! conn profile-id (:team-id icon)) +;; (decode-row icon)))) +;; +;; (def ^:private sql:single-icon +;; "select icon.*, +;; lib.team_id as team_id +;; from icon as icon +;; inner join icon_library as lib on (lib.id = icon.library_id) +;; where icon.deleted_at is null +;; and icon.id = ? +;; order by created_at desc") +;; +;; (defn retrieve-icon +;; [conn id] +;; (let [row (db/exec-one! conn [sql:single-icon id])] +;; (when-not row +;; (ex/raise :type :not-found)) +;; row)) ;; -;; Copyright (c) 2019 Andrey Antukh - -(ns uxbox.services.queries.icons - (:require - [clojure.spec.alpha :as s] - [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.common.uuid :as uuid] - [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.media :as media] - [uxbox.services.queries :as sq] - [uxbox.services.queries.teams :as teams] - [uxbox.util.blob :as blob] - [uxbox.util.data :as data])) - -;; --- Helpers & Specs - -(s/def ::id ::us/uuid) -(s/def ::name ::us/string) -(s/def ::profile-id ::us/uuid) -(s/def ::library-id ::us/uuid) -(s/def ::team-id ::us/uuid) - -(defn decode-row - [{:keys [metadata] :as row}] - (when row - (cond-> row - metadata (assoc :metadata (blob/decode metadata))))) - -;; --- Query: Icons Librarys - -(def ^:private sql:libraries - "select lib.*, - (select count(*) from icon where library_id = lib.id) as num_icons - from icon_library as lib - where lib.team_id = ? - and lib.deleted_at is null - order by lib.created_at desc") - -(s/def ::icon-libraries - (s/keys :req-un [::profile-id ::team-id])) - -(sq/defquery ::icon-libraries - [{:keys [profile-id team-id]}] - (db/with-atomic [conn db/pool] - (teams/check-read-permissions! conn profile-id team-id) - (db/exec! conn [sql:libraries team-id]))) - - - -;; --- Query: Icon Library - -(declare retrieve-library) - -(s/def ::icon-library - (s/keys :req-un [::profile-id ::id])) - -(sq/defquery ::icon-library - [{:keys [profile-id id]}] - (db/with-atomic [conn db/pool] - (let [lib (retrieve-library conn id)] - (teams/check-read-permissions! conn profile-id (:team-id lib)) - lib))) - -(def ^:private sql:single-library - "select lib.*, - (select count(*) from icon where library_id = lib.id) as num_icons - from icon_library as lib - where lib.deleted_at is null - and lib.id = ?") - -(defn- retrieve-library - [conn id] - (let [row (db/exec-one! conn [sql:single-library id])] - (when-not row - (ex/raise :type :not-found)) - row)) - - - -;; --- Query: Icons (by library) - -(declare retrieve-icons) - -(s/def ::icons - (s/keys :req-un [::profile-id ::library-id])) - -(sq/defquery ::icons - [{:keys [profile-id library-id] :as params}] - (db/with-atomic [conn db/pool] - (let [lib (retrieve-library conn library-id)] - (teams/check-read-permissions! conn profile-id (:team-id lib)) - (->> (retrieve-icons conn library-id) - (mapv decode-row))))) - -(def ^:private sql:icons - "select icon.* - from icon as icon - inner join icon_library as lib on (lib.id = icon.library_id) - where icon.deleted_at is null - and icon.library_id = ? - order by created_at desc") - -(defn- retrieve-icons - [conn library-id] - (db/exec! conn [sql:icons library-id])) - - - -;; --- Query: Icon (by ID) - -(declare retrieve-icon) - -(s/def ::id ::us/uuid) -(s/def ::icon - (s/keys :req-un [::profile-id ::id])) - -(sq/defquery ::icon - [{:keys [profile-id id] :as params}] - (db/with-atomic [conn db/pool] - (let [icon (retrieve-icon conn id)] - (teams/check-read-permissions! conn profile-id (:team-id icon)) - (decode-row icon)))) - -(def ^:private sql:single-icon - "select icon.*, - lib.team_id as team_id - from icon as icon - inner join icon_library as lib on (lib.id = icon.library_id) - where icon.deleted_at is null - and icon.id = ? - order by created_at desc") - -(defn retrieve-icon - [conn id] - (let [row (db/exec-one! conn [sql:single-icon id])] - (when-not row - (ex/raise :type :not-found)) - row)) - diff --git a/backend/src/uxbox/services/queries/images.clj b/backend/src/uxbox/services/queries/images.clj deleted file mode 100644 index 4653819d5..000000000 --- a/backend/src/uxbox/services/queries/images.clj +++ /dev/null @@ -1,144 +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/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2019-2020 Andrey Antukh - -(ns uxbox.services.queries.images - (:require - [clojure.spec.alpha :as s] - [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.services.queries :as sq] - [uxbox.services.queries.teams :as teams])) - -(s/def ::id ::us/uuid) -(s/def ::name ::us/string) -(s/def ::profile-id ::us/uuid) -(s/def ::team-id ::us/uuid) -(s/def ::file-id ::us/uuid) - -;; --- Query: Image Librarys - -(def ^:private sql:libraries - "select lib.*, - (select count(*) from image where library_id = lib.id) as num_images - from image_library as lib - where lib.team_id = ? - and lib.deleted_at is null - order by lib.created_at desc") - -(s/def ::image-libraries - (s/keys :req-un [::profile-id ::team-id])) - -(sq/defquery ::image-libraries - [{:keys [profile-id team-id]}] - (db/with-atomic [conn db/pool] - (teams/check-read-permissions! conn profile-id team-id) - (db/exec! conn [sql:libraries team-id]))) - - -;; --- Query: Image Library - -(declare retrieve-library) - -(s/def ::image-library - (s/keys :req-un [::profile-id ::id])) - -(sq/defquery ::image-library - [{:keys [profile-id id]}] - (db/with-atomic [conn db/pool] - (let [lib (retrieve-library conn id)] - (teams/check-read-permissions! conn profile-id (:team-id lib)) - lib))) - -(def ^:private sql:single-library - "select lib.*, - (select count(*) from image where library_id = lib.id) as num_images - from image_library as lib - where lib.deleted_at is null - and lib.id = ?") - -(defn- retrieve-library - [conn id] - (let [row (db/exec-one! conn [sql:single-library id])] - (when-not row - (ex/raise :type :not-found)) - row)) - - -;; --- Query: Images (by library) - -(declare retrieve-images) - -(s/def ::images - (s/keys :req-un [::profile-id ::file-id])) - -;; TODO: check if we can resolve url with transducer for reduce -;; garbage generation for each request - -(sq/defquery ::images - [{:keys [profile-id file-id] :as params}] - (db/with-atomic [conn db/pool] - (->> (retrieve-images conn file-id) - (mapv #(images/resolve-urls % :path :uri)) - (mapv #(images/resolve-urls % :thumb-path :thumb-uri))))) - ;; (let [lib (retrieve-library conn file-id)] - ;; (teams/check-read-permissions! conn profile-id (:team-id lib)) - ;; (->> (retrieve-images conn file-id) - ;; (mapv #(images/resolve-urls % :path :uri)) - ;; (mapv #(images/resolve-urls % :thumb-path :thumb-uri)))))) - - -(def ^:private sql:images - "select * - from image as img - where img.deleted_at is null - and img.file_id = ? - order by created_at desc") - -(defn- retrieve-images - [conn file-id] - (db/exec! conn [sql:images file-id])) - - - -;; --- Query: Image (by ID) - -(declare retrieve-image) - -(s/def ::id ::us/uuid) -(s/def ::image - (s/keys :req-un [::profile-id ::id])) - -(sq/defquery ::image - [{:keys [profile-id id] :as params}] - (db/with-atomic [conn db/pool] - (let [img (retrieve-image conn id)] - (teams/check-read-permissions! conn profile-id (:team-id img)) - (-> img - (images/resolve-urls :path :uri) - (images/resolve-urls :thumb-path :thumb-uri))))) - -(def ^:private sql:single-image - "select img.*, - file.team_id as team_id - from image as img - inner join file on (file.id = img.file_id) - where img.deleted_at is null - and img.id = ? - order by created_at desc") - -(defn retrieve-image - [conn id] - (let [row (db/exec-one! conn [sql:single-image id])] - (when-not row - (ex/raise :type :not-found)) - row)) - - diff --git a/backend/src/uxbox/services/queries/media.clj b/backend/src/uxbox/services/queries/media.clj new file mode 100644 index 000000000..2f220758b --- /dev/null +++ b/backend/src/uxbox/services/queries/media.clj @@ -0,0 +1,154 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2019-2020 Andrey Antukh + +(ns uxbox.services.queries.media + (:require + [clojure.spec.alpha :as s] + [uxbox.common.exceptions :as ex] + [uxbox.common.spec :as us] + [uxbox.db :as db] + [uxbox.media :as media] + [uxbox.services.queries :as sq] + [uxbox.services.queries.teams :as teams])) + +(s/def ::id ::us/uuid) +(s/def ::name ::us/string) +(s/def ::profile-id ::us/uuid) +(s/def ::team-id ::us/uuid) +(s/def ::file-id ::us/uuid) + +;; ;; --- Query: Media Libraries +;; +;; (def ^:private sql:libraries +;; "select lib.*, +;; (select count(*) from media where library_id = lib.id) as num_media +;; from media_library as lib +;; where lib.team_id = ? +;; and lib.deleted_at is null +;; order by lib.created_at desc") +;; +;; (s/def ::media-libraries +;; (s/keys :req-un [::profile-id ::team-id])) +;; +;; (sq/defquery ::media-libraries +;; [{:keys [profile-id team-id]}] +;; (db/with-atomic [conn db/pool] +;; (teams/check-read-permissions! conn profile-id team-id) +;; (db/exec! conn [sql:libraries team-id]))) +;; +;; +;; ;; --- Query: Media Library +;; +;; (declare retrieve-library) +;; +;; (s/def ::media-library +;; (s/keys :req-un [::profile-id ::id])) +;; +;; (sq/defquery ::media-library +;; [{:keys [profile-id id]}] +;; (db/with-atomic [conn db/pool] +;; (let [lib (retrieve-library conn id)] +;; (teams/check-read-permissions! conn profile-id (:team-id lib)) +;; lib))) +;; +;; (def ^:private sql:single-library +;; "select lib.*, +;; (select count(*) from media where library_id = lib.id) as num_media +;; from media_library as lib +;; where lib.deleted_at is null +;; and lib.id = ?") +;; +;; (defn- retrieve-library +;; [conn id] +;; (let [row (db/exec-one! conn [sql:single-library id])] +;; (when-not row +;; (ex/raise :type :not-found)) +;; row)) + + +;; --- Query: Media objects (by file) + +(declare retrieve-media-objects) +(declare retrieve-file) + +(s/def ::is-local boolean?) +(s/def ::media-objects + (s/keys :req-un [::profile-id ::file-id ::is-local])) + +;; TODO: check if we can resolve url with transducer for reduce +;; garbage generation for each request + +(sq/defquery ::media-objects + [{:keys [profile-id file-id is-local] :as params}] + (db/with-atomic [conn db/pool] + (let [file (retrieve-file conn file-id)] + (teams/check-read-permissions! conn profile-id (:team-id file)) + (->> (retrieve-media-objects conn file-id is-local) + (mapv #(media/resolve-urls % :path :uri)))))) + +(def ^:private sql:media-objects + "select * + from media_object + where deleted_at is null + and file_id = ? + and is_local = ? + order by created_at desc") + +(defn retrieve-media-objects + [conn file-id is-local] + (db/exec! conn [sql:media-objects file-id is-local])) + +(def ^:private sql:retrieve-file + "select file.*, + project.team_id as team_id + from file + inner join project on (project.id = file.project_id) + where file.id = ?") + +(defn- retrieve-file + [conn id] + (let [row (db/exec-one! conn [sql:retrieve-file id])] + (when-not row + (ex/raise :type :not-found)) + row)) + + +;; --- Query: Media object (by ID) + +(declare retrieve-media-object) + +(s/def ::id ::us/uuid) +(s/def ::media-object + (s/keys :req-un [::profile-id ::id])) + +(sq/defquery ::media-object + [{:keys [profile-id id] :as params}] + (db/with-atomic [conn db/pool] + (let [media-object (retrieve-media-object conn id)] + (teams/check-read-permissions! conn profile-id (:team-id media-object)) + (-> media-object + (media/resolve-urls :path :uri))))) + +(def ^:private sql:media-object + "select obj.*, + p.team_id as team_id + from media_object as obj + inner join file as f on (f.id = obj.file_id) + inner join project as p on (p.id = f.project_id) + where obj.deleted_at is null + and obj.id = ? + order by created_at desc") + +(defn retrieve-media-object + [conn id] + (let [row (db/exec-one! conn [sql:media-object id])] + (when-not row + (ex/raise :type :not-found)) + row)) + diff --git a/backend/src/uxbox/services/queries/profile.clj b/backend/src/uxbox/services/queries/profile.clj index 19779d7d2..e6e951bd3 100644 --- a/backend/src/uxbox/services/queries/profile.clj +++ b/backend/src/uxbox/services/queries/profile.clj @@ -10,7 +10,7 @@ [uxbox.common.exceptions :as ex] [uxbox.common.spec :as us] [uxbox.db :as db] - [uxbox.images :as images] + [uxbox.media :as media] [uxbox.services.queries :as sq] [uxbox.common.uuid :as uuid] [uxbox.util.blob :as blob])) @@ -78,7 +78,7 @@ (defn retrieve-profile [conn id] (let [profile (some-> (retrieve-profile-data conn id) - (images/resolve-urls :photo :photo-uri) + (media/resolve-urls :photo :photo-uri) (strip-private-attrs) (merge (retrieve-additional-data conn id)))] (when (nil? profile) diff --git a/backend/src/uxbox/services/queries/viewer.clj b/backend/src/uxbox/services/queries/viewer.clj index 60113809c..a2835a448 100644 --- a/backend/src/uxbox/services/queries/viewer.clj +++ b/backend/src/uxbox/services/queries/viewer.clj @@ -16,10 +16,9 @@ [uxbox.common.spec :as us] [uxbox.common.uuid :as uuid] [uxbox.db :as db] - [uxbox.images :as images] - [uxbox.media :as media] [uxbox.services.queries :as sq] [uxbox.services.queries.files :as files] + [uxbox.services.queries.media :as media-queries] [uxbox.services.queries.pages :as pages] [uxbox.util.blob :as blob] [uxbox.util.data :as data])) @@ -52,7 +51,7 @@ (db/with-atomic [conn db/pool] (let [page (pages/retrieve-page conn page-id) file (files/retrieve-file conn (:file-id page)) - images (files/retrieve-file-images conn page) + images (media-queries/retrieve-media-objects conn (:file-id page) true) project (retrieve-project conn (:project-id file))] (if (string? share-token) (when (not= share-token (:share-token page)) diff --git a/backend/src/uxbox/tasks/gc.clj b/backend/src/uxbox/tasks/gc.clj index 690ef922d..bdd4d33fa 100644 --- a/backend/src/uxbox/tasks/gc.clj +++ b/backend/src/uxbox/tasks/gc.clj @@ -18,7 +18,7 @@ [uxbox.common.spec :as us] [uxbox.config :as cfg] [uxbox.db :as db] - [uxbox.media :as media] + [uxbox.media-storage :as mst] [uxbox.util.blob :as blob] [uxbox.util.storage :as ust])) @@ -39,8 +39,8 @@ (run! (fn [item] (let [path1 (get item "path") path2 (get item "thumb_path")] - (ust/delete! media/media-storage path1) - (ust/delete! media/media-storage path2))) + (ust/delete! mst/media-storage path1) + (ust/delete! mst/media-storage path2))) result)) (defn- decode-row diff --git a/backend/src/uxbox/tasks/remove_media.clj b/backend/src/uxbox/tasks/remove_media.clj index 0b438fdb8..127c3b7db 100644 --- a/backend/src/uxbox/tasks/remove_media.clj +++ b/backend/src/uxbox/tasks/remove_media.clj @@ -14,7 +14,7 @@ [clojure.tools.logging :as log] [uxbox.common.exceptions :as ex] [uxbox.common.spec :as us] - [uxbox.media :as media] + [uxbox.media-storage :as mst] [uxbox.metrics :as mtx] [uxbox.util.storage :as ust])) @@ -25,8 +25,8 @@ (defn handler [{:keys [props] :as task}] (us/verify ::props props) - (when (ust/exists? media/media-storage (:path props)) - (ust/delete! media/media-storage (:path props)) + (when (ust/exists? mst/media-storage (:path props)) + (ust/delete! mst/media-storage (:path props)) (log/debug "Media " (:path props) " removed."))) (mtx/instrument-with-summary! diff --git a/backend/tests/uxbox/tests/helpers.clj b/backend/tests/uxbox/tests/helpers.clj index 56a8d5203..286e93416 100644 --- a/backend/tests/uxbox/tests/helpers.clj +++ b/backend/tests/uxbox/tests/helpers.clj @@ -14,13 +14,12 @@ [uxbox.services.mutations.teams :as teams] [uxbox.services.mutations.files :as files] [uxbox.services.mutations.pages :as pages] - [uxbox.services.mutations.images :as images] - [uxbox.services.mutations.icons :as icons] + ;; [uxbox.services.mutations.icons :as icons] [uxbox.services.mutations.colors :as colors] [uxbox.fixtures :as fixtures] [uxbox.migrations] - [uxbox.images] [uxbox.media] + [uxbox.media-storage] [uxbox.db :as db] [uxbox.util.blob :as blob] [uxbox.common.uuid :as uuid] @@ -45,12 +44,12 @@ #'uxbox.db/pool #'uxbox.redis/client #'uxbox.redis/conn - #'uxbox.images/semaphore + #'uxbox.media/semaphore #'uxbox.services.init/query-services #'uxbox.services.init/mutation-services #'uxbox.migrations/migrations - #'uxbox.media/assets-storage - #'uxbox.media/media-storage}) + #'uxbox.media-storage/assets-storage + #'uxbox.media-storage/media-storage}) (mount/swap {#'uxbox.config/config config #'uxbox.db/pool pool}) (mount/start))) @@ -73,8 +72,8 @@ (try (next) (finally - (ust/clear! uxbox.media/media-storage) - (ust/clear! uxbox.media/assets-storage)))) + (ust/clear! uxbox.media-storage/media-storage) + (ust/clear! uxbox.media-storage/assets-storage)))) (defn mk-uuid [prefix & args] @@ -105,10 +104,11 @@ :name (str "project" i)})) (defn create-file - [conn profile-id project-id i] + [conn profile-id project-id is-shared i] (#'files/create-file conn {:id (mk-uuid "file" i) :profile-id profile-id :project-id project-id + :is-shared is-shared :name (str "file" i)})) (defn create-page @@ -120,23 +120,22 @@ :ordering i :data cp/default-page-data})) - -(defn create-image-library - [conn team-id i] - (#'images/create-library conn {:id (mk-uuid "imgcoll" i) - :team-id team-id - :name (str "image library " i)})) - -(defn create-icon-library - [conn team-id i] - (#'icons/create-library conn {:id (mk-uuid "imgcoll" i) - :team-id team-id - :name (str "icon library " i)})) -(defn create-color-library - [conn team-id i] - (#'colors/create-library conn {:id (mk-uuid "imgcoll" i) - :team-id team-id - :name (str "color library " i)})) +;; (defn create-image-library +;; [conn team-id i] +;; (#'images/create-library conn {:id (mk-uuid "imgcoll" i) +;; :team-id team-id +;; :name (str "image library " i)})) +;; +;; (defn create-icon-library +;; [conn team-id i] +;; (#'icons/create-library conn {:id (mk-uuid "imgcoll" i) +;; :team-id team-id +;; :name (str "icon library " i)})) +;; (defn create-color-library +;; [conn team-id i] +;; (#'colors/create-library conn {:id (mk-uuid "imgcoll" i) +;; :team-id team-id +;; :name (str "color library " i)})) (defn handle-error [^Throwable err] diff --git a/backend/tests/uxbox/tests/test_services_colors.clj b/backend/tests/uxbox/tests/test_services_colors.clj index 0b352023c..b51d34c0a 100644 --- a/backend/tests/uxbox/tests/test_services_colors.clj +++ b/backend/tests/uxbox/tests/test_services_colors.clj @@ -22,74 +22,77 @@ (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) -(t/deftest color-libraries-crud - (let [id (uuid/next) - prof (th/create-profile db/pool 2) - team-id (:default-team-id prof)] - - (t/testing "create library" - (let [data {::sm/type :create-color-library - :name "sample library" - :profile-id (:id prof) - :team-id team-id - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= id (:id result))) - (t/is (= team-id (:team-id result))) - (t/is (= (:name data) (:name result)))))) - - (t/testing "update library" - (let [data {::sm/type :rename-color-library - :name "renamed" - :profile-id (:id prof) - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= "renamed" (get-in result [:name])))))) - - (t/testing "delete library" - (let [data {::sm/type :delete-color-library - :profile-id (:id prof) - :id id} - - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (nil? (:result out))))) - - (t/testing "query libraries after delete" - (let [data {::sq/type :color-libraries - :profile-id (:id prof) - :team-id team-id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (let [result (:result out)] - (t/is (= 0 (count result)))))) - )) +;; (t/deftest color-libraries-crud +;; (let [id (uuid/next) +;; prof (th/create-profile db/pool 2) +;; team-id (:default-team-id prof) +;; proj (th/create-project db/pool (:id prof) team-id 2)] +;; +;; (t/testing "create library file" +;; (let [data {::sm/type :create-file +;; :profile-id (:id prof) +;; :name "sample library" +;; :project-id (:id proj) +;; :id id +;; :is-shared true} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= id (:id result))) +;; (t/is (= (:id proj) (:project-id result))) +;; (t/is (= (:name data) (:name result)))))) +;; +;; (t/testing "update library file" +;; (let [data {::sm/type :rename-color-library +;; :name "renamed" +;; :profile-id (:id prof) +;; :id id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= "renamed" (get-in result [:name])))))) +;; +;; (t/testing "delete library" +;; (let [data {::sm/type :delete-color-library +;; :profile-id (:id prof) +;; :id id} +;; +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (t/is (nil? (:result out))))) +;; +;; (t/testing "query libraries after delete" +;; (let [data {::sq/type :color-libraries +;; :profile-id (:id prof) +;; :team-id team-id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (let [result (:result out)] +;; (t/is (= 0 (count result)))))) +;; )) (t/deftest colors-crud (let [prof (th/create-profile db/pool 1) team-id (:default-team-id prof) - coll (th/create-color-library db/pool team-id 1) + proj (th/create-project db/pool (:id prof) team-id 1) + file (th/create-file db/pool (:id prof) (:id proj) true 1) color-id (uuid/next)] (t/testing "upload color to library" (let [data {::sm/type :create-color :id color-id :profile-id (:id prof) - :library-id (:id coll) + :file-id (:id file) :name "testfile" :content "#222222"} out (th/try-on! (sm/handle data))] @@ -102,17 +105,17 @@ (t/is (= (:name data) (:name result))) (t/is (= (:content data) (:content result)))))) - (t/testing "list colors by library" + (t/testing "list colors by library file" (let [data {::sq/type :colors :profile-id (:id prof) - :library-id (:id coll)} + :file-id (:id file)} out (th/try-on! (sq/handle data))] ;; (th/print-result! out) (t/is (= color-id (get-in out [:result 0 :id]))) (t/is (= "testfile" (get-in out [:result 0 :name]))))) - (t/testing "single color" + (t/testing "get single color" (let [data {::sq/type :color :profile-id (:id prof) :id color-id} @@ -150,7 +153,7 @@ (t/testing "query colors after delete" (let [data {::sq/type :colors :profile-id (:id prof) - :library-id (:id coll)} + :file-id (:id file)} out (th/try-on! (sq/handle data))] ;; (th/print-result! out) (let [result (:result out)] diff --git a/backend/tests/uxbox/tests/test_services_files.clj b/backend/tests/uxbox/tests/test_services_files.clj index 796d7c2e6..3ef20d6f2 100644 --- a/backend/tests/uxbox/tests/test_services_files.clj +++ b/backend/tests/uxbox/tests/test_services_files.clj @@ -14,7 +14,6 @@ [uxbox.common.uuid :as uuid] [uxbox.db :as db] [uxbox.http :as http] - [uxbox.media :as media] [uxbox.services.mutations :as sm] [uxbox.services.queries :as sq] [uxbox.tests.helpers :as th] @@ -35,6 +34,7 @@ :profile-id (:id prof) :project-id proj-id :id file-id + :is-shared false :name "test file"} out (th/try-on! (sm/handle data))] @@ -129,105 +129,105 @@ (t/is (= 0 (count result)))))) )) -(t/deftest file-images-crud - (let [prof (th/create-profile db/pool 1) - team-id (:default-team-id prof) - proj-id (:default-project-id prof) - file (th/create-file db/pool (:id prof) proj-id 1)] - - (t/testing "create file image from url" - (let [url "https://raw.githubusercontent.com/uxbox/uxbox/develop/frontend/resources/images/penpot-login.jpg" - data {::sm/type :add-file-image-from-url - :profile-id (:id prof) - :file-id (:id file) - :url url} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= (:id file) (:file-id result))) - (t/is (not (nil? (:name result)))) - (t/is (= 787 (:width result))) - (t/is (= 2000 (:height result))) - (t/is (= "image/jpeg" (:mtype result))) - - (t/is (string? (:path result))) - (t/is (string? (:uri result))) - (t/is (string? (:thumb-path result))) - (t/is (string? (:thumb-uri result)))))) - - (t/testing "upload file image" - (let [content {:filename "sample.jpg" - :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") - :content-type "image/jpeg" - :size 312043} - data {::sm/type :upload-file-image - :profile-id (:id prof) - :file-id (:id file) - :name "testfile" - :content content} - - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= (:id file) (:file-id result))) - (t/is (= (:name data) (:name result))) - (t/is (= 800 (:width result))) - (t/is (= 800 (:height result))) - (t/is (= (:content-type content) (:mtype result))) - - (t/is (string? (:path result))) - (t/is (string? (:uri result))) - (t/is (string? (:thumb-path result))) - (t/is (string? (:thumb-uri result)))))) - - (t/testing "import from library" - (let [lib (th/create-image-library db/pool team-id 1) - image-id (uuid/next) - - content {:filename "sample.jpg" - :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") - :content-type "image/jpeg" - :size 312043} - - data {::sm/type :upload-image - :id image-id - :profile-id (:id prof) - :library-id (:id lib) - :name "testfile" - :content content} - out1 (th/try-on! (sm/handle data))] - - ;; (th/print-result! out1) - (t/is (nil? (:error out1))) - - (let [result (:result out1)] - (t/is (= image-id (:id result))) - (t/is (= "testfile" (:name result))) - (t/is (= "image/jpeg" (:mtype result))) - (t/is (= "image/jpeg" (:thumb-mtype result)))) - - (let [data2 {::sm/type :import-image-to-file - :image-id image-id - :file-id (:id file) - :profile-id (:id prof)} - out2 (th/try-on! (sm/handle data2))] - - ;; (th/print-result! out2) - (t/is (nil? (:error out2))) - - (let [result1 (:result out1) - result2 (:result out2)] - (t/is (not= (:path result2) - (:path result1))) - (t/is (not= (:thumb-path result2) - (:thumb-path result1))))))) - )) +;; (t/deftest file-images-crud +;; (let [prof (th/create-profile db/pool 1) +;; team-id (:default-team-id prof) +;; proj-id (:default-project-id prof) +;; file (th/create-file db/pool (:id prof) proj-id 1)] +;; +;; (t/testing "create file image from url" +;; (let [url "https://raw.githubusercontent.com/uxbox/uxbox/develop/frontend/resources/images/penpot-login.jpg" +;; data {::sm/type :add-file-image-from-url +;; :profile-id (:id prof) +;; :file-id (:id file) +;; :url url} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= (:id file) (:file-id result))) +;; (t/is (not (nil? (:name result)))) +;; (t/is (= 787 (:width result))) +;; (t/is (= 2000 (:height result))) +;; (t/is (= "image/jpeg" (:mtype result))) +;; +;; (t/is (string? (:path result))) +;; (t/is (string? (:uri result))) +;; (t/is (string? (:thumb-path result))) +;; (t/is (string? (:thumb-uri result)))))) +;; +;; (t/testing "upload file image" +;; (let [content {:filename "sample.jpg" +;; :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") +;; :content-type "image/jpeg" +;; :size 312043} +;; data {::sm/type :upload-file-image +;; :profile-id (:id prof) +;; :file-id (:id file) +;; :name "testfile" +;; :content content} +;; +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= (:id file) (:file-id result))) +;; (t/is (= (:name data) (:name result))) +;; (t/is (= 800 (:width result))) +;; (t/is (= 800 (:height result))) +;; (t/is (= (:content-type content) (:mtype result))) +;; +;; (t/is (string? (:path result))) +;; (t/is (string? (:uri result))) +;; (t/is (string? (:thumb-path result))) +;; (t/is (string? (:thumb-uri result)))))) +;; +;; ;; (t/testing "import from library" +;; ;; (let [lib (th/create-image-library db/pool team-id 1) +;; ;; image-id (uuid/next) +;; ;; +;; ;; content {:filename "sample.jpg" +;; ;; :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") +;; ;; :content-type "image/jpeg" +;; ;; :size 312043} +;; ;; +;; ;; data {::sm/type :upload-image +;; ;; :id image-id +;; ;; :profile-id (:id prof) +;; ;; :library-id (:id lib) +;; ;; :name "testfile" +;; ;; :content content} +;; ;; out1 (th/try-on! (sm/handle data))] +;; ;; +;; ;; ;; (th/print-result! out1) +;; ;; (t/is (nil? (:error out1))) +;; ;; +;; ;; (let [result (:result out1)] +;; ;; (t/is (= image-id (:id result))) +;; ;; (t/is (= "testfile" (:name result))) +;; ;; (t/is (= "image/jpeg" (:mtype result))) +;; ;; (t/is (= "image/jpeg" (:thumb-mtype result)))) +;; ;; +;; ;; (let [data2 {::sm/type :import-image-to-file +;; ;; :image-id image-id +;; ;; :file-id (:id file) +;; ;; :profile-id (:id prof)} +;; ;; out2 (th/try-on! (sm/handle data2))] +;; ;; +;; ;; ;; (th/print-result! out2) +;; ;; (t/is (nil? (:error out2))) +;; ;; +;; ;; (let [result1 (:result out1) +;; ;; result2 (:result out2)] +;; ;; (t/is (not= (:path result2) +;; ;; (:path result1))) +;; ;; (t/is (not= (:thumb-path result2) +;; ;; (:thumb-path result1))))))) +;; )) ;; TODO: delete file image diff --git a/backend/tests/uxbox/tests/test_services_icons.clj b/backend/tests/uxbox/tests/test_services_icons.clj index d52e5920a..ea5215b7e 100644 --- a/backend/tests/uxbox/tests/test_services_icons.clj +++ b/backend/tests/uxbox/tests/test_services_icons.clj @@ -19,162 +19,162 @@ [uxbox.tests.helpers :as th] [uxbox.util.storage :as ust])) -(t/use-fixtures :once th/state-init) -(t/use-fixtures :each th/database-reset) - -(t/deftest icon-libraries-crud - (let [id (uuid/next) - prof (th/create-profile db/pool 2) - team-id (:default-team-id prof)] - - (t/testing "create library" - (let [data {::sm/type :create-icon-library - :name "sample library" - :profile-id (:id prof) - :team-id team-id - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= id (:id result))) - (t/is (= team-id (:team-id result))) - (t/is (= (:name data) (:name result)))))) - - (t/testing "rename library" - (let [data {::sm/type :rename-icon-library - :name "renamed" - :profile-id (:id prof) - :team-id team-id - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= id (:id result))) - (t/is (= "renamed" (:name result)))))) - - (t/testing "query libraries" - (let [data {::sq/type :icon-libraries - :profile-id (:id prof) - :team-id team-id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= 1 (count result))) - (t/is (= id (get-in result [0 :id]))) - (t/is (= "renamed" (get-in result [0 :name])))))) - - (t/testing "delete library" - (let [data {::sm/type :delete-icon-library - :profile-id (:id prof) - :id id} - - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (nil? (:result out))))) - - (t/testing "query libraries after delete" - (let [data {::sq/type :icon-libraries - :profile-id (:id prof) - :team-id team-id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= 0 (count result)))))) - )) - -(t/deftest icons-crud - (let [prof (th/create-profile db/pool 1) - team-id (:default-team-id prof) - coll (th/create-icon-library db/pool team-id 1) - icon-id (uuid/next)] - - (t/testing "upload icon to library" - (let [data {::sm/type :create-icon - :id icon-id - :profile-id (:id prof) - :library-id (:id coll) - :name "testfile" - :content "" - :metadata {:width 100 - :height 100 - :view-box [0 0 100 100] - :mimetype "text/svg"}} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= (:id data) (:id result))) - (t/is (= (:name data) (:name result))) - (t/is (= (:content data) (:content result)))))) - - (t/testing "list icons by library" - (let [data {::sq/type :icons - :profile-id (:id prof) - :library-id (:id coll)} - out (th/try-on! (sq/handle data))] - ;; (th/print-result! out) - - (t/is (= icon-id (get-in out [:result 0 :id]))) - (t/is (= "testfile" (get-in out [:result 0 :name]))))) - - (t/testing "single icon" - (let [data {::sq/type :icon - :profile-id (:id prof) - :id icon-id} - out (th/try-on! (sq/handle data))] - ;; (th/print-result! out) - - (t/is (= icon-id (get-in out [:result :id]))) - (t/is (= "testfile" (get-in out [:result :name]))))) - - (t/testing "delete icons" - (let [data {::sm/type :delete-icon - :profile-id (:id prof) - :id icon-id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (nil? (:result out))))) - - (t/testing "query icon after delete" - (let [data {::sq/type :icon - :profile-id (:id prof) - :id icon-id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (let [error (:error out)] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :service-error))) - - (let [error (ex-cause (:error out))] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :not-found))))) - - (t/testing "query icons after delete" - (let [data {::sq/type :icons - :profile-id (:id prof) - :library-id (:id coll)} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (let [result (:result out)] - (t/is (= 0 (count result)))))) - )) +;; (t/use-fixtures :once th/state-init) +;; (t/use-fixtures :each th/database-reset) +;; +;; (t/deftest icon-libraries-crud +;; (let [id (uuid/next) +;; prof (th/create-profile db/pool 2) +;; team-id (:default-team-id prof)] +;; +;; (t/testing "create library" +;; (let [data {::sm/type :create-icon-library +;; :name "sample library" +;; :profile-id (:id prof) +;; :team-id team-id +;; :id id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= id (:id result))) +;; (t/is (= team-id (:team-id result))) +;; (t/is (= (:name data) (:name result)))))) +;; +;; (t/testing "rename library" +;; (let [data {::sm/type :rename-icon-library +;; :name "renamed" +;; :profile-id (:id prof) +;; :team-id team-id +;; :id id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= id (:id result))) +;; (t/is (= "renamed" (:name result)))))) +;; +;; (t/testing "query libraries" +;; (let [data {::sq/type :icon-libraries +;; :profile-id (:id prof) +;; :team-id team-id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= 1 (count result))) +;; (t/is (= id (get-in result [0 :id]))) +;; (t/is (= "renamed" (get-in result [0 :name])))))) +;; +;; (t/testing "delete library" +;; (let [data {::sm/type :delete-icon-library +;; :profile-id (:id prof) +;; :id id} +;; +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (t/is (nil? (:result out))))) +;; +;; (t/testing "query libraries after delete" +;; (let [data {::sq/type :icon-libraries +;; :profile-id (:id prof) +;; :team-id team-id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= 0 (count result)))))) +;; )) +;; +;; (t/deftest icons-crud +;; (let [prof (th/create-profile db/pool 1) +;; team-id (:default-team-id prof) +;; coll (th/create-icon-library db/pool team-id 1) +;; icon-id (uuid/next)] +;; +;; (t/testing "upload icon to library" +;; (let [data {::sm/type :create-icon +;; :id icon-id +;; :profile-id (:id prof) +;; :library-id (:id coll) +;; :name "testfile" +;; :content "" +;; :metadata {:width 100 +;; :height 100 +;; :view-box [0 0 100 100] +;; :mimetype "text/svg"}} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= (:id data) (:id result))) +;; (t/is (= (:name data) (:name result))) +;; (t/is (= (:content data) (:content result)))))) +;; +;; (t/testing "list icons by library" +;; (let [data {::sq/type :icons +;; :profile-id (:id prof) +;; :library-id (:id coll)} +;; out (th/try-on! (sq/handle data))] +;; ;; (th/print-result! out) +;; +;; (t/is (= icon-id (get-in out [:result 0 :id]))) +;; (t/is (= "testfile" (get-in out [:result 0 :name]))))) +;; +;; (t/testing "single icon" +;; (let [data {::sq/type :icon +;; :profile-id (:id prof) +;; :id icon-id} +;; out (th/try-on! (sq/handle data))] +;; ;; (th/print-result! out) +;; +;; (t/is (= icon-id (get-in out [:result :id]))) +;; (t/is (= "testfile" (get-in out [:result :name]))))) +;; +;; (t/testing "delete icons" +;; (let [data {::sm/type :delete-icon +;; :profile-id (:id prof) +;; :id icon-id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (t/is (nil? (:result out))))) +;; +;; (t/testing "query icon after delete" +;; (let [data {::sq/type :icon +;; :profile-id (:id prof) +;; :id icon-id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (let [error (:error out)] +;; (t/is (th/ex-info? error)) +;; (t/is (th/ex-of-type? error :service-error))) +;; +;; (let [error (ex-cause (:error out))] +;; (t/is (th/ex-info? error)) +;; (t/is (th/ex-of-type? error :not-found))))) +;; +;; (t/testing "query icons after delete" +;; (let [data {::sq/type :icons +;; :profile-id (:id prof) +;; :library-id (:id coll)} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (let [result (:result out)] +;; (t/is (= 0 (count result)))))) +;; )) diff --git a/backend/tests/uxbox/tests/test_services_images.clj b/backend/tests/uxbox/tests/test_services_images.clj deleted file mode 100644 index ec7b2b757..000000000 --- a/backend/tests/uxbox/tests/test_services_images.clj +++ /dev/null @@ -1,237 +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/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2020 UXBOX Labs SL - -(ns uxbox.tests.test-services-images - (:require - [clojure.test :as t] - [datoteka.core :as fs] - [uxbox.common.uuid :as uuid] - [uxbox.db :as db] - [uxbox.services.mutations :as sm] - [uxbox.services.queries :as sq] - [uxbox.tests.helpers :as th] - [uxbox.util.storage :as ust])) - -(t/use-fixtures :once th/state-init) -(t/use-fixtures :each th/database-reset) - -(t/deftest image-libraries-crud - (let [id (uuid/next) - prof (th/create-profile db/pool 2) - team-id (:default-team-id prof)] - - (t/testing "create library" - (let [data {::sm/type :create-image-library - :name "sample library" - :profile-id (:id prof) - :team-id team-id - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= team-id (:team-id result))) - (t/is (= (:name data) (:name result)))))) - - (t/testing "rename library" - (let [data {::sm/type :rename-image-library - :name "renamed" - :profile-id (:id prof) - :id id} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= id (:id result))) - (t/is (= "renamed" (:name result)))))) - - (t/testing "query single library" - (let [data {::sq/type :image-library - :profile-id (:id prof) - :id id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= id (:id result))) - (t/is (= "renamed" (:name result)))))) - - (t/testing "query libraries" - (let [data {::sq/type :image-libraries - :team-id team-id - :profile-id (:id prof)} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (let [result (:result out)] - (t/is (= 1 (count result))) - (t/is (= id (get-in result [0 :id])))))) - - (t/testing "delete library" - (let [data {::sm/type :delete-image-library - :profile-id (:id prof) - :id id} - - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (nil? (:result out))))) - - (t/testing "query libraries after delete" - (let [data {::sq/type :image-libraries - :profile-id (:id prof) - :team-id team-id} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (= 0 (count (:result out)))))) - )) - -(t/deftest images-crud - (let [prof (th/create-profile db/pool 1) - team-id (:default-team-id prof) - image-id-1 (uuid/next) - image-id-2 (uuid/next) - lib (th/create-image-library db/pool team-id 1)] - - (t/testing "create image from url to library" - (let [url "https://raw.githubusercontent.com/uxbox/uxbox/develop/frontend/resources/images/penpot-login.jpg" - data {::sm/type :add-image-from-url - :id image-id-1 - :profile-id (:id prof) - :library-id (:id lib) - :url url} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (t/is (= image-id-1 (get-in out [:result :id]))) - (t/is (not (nil? (get-in out [:result :name])))) - (t/is (= "image/jpeg" (get-in out [:result :mtype]))) - (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) - (t/is (= 787 (get-in out [:result :width]))) - (t/is (= 2000 (get-in out [:result :height]))) - - (t/is (string? (get-in out [:result :path]))) - (t/is (string? (get-in out [:result :thumb-path]))) - (t/is (string? (get-in out [:result :uri]))) - (t/is (string? (get-in out [:result :thumb-uri]))))) - - (t/testing "upload image to library" - (let [content {:filename "sample.jpg" - :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") - :content-type "image/jpeg" - :size 312043} - data {::sm/type :upload-image - :id image-id-2 - :profile-id (:id prof) - :library-id (:id lib) - :name "testfile" - :content content} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - - (t/is (= image-id-2 (get-in out [:result :id]))) - (t/is (= "testfile" (get-in out [:result :name]))) - (t/is (= "image/jpeg" (get-in out [:result :mtype]))) - (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) - (t/is (= 800 (get-in out [:result :width]))) - (t/is (= 800 (get-in out [:result :height]))) - - (t/is (string? (get-in out [:result :path]))) - (t/is (string? (get-in out [:result :thumb-path]))) - (t/is (string? (get-in out [:result :uri]))) - (t/is (string? (get-in out [:result :thumb-uri]))))) - - (t/testing "list images by library" - (let [data {::sq/type :images - :profile-id (:id prof) - :library-id (:id lib)} - out (th/try-on! (sq/handle data))] - ;; (th/print-result! out) - - ;; Result is ordered by creation date descendent - (t/is (= image-id-2 (get-in out [:result 0 :id]))) - (t/is (= "testfile" (get-in out [:result 0 :name]))) - (t/is (= "image/jpeg" (get-in out [:result 0 :mtype]))) - (t/is (= "image/jpeg" (get-in out [:result 0 :thumb-mtype]))) - (t/is (= 800 (get-in out [:result 0 :width]))) - (t/is (= 800 (get-in out [:result 0 :height]))) - - (t/is (string? (get-in out [:result 0 :path]))) - (t/is (string? (get-in out [:result 0 :thumb-path]))) - (t/is (string? (get-in out [:result 0 :uri]))) - (t/is (string? (get-in out [:result 0 :thumb-uri]))))) - - (t/testing "single image" - (let [data {::sq/type :image - :profile-id (:id prof) - :id image-id-2} - out (th/try-on! (sq/handle data))] - ;; (th/print-result! out) - - (t/is (= image-id-2 (get-in out [:result :id]))) - (t/is (= "testfile" (get-in out [:result :name]))) - (t/is (= "image/jpeg" (get-in out [:result :mtype]))) - (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) - (t/is (= 800 (get-in out [:result :width]))) - (t/is (= 800 (get-in out [:result :height]))) - - (t/is (string? (get-in out [:result :path]))) - (t/is (string? (get-in out [:result :thumb-path]))) - (t/is (string? (get-in out [:result :uri]))) - (t/is (string? (get-in out [:result :thumb-uri]))))) - - (t/testing "delete images" - (let [data {::sm/type :delete-image - :profile-id (:id prof) - :id image-id-1} - out (th/try-on! (sm/handle data))] - - ;; (th/print-result! out) - (t/is (nil? (:error out))) - (t/is (nil? (:result out))))) - - (t/testing "query image after delete" - (let [data {::sq/type :image - :profile-id (:id prof) - :id image-id-1} - out (th/try-on! (sq/handle data))] - - ;; (th/print-result! out) - (let [error (:error out)] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :service-error))) - - (let [error (ex-cause (:error out))] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :not-found))))) - - (t/testing "query images after delete" - (let [data {::sq/type :images - :profile-id (:id prof) - :library-id (:id lib)} - out (th/try-on! (sq/handle data))] - ;; (th/print-result! out) - (let [result (:result out)] - (t/is (= 1 (count result)))))) - )) diff --git a/backend/tests/uxbox/tests/test_services_media.clj b/backend/tests/uxbox/tests/test_services_media.clj new file mode 100644 index 000000000..3814629ba --- /dev/null +++ b/backend/tests/uxbox/tests/test_services_media.clj @@ -0,0 +1,242 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns uxbox.tests.test-services-media + (:require + [clojure.test :as t] + [datoteka.core :as fs] + [uxbox.common.uuid :as uuid] + [uxbox.db :as db] + [uxbox.services.mutations :as sm] + [uxbox.services.queries :as sq] + [uxbox.tests.helpers :as th] + [uxbox.util.storage :as ust])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +;; (t/deftest image-libraries-crud +;; (let [id (uuid/next) +;; prof (th/create-profile db/pool 2) +;; team-id (:default-team-id prof)] +;; +;; (t/testing "create library" +;; (let [data {::sm/type :create-image-library +;; :name "sample library" +;; :profile-id (:id prof) +;; :team-id team-id +;; :id id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= team-id (:team-id result))) +;; (t/is (= (:name data) (:name result)))))) +;; +;; (t/testing "rename library" +;; (let [data {::sm/type :rename-image-library +;; :name "renamed" +;; :profile-id (:id prof) +;; :id id} +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= id (:id result))) +;; (t/is (= "renamed" (:name result)))))) +;; +;; (t/testing "query single library" +;; (let [data {::sq/type :image-library +;; :profile-id (:id prof) +;; :id id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= id (:id result))) +;; (t/is (= "renamed" (:name result)))))) +;; +;; (t/testing "query libraries" +;; (let [data {::sq/type :image-libraries +;; :team-id team-id +;; :profile-id (:id prof)} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; +;; (let [result (:result out)] +;; (t/is (= 1 (count result))) +;; (t/is (= id (get-in result [0 :id])))))) +;; +;; (t/testing "delete library" +;; (let [data {::sm/type :delete-image-library +;; :profile-id (:id prof) +;; :id id} +;; +;; out (th/try-on! (sm/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (t/is (nil? (:result out))))) +;; +;; (t/testing "query libraries after delete" +;; (let [data {::sq/type :image-libraries +;; :profile-id (:id prof) +;; :team-id team-id} +;; out (th/try-on! (sq/handle data))] +;; +;; ;; (th/print-result! out) +;; (t/is (nil? (:error out))) +;; (t/is (= 0 (count (:result out)))))) +;; )) + +(t/deftest media-crud + (let [prof (th/create-profile db/pool 1) + team-id (:default-team-id prof) + proj (th/create-project db/pool (:id prof) team-id 1) + file (th/create-file db/pool (:id prof) (:id proj) false 1) + object-id-1 (uuid/next) + object-id-2 (uuid/next)] + + (t/testing "create media object from url to file" + (let [url "https://raw.githubusercontent.com/uxbox/uxbox/develop/frontend/resources/images/penpot-login.jpg" + data {::sm/type :add-media-object-from-url + :id object-id-1 + :profile-id (:id prof) + :file-id (:id file) + :url url + :is-local true} + out (th/try-on! (sm/handle data))] + + ;; (th/print-result! out) + (t/is (nil? (:error out))) + + (t/is (= object-id-1 (get-in out [:result :id]))) + (t/is (not (nil? (get-in out [:result :name])))) + (t/is (= "image/jpeg" (get-in out [:result :mtype]))) + ;; (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) + (t/is (= 787 (get-in out [:result :width]))) + (t/is (= 2000 (get-in out [:result :height]))) + + (t/is (string? (get-in out [:result :path]))) + ;; (t/is (string? (get-in out [:result :thumb-path]))) + (t/is (string? (get-in out [:result :uri]))))) + ;; (t/is (string? (get-in out [:result :thumb-uri]))))) + + (t/testing "upload media object to file" + (let [content {:filename "sample.jpg" + :tempfile (th/tempfile "uxbox/tests/_files/sample.jpg") + :content-type "image/jpeg" + :size 312043} + data {::sm/type :upload-media-object + :id object-id-2 + :profile-id (:id prof) + :file-id (:id file) + :name "testfile" + :content content + :is-local true} + out (th/try-on! (sm/handle data))] + + ;; (th/print-result! out) + (t/is (nil? (:error out))) + + (t/is (= object-id-2 (get-in out [:result :id]))) + (t/is (= "testfile" (get-in out [:result :name]))) + (t/is (= "image/jpeg" (get-in out [:result :mtype]))) + ;; (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) + (t/is (= 800 (get-in out [:result :width]))) + (t/is (= 800 (get-in out [:result :height]))) + + (t/is (string? (get-in out [:result :path]))) + ;; (t/is (string? (get-in out [:result :thumb-path]))) + (t/is (string? (get-in out [:result :uri]))))) + ;; (t/is (string? (get-in out [:result :thumb-uri]))))) + + (t/testing "list media objects by file" + (let [data {::sq/type :media-objects + :profile-id (:id prof) + :file-id (:id file) + :is-local true} + out (th/try-on! (sq/handle data))] + ;; (th/print-result! out) + + ;; Result is ordered by creation date descendent + (t/is (= object-id-2 (get-in out [:result 0 :id]))) + (t/is (= "testfile" (get-in out [:result 0 :name]))) + (t/is (= "image/jpeg" (get-in out [:result 0 :mtype]))) + ;; (t/is (= "image/jpeg" (get-in out [:result 0 :thumb-mtype]))) + (t/is (= 800 (get-in out [:result 0 :width]))) + (t/is (= 800 (get-in out [:result 0 :height]))) + + (t/is (string? (get-in out [:result 0 :path]))) + ;; (t/is (string? (get-in out [:result 0 :thumb-path]))) + (t/is (string? (get-in out [:result 0 :uri]))))) + ;; (t/is (string? (get-in out [:result 0 :thumb-uri]))))) + + (t/testing "single media object" + (let [data {::sq/type :media-object + :profile-id (:id prof) + :id object-id-2} + out (th/try-on! (sq/handle data))] + ;; (th/print-result! out) + + (t/is (= object-id-2 (get-in out [:result :id]))) + (t/is (= "testfile" (get-in out [:result :name]))) + (t/is (= "image/jpeg" (get-in out [:result :mtype]))) + ;; (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) + (t/is (= 800 (get-in out [:result :width]))) + (t/is (= 800 (get-in out [:result :height]))) + + (t/is (string? (get-in out [:result :path]))) + ;; (t/is (string? (get-in out [:result :thumb-path]))) + (t/is (string? (get-in out [:result :uri]))))) + ;; (t/is (string? (get-in out [:result :thumb-uri]))))) + + (t/testing "delete media objects" + (let [data {::sm/type :delete-media-object + :profile-id (:id prof) + :id object-id-1} + out (th/try-on! (sm/handle data))] + + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (nil? (:result out))))) + + (t/testing "query media object after delete" + (let [data {::sq/type :media-object + :profile-id (:id prof) + :id object-id-1} + out (th/try-on! (sq/handle data))] + + ;; (th/print-result! out) + (let [error (:error out)] + (t/is (th/ex-info? error)) + (t/is (th/ex-of-type? error :service-error))) + + (let [error (ex-cause (:error out))] + (t/is (th/ex-info? error)) + (t/is (th/ex-of-type? error :not-found))))) + + (t/testing "query media objects after delete" + (let [data {::sq/type :media-objects + :profile-id (:id prof) + :file-id (:id file) + :is-local true} + out (th/try-on! (sq/handle data))] + ;; (th/print-result! out) + (let [result (:result out)] + (t/is (= 1 (count result)))))) + )) diff --git a/backend/tests/uxbox/tests/test_services_pages.clj b/backend/tests/uxbox/tests/test_services_pages.clj index 06e167eef..f463194f5 100644 --- a/backend/tests/uxbox/tests/test_services_pages.clj +++ b/backend/tests/uxbox/tests/test_services_pages.clj @@ -27,7 +27,7 @@ (let [prof (th/create-profile db/pool 1) team-id (:default-team-id prof) proj-id (:default-project-id prof) - file (th/create-file db/pool (:id prof) proj-id 1) + file (th/create-file db/pool (:id prof) proj-id false 1) page-id (uuid/next)] (t/testing "create page" @@ -104,7 +104,7 @@ (let [prof (th/create-profile db/pool 1) team-id (:default-team-id prof) proj-id (:default-project-id prof) - file (th/create-file db/pool (:id prof) proj-id 1) + file (th/create-file db/pool (:id prof) proj-id false 1) page-id (uuid/next)] (t/testing "create empty page" @@ -182,7 +182,7 @@ (let [prof (th/create-profile db/pool 1) team-id (:default-team-id prof) proj-id (:default-project-id prof) - file (th/create-file db/pool (:id prof) proj-id 1) + file (th/create-file db/pool (:id prof) proj-id false 1) page (th/create-page db/pool (:id prof) (:id file) 1)] (t/testing "lagging changes" diff --git a/backend/tests/uxbox/tests/test_services_viewer.clj b/backend/tests/uxbox/tests/test_services_viewer.clj index c2f4dd912..287da13df 100644 --- a/backend/tests/uxbox/tests/test_services_viewer.clj +++ b/backend/tests/uxbox/tests/test_services_viewer.clj @@ -14,7 +14,6 @@ [uxbox.common.uuid :as uuid] [uxbox.db :as db] [uxbox.http :as http] - [uxbox.media :as media] [uxbox.services.mutations :as sm] [uxbox.services.queries :as sq] [uxbox.tests.helpers :as th] @@ -29,7 +28,7 @@ team-id (:default-team-id prof) proj-id (:default-project-id prof) - file (th/create-file db/pool (:id prof) proj-id 1) + file (th/create-file db/pool (:id prof) proj-id false 1) page (th/create-page db/pool (:id prof) (:id file) 1) token (atom nil)]