0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 16:21:57 -05:00

♻️ The big media refactor (backend)

This commit is contained in:
Andrés Moya 2020-08-05 14:31:08 +02:00 committed by Andrey Antukh
parent 8b1ba8c020
commit bd7114182f
33 changed files with 2037 additions and 1937 deletions

View file

@ -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;

View file

@ -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

View file

@ -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]))

View file

@ -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/<!! (a/thread
(try
(process params)
(catch Throwable e
e))))]
(if (instance? Throwable res)
(throw res)
res))
(finally
(.release semaphore))))
(defn resolve-urls
[row src dst]
(s/assert map? row)
(if (and src dst)
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri media/media-storage value)]
(assoc-in row dst (str url)))))
row))
(defn- resolve-uri
[storage row src dst]
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri media/media-storage value)]
(assoc-in row dst (str url))))))
(defn resolve-media-uris
[row & pairs]
(us/assert map? row)
(us/assert (s/coll-of vector?) pairs)
(reduce #(resolve-uri media/media-storage %1 (nth %2 0) (nth %2 1)) row pairs))
(defn download-image
[url]
(let [result (http/get! url {:as :byte-array})
data (:body result)
content-type (get (:headers result) "content-type")
format (mtype->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}))))

View file

@ -28,7 +28,7 @@
[& args]
(require 'uxbox.config
'uxbox.migrations
'uxbox.images
'uxbox.media
'uxbox.http
'uxbox.tasks)
(mount/start))

View file

@ -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 <niwi@niwi.nz>
;; 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/<!! (a/thread
(try
(process params)
(catch Throwable e
e))))]
(if (instance? Throwable res)
(throw res)
res))
(finally
(.release semaphore))))
(defn resolve-urls
[row src dst]
(s/assert map? row)
(if (and src dst)
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri mst/media-storage value)]
(assoc-in row dst (str url)))))
row))
(defn- resolve-uri
[storage row src dst]
(let [src (if (vector? src) src [src])
dst (if (vector? dst) dst [dst])
value (get-in row src)]
(if (empty? value)
row
(let [url (ust/public-uri mst/media-storage value)]
(assoc-in row dst (str url))))))
(defn resolve-media-uris
[row & pairs]
(us/assert map? row)
(us/assert (s/coll-of vector?) pairs)
(reduce #(resolve-uri mst/media-storage %1 (nth %2 0) (nth %2 1)) row pairs))
(defn download-media-object
[url]
(let [result (http/get! url {:as :byte-array})
data (:body result)
content-type (get (:headers result) "content-type")
format (mtype->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)))

View file

@ -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?

View file

@ -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 <niwi@niwi.nz>
(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)))

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)))

View file

@ -7,200 +7,200 @@
;;
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
(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)))

View file

@ -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)))

View file

@ -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)))

View file

@ -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]

View file

@ -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))

View file

@ -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]))

View file

@ -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 <niwi@niwi.nz>
;;
;; 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 <niwi@niwi.nz>
(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))

View file

@ -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 <niwi@niwi.nz>
(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))

View file

@ -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 <niwi@niwi.nz>
(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))

View file

@ -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)

View file

@ -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))

View file

@ -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

View file

@ -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!

View file

@ -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]

View file

@ -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)]

View file

@ -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

View file

@ -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 "<rect></rect>"
: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 "<rect></rect>"
;; :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))))))
;; ))

View file

@ -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))))))
))

View file

@ -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))))))
))

View file

@ -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"

View file

@ -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)]