diff --git a/backend/src/uxbox/services/mutations/images.clj b/backend/src/uxbox/services/mutations/images.clj index c335a0fe7..90463c9a4 100644 --- a/backend/src/uxbox/services/mutations/images.clj +++ b/backend/src/uxbox/services/mutations/images.clj @@ -10,6 +10,7 @@ (ns uxbox.services.mutations.images (:require [clojure.spec.alpha :as s] + [clojure.java.io :as io] [datoteka.core :as fs] [uxbox.common.exceptions :as ex] [uxbox.common.spec :as us] @@ -22,7 +23,8 @@ [uxbox.services.queries.teams :as teams] [uxbox.tasks :as tasks] [uxbox.util.storage :as ust] - [uxbox.util.time :as dt])) + [uxbox.util.time :as dt] + [uxbox.util.http :as http])) (def thumbnail-options {:width 800 @@ -35,7 +37,7 @@ (s/def ::profile-id ::us/uuid) (s/def ::library-id ::us/uuid) (s/def ::team-id ::us/uuid) - +(s/def ::url ::us/url) ;; --- Create Library @@ -108,6 +110,7 @@ ;; --- Create Image (Upload) +(declare download-image) (declare create-image) (declare persist-image-on-fs) (declare persist-image-thumbnail-on-fs) @@ -128,10 +131,43 @@ (s/def ::content ::upload) -(s/def ::upload-image - (s/keys :req-un [::profile-id ::name ::content ::library-id] +(s/def ::add-image-from-url + (s/keys :req-un [::profile-id ::library-id ::name ::url] :opt-un [::id])) +(s/def ::upload-image + (s/keys :req-un [::profile-id ::library-id ::name ::content] + :opt-un [::id])) + +(sm/defmutation ::add-image-from-url + [{:keys [library-id profile-id url] :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)) + (let [content (download-image url) + params' (merge params {:content content})] + (create-image conn params'))))) + +(defn download-image + [url] + (let [result (http/get! url {:as :byte-array}) + data (:body result) + content-type (get (:headers result) "content-type") + format (images/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 (get (fs/split-ext (fs/name tempfile)) 0) + filename (str base-filename (images/format->extension format))] + (with-open [ostream (io/output-stream tempfile)] + (.write ostream data)) + {:filename filename + :size (count data) + :tempfile tempfile + :content-type content-type})))) + (sm/defmutation ::upload-image [{:keys [library-id profile-id] :as params}] (db/with-atomic [conn db/pool] diff --git a/backend/src/uxbox/util/http.clj b/backend/src/uxbox/util/http.clj index 4d1ce8c74..09f07adb0 100644 --- a/backend/src/uxbox/util/http.clj +++ b/backend/src/uxbox/util/http.clj @@ -14,6 +14,11 @@ (def default-client (delay (http/build-client {:executor @px/default-executor}))) +(defn get! + [url opts] + (let [opts' (merge {:client @default-client :as :string} opts)] + (http/get url nil opts'))) + (defn send! [req] (http/send req {:client @default-client :as :string})) diff --git a/backend/tests/uxbox/tests/test_services_images.clj b/backend/tests/uxbox/tests/test_services_images.clj index 92fa7e15a..baf5e57c8 100644 --- a/backend/tests/uxbox/tests/test_services_images.clj +++ b/backend/tests/uxbox/tests/test_services_images.clj @@ -104,10 +104,36 @@ )) (t/deftest images-crud - (let [prof (th/create-profile db/pool 1) - team-id (:default-team-id prof) - lib (th/create-image-library db/pool team-id 1) - image-id (uuid/next)] + (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) + :name "testfile" + :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 (= "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 (= 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" @@ -115,7 +141,7 @@ :content-type "image/jpeg" :size 312043} data {::sm/type :upload-image - :id image-id + :id image-id-2 :profile-id (:id prof) :library-id (:id lib) :name "testfile" @@ -125,7 +151,7 @@ ;; (th/print-result! out) (t/is (nil? (:error out))) - (t/is (= image-id (get-in out [:result :id]))) + (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]))) @@ -135,8 +161,7 @@ (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/is (string? (get-in out [:result :thumb-uri]))))) (t/testing "list images by library" (let [data {::sq/type :images @@ -145,7 +170,8 @@ out (th/try-on! (sq/handle data))] ;; (th/print-result! out) - (t/is (= image-id (get-in out [:result 0 :id]))) + ;; 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]))) @@ -160,11 +186,11 @@ (t/testing "single image" (let [data {::sq/type :image :profile-id (:id prof) - :id image-id} + :id image-id-2} out (th/try-on! (sq/handle data))] ;; (th/print-result! out) - (t/is (= image-id (get-in out [:result :id]))) + (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]))) @@ -179,7 +205,7 @@ (t/testing "delete images" (let [data {::sm/type :delete-image :profile-id (:id prof) - :id image-id} + :id image-id-1} out (th/try-on! (sm/handle data))] ;; (th/print-result! out) @@ -189,7 +215,7 @@ (t/testing "query image after delete" (let [data {::sq/type :image :profile-id (:id prof) - :id image-id} + :id image-id-1} out (th/try-on! (sq/handle data))] ;; (th/print-result! out) @@ -208,5 +234,5 @@ out (th/try-on! (sq/handle data))] ;; (th/print-result! out) (let [result (:result out)] - (t/is (= 0 (count result)))))) + (t/is (= 1 (count result)))))) )) diff --git a/common/uxbox/common/spec.cljc b/common/uxbox/common/spec.cljc index 569a35cfd..f0e405d82 100644 --- a/common/uxbox/common/spec.cljc +++ b/common/uxbox/common/spec.cljc @@ -105,6 +105,7 @@ (s/def ::number (s/conformer number-conformer str)) (s/def ::integer (s/conformer integer-conformer str)) (s/def ::not-empty-string (s/and string? #(not (str/empty? %)))) +(s/def ::url string?) #?(:clj (s/def ::path (s/conformer path-conformer str))) ;; --- Macros