mirror of
https://github.com/penpot/penpot.git
synced 2025-05-13 18:16:38 +02:00
♻️ Rename collections to libraries.
And make them team dependent.
This commit is contained in:
parent
0ad2b13d76
commit
3ce9c8820f
19 changed files with 1068 additions and 766 deletions
|
@ -69,6 +69,12 @@ CREATE TRIGGER team__modified_at__tgr
|
||||||
BEFORE UPDATE ON team
|
BEFORE UPDATE ON team
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
INSERT INTO team (id, name, photo, is_default)
|
||||||
|
VALUES ('00000000-0000-0000-0000-000000000000'::uuid,
|
||||||
|
'System Team',
|
||||||
|
'',
|
||||||
|
true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE team_profile_rel (
|
CREATE TABLE team_profile_rel (
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
CREATE TABLE image_collection (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
name text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX image_collection__profile_id__idx
|
|
||||||
ON image_collection(profile_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER image_collection__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON image_collection
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE image (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
collection_id uuid NOT NULL REFERENCES image_collection(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
name text NOT NULL,
|
|
||||||
|
|
||||||
path text NOT NULL,
|
|
||||||
width int NOT NULL,
|
|
||||||
height int NOT NULL,
|
|
||||||
mtype text NOT NULL,
|
|
||||||
|
|
||||||
thumb_path text NOT NULL,
|
|
||||||
thumb_width int NOT NULL,
|
|
||||||
thumb_height int NOT NULL,
|
|
||||||
thumb_quality int NOT NULL,
|
|
||||||
thumb_mtype text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX image__profile_id__idx
|
|
||||||
ON image(profile_id);
|
|
||||||
|
|
||||||
CREATE INDEX image__collection_id__idx
|
|
||||||
ON image(collection_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER image__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON image
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
||||||
|
|
||||||
CREATE TRIGGER image__on_delete__tgr
|
|
||||||
AFTER DELETE ON image
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE handle_delete();
|
|
||||||
|
|
136
backend/resources/migrations/0005.libraries.sql
Normal file
136
backend/resources/migrations/0005.libraries.sql
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
CREATE TABLE image_library (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
name text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX image_library__team_id__idx
|
||||||
|
ON image_library(team_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER image_library__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON image_library
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE image (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
library_id uuid NOT NULL REFERENCES image_library(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
name text NOT NULL,
|
||||||
|
|
||||||
|
path text NOT NULL,
|
||||||
|
width int NOT NULL,
|
||||||
|
height int NOT NULL,
|
||||||
|
mtype text NOT NULL,
|
||||||
|
|
||||||
|
thumb_path text NOT NULL,
|
||||||
|
thumb_width int NOT NULL,
|
||||||
|
thumb_height int NOT NULL,
|
||||||
|
thumb_quality int NOT NULL,
|
||||||
|
thumb_mtype text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX image__library_id__idx
|
||||||
|
ON image(library_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER image__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON image
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
CREATE TRIGGER image__on_delete__tgr
|
||||||
|
AFTER DELETE ON image
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE handle_delete();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE icon_library (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
name text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX icon_colection__team_id__idx
|
||||||
|
ON icon_library (team_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER icon_library__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON icon_library
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE icon (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
library_id uuid REFERENCES icon_library(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
|
||||||
|
name text NOT NULL,
|
||||||
|
content text NOT NULL,
|
||||||
|
metadata bytea NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX icon__library_id__idx
|
||||||
|
ON icon(library_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER icon__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON icon
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE color_library (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
name text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX color_colection__team_id__idx
|
||||||
|
ON color_library (team_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER color_library__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON color_library
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE color (
|
||||||
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
library_id uuid REFERENCES color_library(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||||
|
deleted_at timestamptz DEFAULT NULL,
|
||||||
|
|
||||||
|
name text NOT NULL,
|
||||||
|
content text NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX color__library_id__idx
|
||||||
|
ON color(library_id);
|
||||||
|
|
||||||
|
CREATE TRIGGER color__modified_at__tgr
|
||||||
|
BEFORE UPDATE ON color
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
@ -1,44 +0,0 @@
|
||||||
CREATE TABLE icon_collection (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
name text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX icon_colection__profile_id__idx
|
|
||||||
ON icon_collection (profile_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER icon_collection__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON icon_collection
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE icon (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
collection_id uuid REFERENCES icon_collection(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
|
|
||||||
name text NOT NULL,
|
|
||||||
content text NOT NULL,
|
|
||||||
metadata bytea NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX icon__profile_id__idx
|
|
||||||
ON icon(profile_id);
|
|
||||||
CREATE INDEX icon__collection_id__idx
|
|
||||||
ON icon(collection_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER icon__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON icon
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
|
@ -1,43 +0,0 @@
|
||||||
CREATE TABLE color_collection (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
name text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX color_colection__profile_id__idx
|
|
||||||
ON color_collection (profile_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER color_collection__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON color_collection
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE color (
|
|
||||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
||||||
profile_id uuid NOT NULL REFERENCES profile(id) ON DELETE CASCADE,
|
|
||||||
|
|
||||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
modified_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
||||||
deleted_at timestamptz DEFAULT NULL,
|
|
||||||
|
|
||||||
collection_id uuid REFERENCES color_collection(id)
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
|
|
||||||
name text NOT NULL,
|
|
||||||
content text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX color__profile_id__idx
|
|
||||||
ON color(profile_id);
|
|
||||||
CREATE INDEX color__collection_id__idx
|
|
||||||
ON color(collection_id);
|
|
||||||
|
|
||||||
CREATE TRIGGER color__modified_at__tgr
|
|
||||||
BEFORE UPDATE ON color
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE update_modified_at();
|
|
|
@ -12,8 +12,6 @@
|
||||||
[uxbox.util.migrations :as mg]
|
[uxbox.util.migrations :as mg]
|
||||||
[uxbox.util.template :as tmpl]))
|
[uxbox.util.template :as tmpl]))
|
||||||
|
|
||||||
;; --- Migrations
|
|
||||||
|
|
||||||
(def +migrations+
|
(def +migrations+
|
||||||
{:name "uxbox-main"
|
{:name "uxbox-main"
|
||||||
:steps
|
:steps
|
||||||
|
@ -29,20 +27,13 @@
|
||||||
{:desc "Initial tasks related tables"
|
{:desc "Initial tasks related tables"
|
||||||
:name "0004-tasks"
|
:name "0004-tasks"
|
||||||
:fn (mg/resource "migrations/0004.tasks.sql")}
|
:fn (mg/resource "migrations/0004.tasks.sql")}
|
||||||
{:desc "Initial images tables"
|
{:desc "Initial libraries tables"
|
||||||
:name "0005-images"
|
:name "0005-libraries"
|
||||||
:fn (mg/resource "migrations/0005.images.sql")}
|
:fn (mg/resource "migrations/0005.libraries.sql")}]})
|
||||||
{:desc "Initial icons tables"
|
|
||||||
:name "0006-icons"
|
|
||||||
:fn (mg/resource "migrations/0006.icons.sql")}
|
|
||||||
{:desc "Initial colors tables"
|
|
||||||
:name "0007-colors"
|
|
||||||
:fn (mg/resource "migrations/0007.colors.sql")}
|
|
||||||
]})
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Entry point
|
;; Entry point
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn migrate
|
(defn migrate
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -10,91 +10,87 @@
|
||||||
(ns uxbox.services.mutations.colors
|
(ns uxbox.services.mutations.colors
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[datoteka.core :as fs]
|
|
||||||
[datoteka.storages :as ds]
|
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
|
||||||
[uxbox.images :as images]
|
|
||||||
[uxbox.tasks :as tasks]
|
[uxbox.tasks :as tasks]
|
||||||
[uxbox.services.queries.colors :refer [decode-row]]
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.mutations :as sm]
|
[uxbox.services.mutations :as sm]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]
|
||||||
[uxbox.util.blob :as blob]
|
[uxbox.util.uuid :as uuid]))
|
||||||
[uxbox.util.uuid :as uuid]
|
|
||||||
[vertx.util :as vu]))
|
|
||||||
|
|
||||||
;; --- Helpers & Specs
|
;; --- Helpers & Specs
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id ::us/uuid)
|
(s/def ::team-id ::us/uuid)
|
||||||
|
(s/def ::library-id ::us/uuid)
|
||||||
(s/def ::content ::us/string)
|
(s/def ::content ::us/string)
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Create Collection
|
;; --- Mutation: Create Library
|
||||||
|
|
||||||
(declare create-color-collection)
|
(declare create-library)
|
||||||
|
|
||||||
(s/def ::create-color-collection
|
(s/def ::create-color-library
|
||||||
(s/keys :req-un [::profile-id ::name]
|
(s/keys :req-un [::profile-id ::team-id ::name]
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::create-color-collection
|
(sm/defmutation ::create-color-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[{:keys [profile-id team-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(create-color-collection conn params)))
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(create-library conn params)))
|
||||||
|
|
||||||
(def ^:private sql:create-color-collection
|
(def ^:private sql:create-library
|
||||||
"insert into color_collection (id, profile_id, name)
|
"insert into color_library (id, team_id, name)
|
||||||
values ($1, $2, $3)
|
values ($1, $2, $3)
|
||||||
returning *;")
|
returning *;")
|
||||||
|
|
||||||
(defn- create-color-collection
|
(defn- create-library
|
||||||
[conn {:keys [id profile-id name] :as params}]
|
[conn {:keys [id team-id name]}]
|
||||||
(let [id (or id (uuid/next))]
|
(let [id (or id (uuid/next))]
|
||||||
(db/query-one conn [sql:create-color-collection id profile-id name])))
|
(db/query-one conn [sql:create-library id team-id name])))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Mutation: Rename Library
|
||||||
|
|
||||||
;; --- Collection Permissions Check
|
(declare select-library-for-update)
|
||||||
|
(declare rename-library)
|
||||||
|
|
||||||
(def ^:private sql:select-collection
|
(s/def ::rename-color-library
|
||||||
"select id, profile_id
|
|
||||||
from color_collection
|
|
||||||
where id=$1 and deleted_at is null
|
|
||||||
for update")
|
|
||||||
|
|
||||||
(defn- check-collection-edition-permissions!
|
|
||||||
[conn profile-id coll-id]
|
|
||||||
(p/let [coll (-> (db/query-one conn [sql:select-collection coll-id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))]
|
|
||||||
(when (not= (:profile-id coll) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-authorized))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Collection
|
|
||||||
|
|
||||||
(def ^:private sql:rename-collection
|
|
||||||
"update color_collection
|
|
||||||
set name = $2
|
|
||||||
where id = $1
|
|
||||||
returning *")
|
|
||||||
|
|
||||||
(s/def ::rename-color-collection
|
|
||||||
(s/keys :req-un [::profile-id ::name ::id]))
|
(s/keys :req-un [::profile-id ::name ::id]))
|
||||||
|
|
||||||
(sm/defmutation ::rename-color-collection
|
(sm/defmutation ::rename-color-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[{:keys [id profile-id name] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
(p/let [lib (select-library-for-update conn id)]
|
||||||
(db/query-one conn [sql:rename-collection id name])))
|
(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/query-one conn [sql:select-library-for-update id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
(defn- rename-library
|
||||||
|
[conn id name]
|
||||||
|
(-> (db/query-one conn [sql:rename-library id name])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Copy Color
|
;; --- Copy Color
|
||||||
|
@ -112,34 +108,48 @@
|
||||||
;; (p/then' su/raise-not-found-if-nil))))
|
;; (p/then' su/raise-not-found-if-nil))))
|
||||||
|
|
||||||
;; (s/def ::copy-color
|
;; (s/def ::copy-color
|
||||||
;; (s/keys :req-un [:us/id ::collection-id ::profile-id]))
|
;; (s/keys :req-un [:us/id ::library-id ::profile-id]))
|
||||||
|
|
||||||
;; (sm/defmutation ::copy-color
|
;; (sm/defmutation ::copy-color
|
||||||
;; [{:keys [profile-id id collection-id] :as params}]
|
;; [{:keys [profile-id id library-id] :as params}]
|
||||||
;; (db/with-atomic [conn db/pool]
|
;; (db/with-atomic [conn db/pool]
|
||||||
;; (-> (retrieve-color conn {:profile-id profile-id :id id})
|
;; (-> (retrieve-color conn {:profile-id profile-id :id id})
|
||||||
;; (p/then (fn [color]
|
;; (p/then (fn [color]
|
||||||
;; (let [color (-> (dissoc color :id)
|
;; (let [color (-> (dissoc color :id)
|
||||||
;; (assoc :collection-id collection-id))]
|
;; (assoc :library-id library-id))]
|
||||||
;; (create-color conn color)))))))
|
;; (create-color conn color)))))))
|
||||||
|
|
||||||
;; --- Delete Collection
|
|
||||||
|
|
||||||
(def ^:private sql:mark-collection-deleted
|
|
||||||
"update color_collection
|
|
||||||
set deleted_at = clock_timestamp()
|
|
||||||
where id = $1
|
|
||||||
returning id")
|
|
||||||
|
|
||||||
(s/def ::delete-color-collection
|
;; --- Delete Library
|
||||||
|
|
||||||
|
(declare delete-library)
|
||||||
|
|
||||||
|
(s/def ::delete-color-library
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
(sm/defmutation ::delete-color-collection
|
(sm/defmutation ::delete-color-library
|
||||||
[{:keys [profile-id id] :as params}]
|
[{:keys [id profile-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
(p/let [lib (select-library-for-update conn id)]
|
||||||
(-> (db/query-one conn [sql:mark-collection-deleted id])
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
(p/then' su/constantly-nil))))
|
|
||||||
|
;; Schedule object deletion
|
||||||
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
|
:delay cfg/default-deletion-delay
|
||||||
|
:props {:id id :type :color-library}})
|
||||||
|
|
||||||
|
(delete-library conn id))))
|
||||||
|
|
||||||
|
(def ^:private sql:mark-library-deleted
|
||||||
|
"update color_library
|
||||||
|
set deleted_at = clock_timestamp()
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- delete-library
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-library-deleted id])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,72 +158,93 @@
|
||||||
(declare create-color)
|
(declare create-color)
|
||||||
|
|
||||||
(s/def ::create-color
|
(s/def ::create-color
|
||||||
(s/keys :req-un [::profile-id ::name ::content ::collection-id]
|
(s/keys :req-un [::profile-id ::name ::content ::library-id]
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::create-color
|
(sm/defmutation ::create-color
|
||||||
[{:keys [profile-id collection-id] :as params}]
|
[{:keys [profile-id library-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id collection-id)
|
(p/let [lib (select-library-for-update conn library-id)]
|
||||||
(create-color conn params)))
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(create-color conn params))))
|
||||||
|
|
||||||
(def ^:private sql:create-color
|
(def ^:private sql:create-color
|
||||||
"insert into color (id, profile_id, name, collection_id, content)
|
"insert into color (id, name, library_id, content)
|
||||||
values ($1, $2, $3, $4, $5) returning *")
|
values ($1, $2, $3, $4) returning *")
|
||||||
|
|
||||||
(defn create-color
|
(defn create-color
|
||||||
[conn {:keys [id profile-id name collection-id content]}]
|
[conn {:keys [id name library-id content]}]
|
||||||
(let [id (or id (uuid/next))]
|
(let [id (or id (uuid/next))]
|
||||||
(-> (db/query-one conn [sql:create-color id profile-id name collection-id content])
|
(db/query-one conn [sql:create-color id name library-id content])))
|
||||||
(p/then' decode-row))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Color
|
;; --- Mutation: Rename Color
|
||||||
|
|
||||||
(def ^:private sql:update-color
|
(declare select-color-for-update)
|
||||||
"update color
|
(declare rename-color)
|
||||||
set name = $3,
|
|
||||||
collection_id = $4
|
|
||||||
where id = $1
|
|
||||||
and profile_id = $2
|
|
||||||
returning *")
|
|
||||||
|
|
||||||
(s/def ::update-color
|
(s/def ::rename-color
|
||||||
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
|
(s/keys :req-un [::id ::profile-id ::name]))
|
||||||
|
|
||||||
(sm/defmutation ::update-color
|
(sm/defmutation ::rename-color
|
||||||
[{:keys [id name profile-id collection-id] :as params}]
|
[{:keys [id profile-id name] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id collection-id)
|
(p/let [clr (select-color-for-update conn id)]
|
||||||
(-> (db/query-one db/pool [sql:update-color id profile-id name collection-id])
|
(teams/check-edition-permissions! conn profile-id (:team-id clr))
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
(rename-color conn id name))))
|
||||||
|
|
||||||
|
(def ^:private sql:select-color-for-update
|
||||||
|
"select c.*,
|
||||||
|
lib.team_id as team_id
|
||||||
|
from color as c
|
||||||
|
inner join color_library as lib on (lib.id = c.library_id)
|
||||||
|
where c.id = $1
|
||||||
|
for update of c")
|
||||||
|
|
||||||
|
(def ^:private sql:rename-color
|
||||||
|
"update color
|
||||||
|
set name = $2
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- select-color-for-update
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:select-color-for-update id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
(defn- rename-color
|
||||||
|
[conn id name]
|
||||||
|
(-> (db/query-one conn [sql:rename-color id name])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Delete Color
|
;; --- Delete Color
|
||||||
|
|
||||||
|
(declare delete-color)
|
||||||
|
|
||||||
|
(s/def ::delete-color
|
||||||
|
(s/keys :req-un [::id ::profile-id]))
|
||||||
|
|
||||||
|
(sm/defmutation ::delete-color
|
||||||
|
[{:keys [profile-id id] :as params}]
|
||||||
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/let [clr (select-color-for-update conn id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id clr))
|
||||||
|
|
||||||
|
;; Schedule object deletion
|
||||||
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
|
:delay cfg/default-deletion-delay
|
||||||
|
:props {:id id :type :color}})
|
||||||
|
|
||||||
|
(delete-color conn id))))
|
||||||
|
|
||||||
(def ^:private sql:mark-color-deleted
|
(def ^:private sql:mark-color-deleted
|
||||||
"update color
|
"update color
|
||||||
set deleted_at = clock_timestamp()
|
set deleted_at = clock_timestamp()
|
||||||
where id = $1
|
where id = $1")
|
||||||
and profile_id = $2
|
|
||||||
returning id")
|
|
||||||
|
|
||||||
(s/def ::delete-color
|
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
|
||||||
|
|
||||||
(sm/defmutation ::delete-color
|
|
||||||
[{:keys [id profile-id] :as params}]
|
|
||||||
(db/with-atomic [conn db/pool]
|
|
||||||
(-> (db/query-one conn [sql:mark-color-deleted id profile-id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))
|
|
||||||
|
|
||||||
;; Schedule object deletion
|
|
||||||
(tasks/schedule! conn {:name "delete-object"
|
|
||||||
:delay cfg/default-deletion-delay
|
|
||||||
:props {:id id :type :color}})
|
|
||||||
|
|
||||||
nil))
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn- delete-color
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-color-deleted id])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
|
@ -5,39 +5,32 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.services.mutations.icons
|
(ns uxbox.services.mutations.icons
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[datoteka.core :as fs]
|
|
||||||
[datoteka.storages :as ds]
|
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
|
||||||
[uxbox.images :as images]
|
|
||||||
[uxbox.tasks :as tasks]
|
|
||||||
[uxbox.services.queries.icons :refer [decode-row]]
|
|
||||||
[uxbox.services.mutations :as sm]
|
[uxbox.services.mutations :as sm]
|
||||||
|
[uxbox.services.queries.icons :refer [decode-row]]
|
||||||
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]
|
||||||
|
[uxbox.tasks :as tasks]
|
||||||
[uxbox.util.blob :as blob]
|
[uxbox.util.blob :as blob]
|
||||||
[uxbox.util.data :as data]
|
[uxbox.util.uuid :as uuid]))
|
||||||
[uxbox.util.uuid :as uuid]
|
|
||||||
[uxbox.util.storage :as ust]
|
|
||||||
[vertx.util :as vu]))
|
|
||||||
|
|
||||||
;; --- Helpers & Specs
|
;; --- Helpers & Specs
|
||||||
|
|
||||||
|
(s/def ::height ::us/integer)
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
|
(s/def ::library-id ::us/uuid)
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id ::us/uuid)
|
(s/def ::team-id ::us/uuid)
|
||||||
(s/def ::width ::us/integer)
|
(s/def ::width ::us/integer)
|
||||||
(s/def ::height ::us/integer)
|
|
||||||
|
|
||||||
(s/def ::view-box
|
(s/def ::view-box
|
||||||
(s/and (s/coll-of number?)
|
(s/and (s/coll-of number?)
|
||||||
|
@ -52,65 +45,67 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Create Collection
|
;; --- Mutation: Create Library
|
||||||
|
|
||||||
(declare create-icon-collection)
|
(declare create-library)
|
||||||
|
|
||||||
(s/def ::create-icon-collection
|
(s/def ::create-icon-library
|
||||||
(s/keys :req-un [::profile-id ::name]
|
(s/keys :req-un [::profile-id ::team-id ::name]
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::create-icon-collection
|
(sm/defmutation ::create-icon-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[{:keys [profile-id team-id id name] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(create-icon-collection conn params)))
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(create-library conn params)))
|
||||||
|
|
||||||
(def ^:private sql:create-icon-collection
|
(def ^:private sql:create-library
|
||||||
"insert into icon_collection (id, profile_id, name)
|
"insert into icon_library (id, team_id, name)
|
||||||
values ($1, $2, $3)
|
values ($1, $2, $3)
|
||||||
returning *;")
|
returning *;")
|
||||||
|
|
||||||
(defn- create-icon-collection
|
(defn- create-library
|
||||||
[conn {:keys [id profile-id name] :as params}]
|
[conn {:keys [team-id id name] :as params}]
|
||||||
(let [id (or id (uuid/next))]
|
(let [id (or id (uuid/next))]
|
||||||
(db/query-one conn [sql:create-icon-collection id profile-id name])))
|
(db/query-one conn [sql:create-library id team-id name])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Collection Permissions Check
|
;; --- Mutation: Rename Library
|
||||||
|
|
||||||
(def ^:private sql:select-collection
|
(declare select-library-for-update)
|
||||||
"select id, profile_id
|
(declare rename-library)
|
||||||
from icon_collection
|
|
||||||
where id=$1 and deleted_at is null
|
|
||||||
for update")
|
|
||||||
|
|
||||||
(defn- check-collection-edition-permissions!
|
(s/def ::rename-icon-library
|
||||||
[conn profile-id coll-id]
|
|
||||||
(p/let [coll (-> (db/query-one conn [sql:select-collection coll-id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))]
|
|
||||||
(when (not= (:profile-id coll) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-authorized))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Collection
|
|
||||||
|
|
||||||
(def ^:private sql:rename-collection
|
|
||||||
"update icon_collection
|
|
||||||
set name = $2
|
|
||||||
where id = $1
|
|
||||||
returning *")
|
|
||||||
|
|
||||||
(s/def ::rename-icon-collection
|
|
||||||
(s/keys :req-un [::profile-id ::name ::id]))
|
(s/keys :req-un [::profile-id ::name ::id]))
|
||||||
|
|
||||||
(sm/defmutation ::rename-icon-collection
|
(sm/defmutation ::rename-icon-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[{:keys [id profile-id name] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
(p/let [lib (select-library-for-update conn id)]
|
||||||
(db/query-one conn [sql:rename-collection id name])))
|
(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 icon_library as l
|
||||||
|
where l.id = $1
|
||||||
|
for update")
|
||||||
|
|
||||||
|
(def ^:private sql:rename-library
|
||||||
|
"update icon_library
|
||||||
|
set name = $2
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- select-library-for-update
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:select-library-for-update id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
(defn- rename-library
|
||||||
|
[conn id name]
|
||||||
|
(-> (db/query-one conn [sql:rename-library id name])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,34 +124,48 @@
|
||||||
;; (p/then' su/raise-not-found-if-nil))))
|
;; (p/then' su/raise-not-found-if-nil))))
|
||||||
|
|
||||||
;; (s/def ::copy-icon
|
;; (s/def ::copy-icon
|
||||||
;; (s/keys :req-un [:us/id ::collection-id ::profile-id]))
|
;; (s/keys :req-un [:us/id ::library-id ::profile-id]))
|
||||||
|
|
||||||
;; (sm/defmutation ::copy-icon
|
;; (sm/defmutation ::copy-icon
|
||||||
;; [{:keys [profile-id id collection-id] :as params}]
|
;; [{:keys [profile-id id library-id] :as params}]
|
||||||
;; (db/with-atomic [conn db/pool]
|
;; (db/with-atomic [conn db/pool]
|
||||||
;; (-> (retrieve-icon conn {:profile-id profile-id :id id})
|
;; (-> (retrieve-icon conn {:profile-id profile-id :id id})
|
||||||
;; (p/then (fn [icon]
|
;; (p/then (fn [icon]
|
||||||
;; (let [icon (-> (dissoc icon :id)
|
;; (let [icon (-> (dissoc icon :id)
|
||||||
;; (assoc :collection-id collection-id))]
|
;; (assoc :library-id library-id))]
|
||||||
;; (create-icon conn icon)))))))
|
;; (create-icon conn icon)))))))
|
||||||
|
|
||||||
;; --- Delete Collection
|
|
||||||
|
|
||||||
(def ^:private sql:mark-collection-deleted
|
;; --- Mutation: Delete Library
|
||||||
"update icon_collection
|
|
||||||
|
(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]
|
||||||
|
(p/let [lib (select-library-for-update conn id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
|
||||||
|
;; Schedule object deletion
|
||||||
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
|
:delay cfg/default-deletion-delay
|
||||||
|
:props {:id id :type :icon-library}})
|
||||||
|
|
||||||
|
(delete-library conn id))))
|
||||||
|
|
||||||
|
(def ^:private sql:mark-library-deleted
|
||||||
|
"update icon_library
|
||||||
set deleted_at = clock_timestamp()
|
set deleted_at = clock_timestamp()
|
||||||
where id = $1
|
where id = $1
|
||||||
returning id")
|
returning id")
|
||||||
|
|
||||||
(s/def ::delete-icon-collection
|
(defn- delete-library
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-library-deleted id])
|
||||||
(sm/defmutation ::delete-icon-collection
|
(p/then' su/constantly-nil)))
|
||||||
[{:keys [profile-id id] :as params}]
|
|
||||||
(db/with-atomic [conn db/pool]
|
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
|
||||||
(-> (db/query-one conn [sql:mark-collection-deleted id])
|
|
||||||
(p/then' su/constantly-nil))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,58 +174,72 @@
|
||||||
(declare create-icon)
|
(declare create-icon)
|
||||||
|
|
||||||
(s/def ::create-icon
|
(s/def ::create-icon
|
||||||
(s/keys :req-un [::profile-id ::name ::metadata ::content ::collection-id]
|
(s/keys :req-un [::profile-id ::name ::metadata ::content ::library-id]
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::create-icon
|
(sm/defmutation ::create-icon
|
||||||
[{:keys [profile-id collection-id] :as params}]
|
[{:keys [profile-id library-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id collection-id)
|
(p/let [lib (select-library-for-update conn library-id)]
|
||||||
(create-icon conn params)))
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(create-icon conn params))))
|
||||||
|
|
||||||
(def ^:private sql:create-icon
|
(def ^:private sql:create-icon
|
||||||
"insert into icon (id, profile_id, name, collection_id, content, metadata)
|
"insert into icon (id, name, library_id, content, metadata)
|
||||||
values ($1, $2, $3, $4, $5, $6) returning *")
|
values ($1, $2, $3, $4, $5) returning *")
|
||||||
|
|
||||||
(defn create-icon
|
(defn create-icon
|
||||||
[conn {:keys [id profile-id name collection-id metadata content]}]
|
[conn {:keys [id name library-id metadata content]}]
|
||||||
(let [id (or id (uuid/next))]
|
(let [id (or id (uuid/next))]
|
||||||
(-> (db/query-one conn [sql:create-icon id profile-id name
|
(-> (db/query-one conn [sql:create-icon id name library-id
|
||||||
collection-id content (blob/encode metadata)])
|
content (blob/encode metadata)])
|
||||||
(p/then' decode-row))))
|
(p/then' decode-row))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Icon
|
;; --- Mutation: Rename Icon
|
||||||
|
|
||||||
(def ^:private sql:update-icon
|
(declare select-icon-for-update)
|
||||||
"update icon
|
(declare rename-icon)
|
||||||
set name = $3,
|
|
||||||
collection_id = $4
|
|
||||||
where id = $1
|
|
||||||
and profile_id = $2
|
|
||||||
returning *")
|
|
||||||
|
|
||||||
(s/def ::update-icon
|
(s/def ::rename-icon
|
||||||
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
|
(s/keys :req-un [::id ::profile-id ::name]))
|
||||||
|
|
||||||
(sm/defmutation ::update-icon
|
(sm/defmutation ::rename-icon
|
||||||
[{:keys [id name profile-id collection-id] :as params}]
|
[{:keys [id profile-id name] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id collection-id)
|
(p/let [clr (select-icon-for-update conn id)]
|
||||||
(-> (db/query-one db/pool [sql:update-icon id profile-id name collection-id])
|
(teams/check-edition-permissions! conn profile-id (:team-id clr))
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
(rename-icon conn id name))))
|
||||||
|
|
||||||
|
(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 = $1
|
||||||
|
for update of i")
|
||||||
|
|
||||||
|
(def ^:private sql:rename-icon
|
||||||
|
"update icon
|
||||||
|
set name = $2
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- select-icon-for-update
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:select-icon-for-update id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
(defn- rename-icon
|
||||||
|
[conn id name]
|
||||||
|
(-> (db/query-one conn [sql:rename-icon id name])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Delete Icon
|
;; --- Mutation: Delete Icon
|
||||||
|
|
||||||
(def ^:private sql:mark-icon-deleted
|
(declare delete-icon)
|
||||||
"update icon
|
|
||||||
set deleted_at = clock_timestamp()
|
|
||||||
where id = $1
|
|
||||||
and profile_id = $2
|
|
||||||
returning id")
|
|
||||||
|
|
||||||
(s/def ::delete-icon
|
(s/def ::delete-icon
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
@ -224,14 +247,22 @@
|
||||||
(sm/defmutation ::delete-icon
|
(sm/defmutation ::delete-icon
|
||||||
[{:keys [id profile-id] :as params}]
|
[{:keys [id profile-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(-> (db/query-one conn [sql:mark-icon-deleted id profile-id])
|
(p/let [icn (select-icon-for-update conn id)]
|
||||||
(p/then' su/raise-not-found-if-nil))
|
(teams/check-edition-permissions! conn profile-id (:team-id icn))
|
||||||
|
|
||||||
;; Schedule object deletion
|
;; Schedule object deletion
|
||||||
(tasks/schedule! conn {:name "delete-object"
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
:delay cfg/default-deletion-delay
|
:delay cfg/default-deletion-delay
|
||||||
:props {:id id :type :icon}})
|
:props {:id id :type :icon}})
|
||||||
|
|
||||||
nil))
|
(delete-icon conn id))))
|
||||||
|
|
||||||
|
(def ^:private sql:mark-icon-deleted
|
||||||
|
"update icon
|
||||||
|
set deleted_at = clock_timestamp()
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- delete-icon
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-icon-deleted id])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
|
@ -5,15 +5,13 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.services.mutations.images
|
(ns uxbox.services.mutations.images
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[datoteka.core :as fs]
|
[datoteka.core :as fs]
|
||||||
[datoteka.storages :as ds]
|
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
|
@ -21,10 +19,9 @@
|
||||||
[uxbox.media :as media]
|
[uxbox.media :as media]
|
||||||
[uxbox.images :as images]
|
[uxbox.images :as images]
|
||||||
[uxbox.tasks :as tasks]
|
[uxbox.tasks :as tasks]
|
||||||
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.mutations :as sm]
|
[uxbox.services.mutations :as sm]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]
|
||||||
[uxbox.util.blob :as blob]
|
|
||||||
[uxbox.util.data :as data]
|
|
||||||
[uxbox.util.uuid :as uuid]
|
[uxbox.util.uuid :as uuid]
|
||||||
[uxbox.util.storage :as ust]
|
[uxbox.util.storage :as ust]
|
||||||
[vertx.util :as vu]))
|
[vertx.util :as vu]))
|
||||||
|
@ -32,102 +29,108 @@
|
||||||
(def thumbnail-options
|
(def thumbnail-options
|
||||||
{:width 800
|
{:width 800
|
||||||
:height 800
|
:height 800
|
||||||
:quality 80
|
:quality 85
|
||||||
:format "webp"})
|
:format "webp"})
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id ::us/uuid)
|
(s/def ::library-id ::us/uuid)
|
||||||
|
(s/def ::team-id ::us/uuid)
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Create Library
|
||||||
|
|
||||||
;; --- Create Collection
|
(declare create-library)
|
||||||
|
|
||||||
(declare create-image-collection)
|
(s/def ::create-image-library
|
||||||
|
(s/keys :req-un [::profile-id ::team-id ::name]
|
||||||
(s/def ::create-image-collection
|
|
||||||
(s/keys :req-un [::profile-id ::name]
|
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::create-image-collection
|
(sm/defmutation ::create-image-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[{:keys [profile-id team-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(create-image-collection conn params)))
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(create-library conn params)))
|
||||||
|
|
||||||
(def ^:private sql:create-image-collection
|
(def ^:private sql:create-library
|
||||||
"insert into image_collection (id, profile_id, name)
|
"insert into image_library (id, team_id, name)
|
||||||
values ($1, $2, $3)
|
values ($1, $2, $3)
|
||||||
returning *;")
|
returning *;")
|
||||||
|
|
||||||
(defn- create-image-collection
|
(defn- create-library
|
||||||
[conn {:keys [id profile-id name] :as params}]
|
[conn {:keys [id team-id name]}]
|
||||||
(let [id (or id (uuid/next))]
|
(let [id (or id (uuid/next))]
|
||||||
(db/query-one conn [sql:create-image-collection id profile-id name])))
|
(db/query-one conn [sql:create-library id team-id name])))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Rename Library
|
||||||
|
|
||||||
;; --- Collection Permissions Check
|
(declare select-library-for-update)
|
||||||
|
(declare rename-library)
|
||||||
|
|
||||||
(def ^:private sql:select-collection
|
(s/def ::rename-image-library
|
||||||
"select id, profile_id
|
(s/keys :req-un [::id ::profile-id ::name]))
|
||||||
from image_collection
|
|
||||||
where id=$1 and deleted_at is null
|
(sm/defmutation ::rename-image-library
|
||||||
|
[{:keys [profile-id id name] :as params}]
|
||||||
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/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 image_library as l
|
||||||
|
where l.id = $1
|
||||||
for update")
|
for update")
|
||||||
|
|
||||||
(defn- check-collection-edition-permissions!
|
(def ^:private sql:rename-library
|
||||||
[conn profile-id coll-id]
|
"update image_library
|
||||||
(p/let [coll (-> (db/query-one conn [sql:select-collection coll-id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))]
|
|
||||||
(when (not= (:profile-id coll) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-authorized))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Rename Collection
|
|
||||||
|
|
||||||
(def ^:private sql:rename-image-collection
|
|
||||||
"update image_collection
|
|
||||||
set name = $2
|
set name = $2
|
||||||
where id = $1
|
where id = $1")
|
||||||
returning *;")
|
|
||||||
|
|
||||||
(s/def ::rename-image-collection
|
(defn- select-library-for-update
|
||||||
(s/keys :req-un [::id ::profile-id ::us/name]))
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:select-library-for-update id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
(sm/defmutation ::rename-image-collection
|
(defn- rename-library
|
||||||
[{:keys [id profile-id name] :as params}]
|
[conn id name]
|
||||||
(db/with-atomic [conn db/pool]
|
(-> (db/query-one conn [sql:rename-library id name])
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
(p/then' su/constantly-nil)))
|
||||||
(db/query-one conn [sql:rename-image-collection id name])))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Delete Collection
|
;; --- Delete Library
|
||||||
|
|
||||||
(s/def ::delete-image-collection
|
(declare delete-library)
|
||||||
|
|
||||||
|
(s/def ::delete-image-library
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
(def ^:private sql:mark-image-collection-as-deleted
|
(sm/defmutation ::delete-image-library
|
||||||
"update image_collection
|
|
||||||
set deleted_at = clock_timestamp()
|
|
||||||
where id = $1
|
|
||||||
returning id")
|
|
||||||
|
|
||||||
(sm/defmutation ::delete-image-collection
|
|
||||||
[{:keys [id profile-id] :as params}]
|
[{:keys [id profile-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id id)
|
(p/let [lib (select-library-for-update conn id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
|
||||||
;; Schedule object deletion
|
;; Schedule object deletion
|
||||||
(tasks/schedule! conn {:name "delete-object"
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
:delay cfg/default-deletion-delay
|
:delay cfg/default-deletion-delay
|
||||||
:props {:id id :type :image-collection}})
|
:props {:id id :type :image-library}})
|
||||||
|
|
||||||
(-> (db/query-one conn [sql:mark-image-collection-as-deleted id])
|
(delete-library conn id))))
|
||||||
(p/then' su/raise-not-found-if-nil)
|
|
||||||
(p/then' su/constantly-nil))))
|
(def ^:private sql:mark-library-deleted
|
||||||
|
"update image_library
|
||||||
|
set deleted_at = clock_timestamp()
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- delete-library
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-library-deleted id])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,24 +157,25 @@
|
||||||
(s/def ::content ::upload)
|
(s/def ::content ::upload)
|
||||||
|
|
||||||
(s/def ::upload-image
|
(s/def ::upload-image
|
||||||
(s/keys :req-un [::profile-id ::name ::content ::collection-id]
|
(s/keys :req-un [::profile-id ::name ::content ::library-id]
|
||||||
:opt-un [::id]))
|
:opt-un [::id]))
|
||||||
|
|
||||||
(sm/defmutation ::upload-image
|
(sm/defmutation ::upload-image
|
||||||
[{:keys [collection-id profile-id] :as params}]
|
[{:keys [library-id profile-id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(check-collection-edition-permissions! conn profile-id collection-id)
|
(p/let [lib (select-library-for-update conn library-id)]
|
||||||
(create-image conn params)))
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(create-image conn params))))
|
||||||
|
|
||||||
(def ^:private sql:insert-image
|
(def ^:private sql:insert-image
|
||||||
"insert into image
|
"insert into image
|
||||||
(id, collection_id, profile_id, name, path, width, height, mtype,
|
(id, library_id, name, path, width, height, mtype,
|
||||||
thumb_path, thumb_width, thumb_height, thumb_quality, thumb_mtype)
|
thumb_path, thumb_width, thumb_height, thumb_quality, thumb_mtype)
|
||||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||||
returning *")
|
returning *")
|
||||||
|
|
||||||
(defn create-image
|
(defn create-image
|
||||||
[conn {:keys [id content collection-id profile-id name] :as params}]
|
[conn {:keys [id content library-id name]}]
|
||||||
(when-not (valid-image-types? (:mtype content))
|
(when-not (valid-image-types? (:mtype content))
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :image-type-not-allowed
|
:code :image-type-not-allowed
|
||||||
|
@ -184,8 +188,7 @@
|
||||||
|
|
||||||
sqlv [sql:insert-image
|
sqlv [sql:insert-image
|
||||||
id
|
id
|
||||||
collection-id
|
library-id
|
||||||
profile-id
|
|
||||||
name
|
name
|
||||||
(str image-path)
|
(str image-path)
|
||||||
(:width image-opts)
|
(:width image-opts)
|
||||||
|
@ -202,7 +205,7 @@
|
||||||
(p/then' #(images/resolve-urls % :thumb-path :thumb-uri)))))
|
(p/then' #(images/resolve-urls % :thumb-path :thumb-uri)))))
|
||||||
|
|
||||||
(defn persist-image-on-fs
|
(defn persist-image-on-fs
|
||||||
[{:keys [name path] :as upload}]
|
[{:keys [name path]}]
|
||||||
(vu/blocking
|
(vu/blocking
|
||||||
(let [filename (fs/name name)]
|
(let [filename (fs/name name)]
|
||||||
(ust/save! media/media-storage filename path))))
|
(ust/save! media/media-storage filename path))))
|
||||||
|
@ -212,48 +215,68 @@
|
||||||
(vu/blocking
|
(vu/blocking
|
||||||
(let [input-path (ust/lookup media/media-storage input-path)
|
(let [input-path (ust/lookup media/media-storage input-path)
|
||||||
thumb-data (images/generate-thumbnail input-path thumb-opts)
|
thumb-data (images/generate-thumbnail input-path thumb-opts)
|
||||||
[filename ext] (fs/split-ext (fs/name input-path))
|
[filename _] (fs/split-ext (fs/name input-path))
|
||||||
thumb-name (->> (images/format->extension (:format thumb-opts))
|
thumb-name (->> (images/format->extension (:format thumb-opts))
|
||||||
(str "thumbnail-" filename))]
|
(str "thumbnail-" filename))]
|
||||||
(ust/save! media/media-storage thumb-name thumb-data))))
|
(ust/save! media/media-storage thumb-name thumb-data))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Update Image
|
;; --- Mutation: Rename Image
|
||||||
|
|
||||||
(s/def ::update-image
|
(declare select-image-for-update)
|
||||||
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
|
(declare rename-image)
|
||||||
|
|
||||||
(def ^:private sql:update-image
|
(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]
|
||||||
|
(p/let [img (select-image-for-update conn id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id img))
|
||||||
|
(rename-image conn id name))))
|
||||||
|
|
||||||
|
(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 = $1
|
||||||
|
for update of img")
|
||||||
|
|
||||||
|
(def ^:private sql:rename-image
|
||||||
"update image
|
"update image
|
||||||
set name = $3,
|
set name = $2
|
||||||
collection_id = $2
|
where id = $1")
|
||||||
where id = $1
|
|
||||||
and profile_id = $4
|
|
||||||
returning *;")
|
|
||||||
|
|
||||||
(sm/defmutation ::update-image
|
(defn- select-image-for-update
|
||||||
[{:keys [id name profile-id collection-id] :as params}]
|
[conn id]
|
||||||
(-> (db/query-one db/pool [sql:update-image id
|
(-> (db/query-one conn [sql:select-image-for-update id])
|
||||||
collection-id name profile-id])
|
|
||||||
(p/then' su/raise-not-found-if-nil)))
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
(defn- rename-image
|
||||||
|
[conn id name]
|
||||||
|
(-> (db/query-one conn [sql:rename-image id name])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Copy Image
|
;; --- Copy Image
|
||||||
|
|
||||||
;; (declare retrieve-image)
|
;; (declare retrieve-image)
|
||||||
|
|
||||||
;; (s/def ::copy-image
|
;; (s/def ::copy-image
|
||||||
;; (s/keys :req-un [::id ::collection-id ::profile-id]))
|
;; (s/keys :req-un [::id ::library-id ::profile-id]))
|
||||||
|
|
||||||
;; (sm/defmutation ::copy-image
|
;; (sm/defmutation ::copy-image
|
||||||
;; [{:keys [profile-id id collection-id] :as params}]
|
;; [{:keys [profile-id id library-id] :as params}]
|
||||||
;; (letfn [(copy-image [conn {:keys [path] :as image}]
|
;; (letfn [(copy-image [conn {:keys [path] :as image}]
|
||||||
;; (-> (ds/lookup media/images-storage (:path image))
|
;; (-> (ds/lookup media/images-storage (:path image))
|
||||||
;; (p/then (fn [path] (ds/save media/images-storage (fs/name path) path)))
|
;; (p/then (fn [path] (ds/save media/images-storage (fs/name path) path)))
|
||||||
;; (p/then (fn [path]
|
;; (p/then (fn [path]
|
||||||
;; (-> image
|
;; (-> image
|
||||||
;; (assoc :path (str path) :collection-id collection-id)
|
;; (assoc :path (str path) :library-id library-id)
|
||||||
;; (dissoc :id))))
|
;; (dissoc :id))))
|
||||||
;; (p/then (partial store-image-in-db conn))))]
|
;; (p/then (partial store-image-in-db conn))))]
|
||||||
|
|
||||||
|
@ -262,14 +285,10 @@
|
||||||
;; (p/then su/raise-not-found-if-nil)
|
;; (p/then su/raise-not-found-if-nil)
|
||||||
;; (p/then (partial copy-image conn))))))
|
;; (p/then (partial copy-image conn))))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Delete Image
|
;; --- Delete Image
|
||||||
|
|
||||||
(def ^:private sql:mark-image-deleted
|
(declare delete-image)
|
||||||
"update image
|
|
||||||
set deleted_at = clock_timestamp()
|
|
||||||
where id = $1
|
|
||||||
and profile_id = $2
|
|
||||||
returning id")
|
|
||||||
|
|
||||||
(s/def ::delete-image
|
(s/def ::delete-image
|
||||||
(s/keys :req-un [::id ::profile-id]))
|
(s/keys :req-un [::id ::profile-id]))
|
||||||
|
@ -277,14 +296,22 @@
|
||||||
(sm/defmutation ::delete-image
|
(sm/defmutation ::delete-image
|
||||||
[{:keys [profile-id id] :as params}]
|
[{:keys [profile-id id] :as params}]
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(-> (db/query-one conn [sql:mark-image-deleted id profile-id])
|
(p/let [img (select-image-for-update conn id)]
|
||||||
(p/then' su/raise-not-found-if-nil))
|
(teams/check-edition-permissions! conn profile-id (:team-id img))
|
||||||
|
|
||||||
;; Schedule object deletion
|
;; Schedule object deletion
|
||||||
(tasks/schedule! conn {:name "delete-object"
|
(tasks/schedule! conn {:name "delete-object"
|
||||||
:delay cfg/default-deletion-delay
|
:delay cfg/default-deletion-delay
|
||||||
:props {:id id :type :image}})
|
:props {:id id :type :image}})
|
||||||
|
|
||||||
nil))
|
(delete-image conn id))))
|
||||||
|
|
||||||
|
(def ^:private sql:mark-image-deleted
|
||||||
|
"update image
|
||||||
|
set deleted_at = clock_timestamp()
|
||||||
|
where id = $1")
|
||||||
|
|
||||||
|
(defn- delete-image
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:mark-image-deleted id])
|
||||||
|
(p/then' su/constantly-nil)))
|
||||||
|
|
|
@ -46,20 +46,43 @@
|
||||||
values ($1, $2, '', $3)
|
values ($1, $2, '', $3)
|
||||||
returning *")
|
returning *")
|
||||||
|
|
||||||
|
(def ^:private sql:create-team-profile
|
||||||
|
"insert into team_profile_rel (team_id, profile_id, is_owner, is_admin, can_edit)
|
||||||
|
values ($1, $2, true, true, true)
|
||||||
|
returning *")
|
||||||
|
|
||||||
(defn create-team
|
(defn create-team
|
||||||
[conn {:keys [id profile-id name default?] :as params}]
|
[conn {:keys [id profile-id name default?] :as params}]
|
||||||
(let [id (or id (uuid/next))
|
(let [id (or id (uuid/next))
|
||||||
default? (if (boolean? default?) default? false)]
|
default? (if (boolean? default?) default? false)]
|
||||||
(db/query-one conn [sql:insert-team id name default?])))
|
(db/query-one conn [sql:insert-team id name default?])))
|
||||||
|
|
||||||
(def ^:private sql:create-team-profile
|
|
||||||
"insert into team_profile_rel (team_id, profile_id, is_owner, is_admin, can_edit)
|
|
||||||
values ($1, $2, true, true, true)
|
|
||||||
returning *")
|
|
||||||
|
|
||||||
(defn create-team-profile
|
(defn create-team-profile
|
||||||
[conn {:keys [team-id profile-id] :as params}]
|
[conn {:keys [team-id profile-id] :as params}]
|
||||||
(-> (db/query-one conn [sql:create-team-profile team-id profile-id])
|
(-> (db/query-one conn [sql:create-team-profile team-id profile-id])
|
||||||
(p/then' su/constantly-nil)))
|
(p/then' su/constantly-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Mutation: Team Edition Permissions
|
||||||
|
|
||||||
|
(def ^:private sql:team-permissions
|
||||||
|
"select tpr.is_owner,
|
||||||
|
tpr.is_admin,
|
||||||
|
tpr.can_edit
|
||||||
|
from team_profile_rel as tpr
|
||||||
|
where tpr.profile_id = $1
|
||||||
|
and tpr.team_id = $2")
|
||||||
|
|
||||||
|
(defn check-edition-permissions!
|
||||||
|
[conn profile-id team-id]
|
||||||
|
(-> (db/query-one conn [sql:team-permissions profile-id team-id])
|
||||||
|
(p/then' (fn [row]
|
||||||
|
(when-not (or (:can-edit row)
|
||||||
|
(:is-admin row)
|
||||||
|
(:is-owner row))
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-authorized))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
[uxbox.media :as media]
|
||||||
[uxbox.images :as images]
|
[uxbox.images :as images]
|
||||||
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.queries :as sq]
|
[uxbox.services.queries :as sq]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]
|
||||||
[uxbox.util.blob :as blob]
|
[uxbox.util.blob :as blob]
|
||||||
|
@ -28,55 +29,85 @@
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
(s/def ::team-id ::us/uuid)
|
||||||
|
(s/def ::library-id (s/nilable ::us/uuid))
|
||||||
(defn decode-row
|
|
||||||
[{:keys [metadata] :as row}]
|
|
||||||
(when row
|
|
||||||
(cond-> row
|
|
||||||
metadata (assoc :metadata (blob/decode metadata)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Query: Collections
|
;; --- Query: Colors Librarys
|
||||||
|
|
||||||
(def ^:private sql:collections
|
(def ^:private sql:libraries
|
||||||
"select *,
|
"select lib.*,
|
||||||
(select count(*) from color where collection_id = ic.id) as num_colors
|
(select count(*) from color where library_id = lib.id) as num_colors
|
||||||
from color_collection as ic
|
from color_library as lib
|
||||||
where (ic.profile_id = $1 or
|
where lib.team_id = $1
|
||||||
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
and lib.deleted_at is null
|
||||||
and ic.deleted_at is null
|
order by lib.created_at desc")
|
||||||
order by ic.created_at desc")
|
|
||||||
|
|
||||||
(s/def ::color-collections
|
(s/def ::color-libraries
|
||||||
(s/keys :req-un [::profile-id]))
|
(s/keys :req-un [::profile-id ::team-id]))
|
||||||
|
|
||||||
(sq/defquery ::color-collections
|
(sq/defquery ::color-libraries
|
||||||
[{:keys [profile-id] :as params}]
|
[{:keys [profile-id team-id]}]
|
||||||
(let [sqlv [sql:collections profile-id]]
|
(db/with-atomic [conn db/pool]
|
||||||
(db/query db/pool sqlv)))
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(db/query conn [sql:libraries team-id])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Colors By Collection ID
|
;; --- Query: Color Library
|
||||||
|
|
||||||
(def ^:private sql:colors
|
(declare retrieve-library)
|
||||||
"select *
|
|
||||||
from color as i
|
(s/def ::color-library
|
||||||
where (i.profile_id = $1 or
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
i.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
|
||||||
and i.deleted_at is null
|
(sq/defquery ::color-library
|
||||||
and i.collection_id = $2
|
[{:keys [profile-id id]}]
|
||||||
order by i.created_at desc")
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/let [lib (retrieve-library conn id)]
|
||||||
|
(teams/check-edition-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 = $1")
|
||||||
|
|
||||||
|
(defn- retrieve-library
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:single-library id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Query: Colors (by library)
|
||||||
|
|
||||||
|
(declare retrieve-colors)
|
||||||
|
|
||||||
(s/def ::colors
|
(s/def ::colors
|
||||||
(s/keys :req-un [::profile-id ::collection-id]))
|
(s/keys :req-un [::profile-id ::library-id]))
|
||||||
|
|
||||||
(sq/defquery ::colors
|
(sq/defquery ::colors
|
||||||
[{:keys [profile-id collection-id] :as params}]
|
[{:keys [profile-id library-id] :as params}]
|
||||||
(-> (db/query db/pool [sql:colors profile-id collection-id])
|
(db/with-atomic [conn db/pool]
|
||||||
(p/then' #(mapv decode-row %))))
|
(p/let [lib (retrieve-library conn library-id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(retrieve-colors conn library-id))))
|
||||||
|
|
||||||
|
(def ^:private sql:colors
|
||||||
|
"select color.*
|
||||||
|
from color as color
|
||||||
|
inner join color_library as lib on (lib.id = color.library_id)
|
||||||
|
where color.deleted_at is null
|
||||||
|
and color.library_id = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
|
(defn- retrieve-colors
|
||||||
|
[conn library-id]
|
||||||
|
(db/query conn [sql:colors library-id]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,14 +120,22 @@
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
(sq/defquery ::color
|
(sq/defquery ::color
|
||||||
[{:keys [id] :as params}]
|
[{:keys [profile-id id] :as params}]
|
||||||
(-> (retrieve-color db/pool id)
|
(db/with-atomic [conn db/pool]
|
||||||
(p/then' su/raise-not-found-if-nil)))
|
(p/let [color (retrieve-color conn id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id color))
|
||||||
|
color)))
|
||||||
|
|
||||||
|
(def ^:private sql:single-color
|
||||||
|
"select color.*,
|
||||||
|
lib.team_id as team_id
|
||||||
|
from color as color
|
||||||
|
inner join color_library as lib on (lib.id = color.library_id)
|
||||||
|
where color.deleted_at is null
|
||||||
|
and color.id = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
(defn retrieve-color
|
(defn retrieve-color
|
||||||
[conn id]
|
[conn id]
|
||||||
(let [sql "select * from color
|
(-> (db/query-one conn [sql:single-color id])
|
||||||
where id = $1
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
and deleted_at is null;"]
|
|
||||||
(-> (db/query-one conn [sql id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
[uxbox.media :as media]
|
||||||
[uxbox.images :as images]
|
[uxbox.images :as images]
|
||||||
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.queries :as sq]
|
[uxbox.services.queries :as sq]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]
|
||||||
[uxbox.util.blob :as blob]
|
[uxbox.util.blob :as blob]
|
||||||
|
@ -27,8 +28,9 @@
|
||||||
;; --- Helpers & Specs
|
;; --- Helpers & Specs
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
|
(s/def ::name ::us/string)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
(s/def ::library-id ::us/uuid)
|
||||||
|
|
||||||
(defn decode-row
|
(defn decode-row
|
||||||
[{:keys [metadata] :as row}]
|
[{:keys [metadata] :as row}]
|
||||||
|
@ -38,45 +40,82 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Query: Collections
|
;; --- Query: Icons Librarys
|
||||||
|
|
||||||
(def ^:private sql:collections
|
(def ^:private sql:libraries
|
||||||
"select *,
|
"select lib.*,
|
||||||
(select count(*) from icon where collection_id = ic.id) as num_icons
|
(select count(*) from icon where library_id = lib.id) as num_icons
|
||||||
from icon_collection as ic
|
from icon_library as lib
|
||||||
where (ic.profile_id = $1 or
|
where lib.team_id = $1
|
||||||
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
and lib.deleted_at is null
|
||||||
and ic.deleted_at is null
|
order by lib.created_at desc")
|
||||||
order by ic.created_at desc")
|
|
||||||
|
|
||||||
(s/def ::icon-collections
|
(s/def ::icon-libraries
|
||||||
(s/keys :req-un [::profile-id]))
|
(s/keys :req-un [::profile-id ::team-id]))
|
||||||
|
|
||||||
(sq/defquery ::icon-collections
|
(sq/defquery ::icon-libraries
|
||||||
[{:keys [profile-id] :as params}]
|
[{:keys [profile-id team-id]}]
|
||||||
(let [sqlv [sql:collections profile-id]]
|
(db/with-atomic [conn db/pool]
|
||||||
(db/query db/pool sqlv)))
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(db/query conn [sql:libraries team-id])))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Icons By Collection ID
|
;; --- Query: Icon Library
|
||||||
|
|
||||||
(def ^:private sql:icons
|
(declare retrieve-library)
|
||||||
"select *
|
|
||||||
from icon as i
|
(s/def ::icon-library
|
||||||
where (i.profile_id = $1 or
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
i.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
|
||||||
and i.deleted_at is null
|
(sq/defquery ::icon-library
|
||||||
and i.collection_id = $2
|
[{:keys [profile-id id]}]
|
||||||
order by i.created_at desc")
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/let [lib (retrieve-library conn id)]
|
||||||
|
(teams/check-edition-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 = $1")
|
||||||
|
|
||||||
|
(defn- retrieve-library
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:single-library id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Query: Icons (by library)
|
||||||
|
|
||||||
|
(declare retrieve-icons)
|
||||||
|
|
||||||
(s/def ::icons
|
(s/def ::icons
|
||||||
(s/keys :req-un [::profile-id ::collection-id]))
|
(s/keys :req-un [::profile-id ::library-id]))
|
||||||
|
|
||||||
(sq/defquery ::icons
|
(sq/defquery ::icons
|
||||||
[{:keys [profile-id collection-id] :as params}]
|
[{:keys [profile-id library-id] :as params}]
|
||||||
(-> (db/query db/pool [sql:icons profile-id collection-id])
|
(db/with-atomic [conn db/pool]
|
||||||
(p/then' #(mapv decode-row %))))
|
(p/let [lib (retrieve-library conn library-id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(-> (retrieve-icons conn library-id)
|
||||||
|
(p/then' (fn [rows] (mapv decode-row rows)))))))
|
||||||
|
|
||||||
|
(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 = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
|
(defn- retrieve-icons
|
||||||
|
[conn library-id]
|
||||||
|
(db/query conn [sql:icons library-id]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Query: Icon (by ID)
|
;; --- Query: Icon (by ID)
|
||||||
|
@ -88,15 +127,23 @@
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
(sq/defquery ::icon
|
(sq/defquery ::icon
|
||||||
[{:keys [id] :as params}]
|
[{:keys [profile-id id] :as params}]
|
||||||
(-> (retrieve-icon db/pool id)
|
(db/with-atomic [conn db/pool]
|
||||||
(p/then' su/raise-not-found-if-nil)))
|
(p/let [icon (retrieve-icon conn id)]
|
||||||
|
(teams/check-edition-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 = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
(defn retrieve-icon
|
(defn retrieve-icon
|
||||||
[conn id]
|
[conn id]
|
||||||
(let [sql "select * from icon
|
(-> (db/query-one conn [sql:single-icon id])
|
||||||
where id = $1
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
and deleted_at is null;"]
|
|
||||||
(-> (db/query-one conn [sql id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
|
||||||
|
|
||||||
|
|
|
@ -5,47 +5,105 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.services.queries.images
|
(ns uxbox.services.queries.images
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
|
||||||
[uxbox.images :as images]
|
[uxbox.images :as images]
|
||||||
|
[uxbox.services.queries.teams :as teams]
|
||||||
[uxbox.services.queries :as sq]
|
[uxbox.services.queries :as sq]
|
||||||
[uxbox.services.util :as su]
|
[uxbox.services.util :as su]))
|
||||||
[uxbox.util.blob :as blob]
|
|
||||||
[uxbox.util.data :as data]
|
|
||||||
[uxbox.util.uuid :as uuid]
|
|
||||||
[vertx.core :as vc]))
|
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::collection-id (s/nilable ::us/uuid))
|
(s/def ::library-id ::us/uuid)
|
||||||
|
|
||||||
;; --- Query: Image Collections
|
;; --- Query: Image Librarys
|
||||||
|
|
||||||
(def ^:private sql:collections
|
(def ^:private sql:libraries
|
||||||
"select *,
|
"select lib.*,
|
||||||
(select count(*) from image where collection_id = ic.id) as num_images
|
(select count(*) from image where library_id = lib.id) as num_images
|
||||||
from image_collection as ic
|
from image_library as lib
|
||||||
where (ic.profile_id = $1 or
|
where lib.team_id = $1
|
||||||
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
and lib.deleted_at is null
|
||||||
and ic.deleted_at is null
|
order by lib.created_at desc")
|
||||||
order by ic.created_at desc;")
|
|
||||||
|
|
||||||
(s/def ::image-collections
|
(s/def ::image-libraries
|
||||||
(s/keys :req-un [::profile-id]))
|
(s/keys :req-un [::profile-id ::team-id]))
|
||||||
|
|
||||||
(sq/defquery ::image-collections
|
(sq/defquery ::image-libraries
|
||||||
[{:keys [profile-id] :as params}]
|
[{:keys [profile-id team-id]}]
|
||||||
(db/query db/pool [sql:collections profile-id]))
|
(db/with-atomic [conn db/pool]
|
||||||
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(db/query 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]
|
||||||
|
(p/let [lib (retrieve-library conn id)]
|
||||||
|
(teams/check-edition-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 = $1")
|
||||||
|
|
||||||
|
(defn- retrieve-library
|
||||||
|
[conn id]
|
||||||
|
(-> (db/query-one conn [sql:single-library id])
|
||||||
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Query: Images (by library)
|
||||||
|
|
||||||
|
(declare retrieve-images)
|
||||||
|
|
||||||
|
(s/def ::images
|
||||||
|
(s/keys :req-un [::profile-id ::library-id]))
|
||||||
|
|
||||||
|
;; TODO: check if we can resolve url with transducer for reduce
|
||||||
|
;; garbage generation for each request
|
||||||
|
|
||||||
|
(sq/defquery ::images
|
||||||
|
[{:keys [profile-id library-id] :as params}]
|
||||||
|
(db/with-atomic [conn db/pool]
|
||||||
|
(p/let [lib (retrieve-library conn library-id)]
|
||||||
|
(teams/check-edition-permissions! conn profile-id (:team-id lib))
|
||||||
|
(-> (retrieve-images conn library-id)
|
||||||
|
(p/then' (fn [rows]
|
||||||
|
(->> rows
|
||||||
|
(mapv #(images/resolve-urls % :path :uri))
|
||||||
|
(mapv #(images/resolve-urls % :thumb-path :thumb-uri)))))))))
|
||||||
|
|
||||||
|
|
||||||
|
(def ^:private sql:images
|
||||||
|
"select img.*
|
||||||
|
from image as img
|
||||||
|
inner join image_library as lib on (lib.id = img.library_id)
|
||||||
|
where img.deleted_at is null
|
||||||
|
and img.library_id = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
|
(defn- retrieve-images
|
||||||
|
[conn library-id]
|
||||||
|
(db/query conn [sql:images library-id]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,42 +116,27 @@
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
(sq/defquery ::image
|
(sq/defquery ::image
|
||||||
[{:keys [id] :as params}]
|
[{:keys [profile-id id] :as params}]
|
||||||
(-> (retrieve-image db/pool id)
|
(db/with-atomic [conn db/pool]
|
||||||
(p/then' #(images/resolve-urls % :path :uri))
|
(p/let [img (retrieve-image conn id)]
|
||||||
(p/then' #(images/resolve-urls % :thumb-path :thumb-uri))))
|
(teams/check-edition-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.*,
|
||||||
|
lib.team_id as team_id
|
||||||
|
from image as img
|
||||||
|
inner join image_library as lib on (lib.id = img.library_id)
|
||||||
|
where img.deleted_at is null
|
||||||
|
and img.id = $1
|
||||||
|
order by created_at desc")
|
||||||
|
|
||||||
(defn retrieve-image
|
(defn retrieve-image
|
||||||
[conn id]
|
[conn id]
|
||||||
(let [sql "select * from image
|
(-> (db/query-one conn [sql:single-image id])
|
||||||
where id = $1
|
(p/then' su/raise-not-found-if-nil)))
|
||||||
and deleted_at is null;"]
|
|
||||||
(-> (db/query-one conn [sql id])
|
|
||||||
(p/then' su/raise-not-found-if-nil))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Query: Images (by collection)
|
|
||||||
|
|
||||||
(def ^:private sql:images
|
|
||||||
"select *
|
|
||||||
from image
|
|
||||||
where (profile_id = $1 or
|
|
||||||
profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
|
|
||||||
and deleted_at is null
|
|
||||||
and collection_id = $2
|
|
||||||
order by created_at desc")
|
|
||||||
|
|
||||||
(s/def ::images
|
|
||||||
(s/keys :req-un [::profile-id ::collection-id]))
|
|
||||||
|
|
||||||
;; TODO: check if we can resolve url with transducer for reduce
|
|
||||||
;; garbage generation for each request
|
|
||||||
|
|
||||||
(sq/defquery ::images
|
|
||||||
[{:keys [profile-id collection-id] :as params}]
|
|
||||||
(-> (db/query db/pool [sql:images profile-id collection-id])
|
|
||||||
(p/then' (fn [rows]
|
|
||||||
(->> rows
|
|
||||||
(mapv #(images/resolve-urls % :path :uri))
|
|
||||||
(mapv #(images/resolve-urls % :thumb-path :thumb-uri)))))))
|
|
||||||
|
|
44
backend/src/uxbox/services/queries/teams.clj
Normal file
44
backend/src/uxbox/services/queries/teams.clj
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
;; 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 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(ns uxbox.services.queries.teams
|
||||||
|
(:require
|
||||||
|
[clojure.spec.alpha :as s]
|
||||||
|
[promesa.core :as p]
|
||||||
|
[uxbox.db :as db]
|
||||||
|
[uxbox.common.exceptions :as ex]
|
||||||
|
[uxbox.common.spec :as us]
|
||||||
|
[uxbox.services.queries :as sq]
|
||||||
|
[uxbox.services.util :as su]
|
||||||
|
[uxbox.util.blob :as blob]
|
||||||
|
[uxbox.util.uuid :as uuid]))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Team Edition Permissions
|
||||||
|
|
||||||
|
(def ^:private sql:team-permissions
|
||||||
|
"select tpr.is_owner,
|
||||||
|
tpr.is_admin,
|
||||||
|
tpr.can_edit
|
||||||
|
from team_profile_rel as tpr
|
||||||
|
where tpr.profile_id = $1
|
||||||
|
and tpr.team_id = $2")
|
||||||
|
|
||||||
|
(defn check-edition-permissions!
|
||||||
|
[conn profile-id team-id]
|
||||||
|
(-> (db/query-one conn [sql:team-permissions profile-id team-id])
|
||||||
|
(p/then' (fn [row]
|
||||||
|
(when-not (or (:can-edit row)
|
||||||
|
(:is-admin row)
|
||||||
|
(:is-owner row))
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-authorized))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -111,22 +111,22 @@
|
||||||
:shapes-by-id {}}}))
|
:shapes-by-id {}}}))
|
||||||
|
|
||||||
|
|
||||||
(defn create-image-collection
|
(defn create-image-library
|
||||||
[conn profile-id i]
|
[conn team-id i]
|
||||||
(#'images/create-image-collection conn {:id (mk-uuid "imgcoll" i)
|
(#'images/create-library conn {:id (mk-uuid "imgcoll" i)
|
||||||
:profile-id profile-id
|
:team-id team-id
|
||||||
:name (str "image collection " i)}))
|
:name (str "image library " i)}))
|
||||||
|
|
||||||
(defn create-icon-collection
|
(defn create-icon-library
|
||||||
[conn profile-id i]
|
[conn team-id i]
|
||||||
(#'icons/create-icon-collection conn {:id (mk-uuid "imgcoll" i)
|
(#'icons/create-library conn {:id (mk-uuid "imgcoll" i)
|
||||||
:profile-id profile-id
|
:team-id team-id
|
||||||
:name (str "icon collection " i)}))
|
:name (str "icon library " i)}))
|
||||||
(defn create-color-collection
|
(defn create-color-library
|
||||||
[conn profile-id i]
|
[conn team-id i]
|
||||||
(#'colors/create-color-collection conn {:id (mk-uuid "imgcoll" i)
|
(#'colors/create-library conn {:id (mk-uuid "imgcoll" i)
|
||||||
:profile-id profile-id
|
:team-id team-id
|
||||||
:name (str "color collection " i)}))
|
:name (str "color library " i)}))
|
||||||
|
|
||||||
(defn handle-error
|
(defn handle-error
|
||||||
[^Throwable err]
|
[^Throwable err]
|
||||||
|
|
|
@ -16,14 +16,16 @@
|
||||||
(t/use-fixtures :once th/state-init)
|
(t/use-fixtures :once th/state-init)
|
||||||
(t/use-fixtures :each th/database-reset)
|
(t/use-fixtures :each th/database-reset)
|
||||||
|
|
||||||
(t/deftest color-collections-crud
|
(t/deftest color-libraries-crud
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
profile @(th/create-profile db/pool 2)]
|
prof @(th/create-profile db/pool 2)
|
||||||
|
team (:default-team prof)]
|
||||||
|
|
||||||
(t/testing "create collection"
|
(t/testing "create library"
|
||||||
(let [data {::sm/type :create-color-collection
|
(let [data {::sm/type :create-color-library
|
||||||
:name "sample collection"
|
:name "sample library"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
|
@ -31,38 +33,38 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (:profile-id result)))
|
(t/is (= id (:id result)))
|
||||||
|
(t/is (= (:id team) (:team-id result)))
|
||||||
(t/is (= (:name data) (:name result))))))
|
(t/is (= (:name data) (:name result))))))
|
||||||
|
|
||||||
(t/testing "update collection"
|
(t/testing "update library"
|
||||||
(let [data {::sm/type :rename-color-collection
|
(let [data {::sm/type :rename-color-library
|
||||||
:name "sample collection renamed"
|
:name "renamed"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/is (= id (get-in out [:result :id])))
|
(t/testing "query libraries"
|
||||||
(t/is (= (:id profile) (get-in out [:result :profile-id])))
|
(let [data {::sq/type :color-libraries
|
||||||
(t/is (= (:name data) (get-in out [:result :name])))))
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)}
|
||||||
(t/testing "query collections"
|
|
||||||
(let [data {::sq/type :color-collections
|
|
||||||
:profile-id (:id profile)}
|
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(t/is (= 1 (count (:result out))))
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (get-in out [:result 0 :profile-id])))
|
(t/is (= 1 (count result)))
|
||||||
(t/is (= id (get-in out [:result 0 :id])))))
|
(t/is (= (:id team) (get-in result [0 :team-id])))
|
||||||
|
(t/is (= "renamed" (get-in result [0 :name]))))))
|
||||||
|
|
||||||
(t/testing "delete collection"
|
(t/testing "delete library"
|
||||||
(let [data {::sm/type :delete-color-collection
|
(let [data {::sm/type :delete-color-library
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id id}
|
:id id}
|
||||||
|
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
@ -71,26 +73,29 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (:result out)))))
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/testing "query collections after delete"
|
(t/testing "query libraries after delete"
|
||||||
(let [data {::sq/type :color-collections
|
(let [data {::sq/type :color-libraries
|
||||||
:profile-id (:id profile)}
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (= 0 (count (:result out))))))
|
(let [result (:result out)]
|
||||||
|
(t/is (= 0 (count result))))))
|
||||||
))
|
))
|
||||||
|
|
||||||
(t/deftest colors-crud
|
(t/deftest colors-crud
|
||||||
(let [profile @(th/create-profile db/pool 1)
|
(let [prof @(th/create-profile db/pool 1)
|
||||||
coll @(th/create-color-collection db/pool (:id profile) 1)
|
team (:default-team prof)
|
||||||
|
coll @(th/create-color-library db/pool (:id team) 1)
|
||||||
color-id (uuid/next)]
|
color-id (uuid/next)]
|
||||||
|
|
||||||
(t/testing "upload color to collection"
|
(t/testing "upload color to library"
|
||||||
(let [data {::sm/type :create-color
|
(let [data {::sm/type :create-color
|
||||||
:id color-id
|
:id color-id
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)
|
:library-id (:id coll)
|
||||||
:name "testfile"
|
:name "testfile"
|
||||||
:content "#222222"}
|
:content "#222222"}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
@ -103,10 +108,10 @@
|
||||||
(t/is (= (:name data) (:name result)))
|
(t/is (= (:name data) (:name result)))
|
||||||
(t/is (= (:content data) (:content result))))))
|
(t/is (= (:content data) (:content result))))))
|
||||||
|
|
||||||
(t/testing "list colors by collection"
|
(t/testing "list colors by library"
|
||||||
(let [data {::sq/type :colors
|
(let [data {::sq/type :colors
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id coll)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
|
||||||
|
@ -115,7 +120,7 @@
|
||||||
|
|
||||||
(t/testing "single color"
|
(t/testing "single color"
|
||||||
(let [data {::sq/type :color
|
(let [data {::sq/type :color
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id color-id}
|
:id color-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
@ -125,7 +130,7 @@
|
||||||
|
|
||||||
(t/testing "delete colors"
|
(t/testing "delete colors"
|
||||||
(let [data {::sm/type :delete-color
|
(let [data {::sm/type :delete-color
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id color-id}
|
:id color-id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
|
@ -135,7 +140,7 @@
|
||||||
|
|
||||||
(t/testing "query color after delete"
|
(t/testing "query color after delete"
|
||||||
(let [data {::sq/type :color
|
(let [data {::sq/type :color
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id color-id}
|
:id color-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
|
@ -150,8 +155,8 @@
|
||||||
|
|
||||||
(t/testing "query colors after delete"
|
(t/testing "query colors after delete"
|
||||||
(let [data {::sq/type :colors
|
(let [data {::sq/type :colors
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id coll)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
|
|
|
@ -173,8 +173,8 @@
|
||||||
(t/is (string? (:thumb-path result)))
|
(t/is (string? (:thumb-path result)))
|
||||||
(t/is (string? (:thumb-uri result))))))
|
(t/is (string? (:thumb-uri result))))))
|
||||||
|
|
||||||
(t/testing "import from collection"
|
(t/testing "import from library"
|
||||||
(let [coll @(th/create-image-collection db/pool (:id prof) 1)
|
(let [lib @(th/create-image-library db/pool (:id team) 1)
|
||||||
image-id (uuid/next)
|
image-id (uuid/next)
|
||||||
|
|
||||||
content {:name "sample.jpg"
|
content {:name "sample.jpg"
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
data {::sm/type :upload-image
|
data {::sm/type :upload-image
|
||||||
:id image-id
|
:id image-id
|
||||||
:profile-id (:id prof)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)
|
:library-id (:id lib)
|
||||||
:name "testfile"
|
:name "testfile"
|
||||||
:content content}
|
:content content}
|
||||||
out1 (th/try-on! (sm/handle data))]
|
out1 (th/try-on! (sm/handle data))]
|
||||||
|
|
|
@ -16,14 +16,16 @@
|
||||||
(t/use-fixtures :once th/state-init)
|
(t/use-fixtures :once th/state-init)
|
||||||
(t/use-fixtures :each th/database-reset)
|
(t/use-fixtures :each th/database-reset)
|
||||||
|
|
||||||
(t/deftest icon-collections-crud
|
(t/deftest icon-libraries-crud
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
profile @(th/create-profile db/pool 2)]
|
prof @(th/create-profile db/pool 2)
|
||||||
|
team (:default-team prof)]
|
||||||
|
|
||||||
(t/testing "create collection"
|
(t/testing "create library"
|
||||||
(let [data {::sm/type :create-icon-collection
|
(let [data {::sm/type :create-icon-library
|
||||||
:name "sample collection"
|
:name "sample library"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
|
@ -31,38 +33,39 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (:profile-id result)))
|
(t/is (= id (:id result)))
|
||||||
|
(t/is (= (:id team) (:team-id result)))
|
||||||
(t/is (= (:name data) (:name result))))))
|
(t/is (= (:name data) (:name result))))))
|
||||||
|
|
||||||
(t/testing "update collection"
|
(t/testing "rename library"
|
||||||
(let [data {::sm/type :rename-icon-collection
|
(let [data {::sm/type :rename-icon-library
|
||||||
:name "sample collection renamed"
|
:name "renamed"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/is (= id (get-in out [:result :id])))
|
(t/testing "query libraries"
|
||||||
(t/is (= (:id profile) (get-in out [:result :profile-id])))
|
(let [data {::sq/type :icon-libraries
|
||||||
(t/is (= (:name data) (get-in out [:result :name])))))
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)}
|
||||||
(t/testing "query collections"
|
|
||||||
(let [data {::sq/type :icon-collections
|
|
||||||
:profile-id (:id profile)}
|
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(t/is (= 1 (count (:result out))))
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (get-in out [:result 0 :profile-id])))
|
(t/is (= 1 (count result)))
|
||||||
(t/is (= id (get-in out [:result 0 :id])))))
|
(t/is (= id (get-in result [0 :id])))
|
||||||
|
(t/is (= "renamed" (get-in result [0 :name]))))))
|
||||||
|
|
||||||
(t/testing "delete collection"
|
(t/testing "delete library"
|
||||||
(let [data {::sm/type :delete-icon-collection
|
(let [data {::sm/type :delete-icon-library
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id id}
|
:id id}
|
||||||
|
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
@ -71,26 +74,30 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (:result out)))))
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/testing "query collections after delete"
|
(t/testing "query libraries after delete"
|
||||||
(let [data {::sq/type :icon-collections
|
(let [data {::sq/type :icon-libraries
|
||||||
:profile-id (:id profile)}
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (= 0 (count (:result out))))))
|
|
||||||
|
(let [result (:result out)]
|
||||||
|
(t/is (= 0 (count result))))))
|
||||||
))
|
))
|
||||||
|
|
||||||
(t/deftest icons-crud
|
(t/deftest icons-crud
|
||||||
(let [profile @(th/create-profile db/pool 1)
|
(let [prof @(th/create-profile db/pool 1)
|
||||||
coll @(th/create-icon-collection db/pool (:id profile) 1)
|
team (:default-team prof)
|
||||||
|
coll @(th/create-icon-library db/pool (:id team) 1)
|
||||||
icon-id (uuid/next)]
|
icon-id (uuid/next)]
|
||||||
|
|
||||||
(t/testing "upload icon to collection"
|
(t/testing "upload icon to library"
|
||||||
(let [data {::sm/type :create-icon
|
(let [data {::sm/type :create-icon
|
||||||
:id icon-id
|
:id icon-id
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)
|
:library-id (:id coll)
|
||||||
:name "testfile"
|
:name "testfile"
|
||||||
:content "<rect></rect>"
|
:content "<rect></rect>"
|
||||||
:metadata {:width 100
|
:metadata {:width 100
|
||||||
|
@ -107,10 +114,10 @@
|
||||||
(t/is (= (:name data) (:name result)))
|
(t/is (= (:name data) (:name result)))
|
||||||
(t/is (= (:content data) (:content result))))))
|
(t/is (= (:content data) (:content result))))))
|
||||||
|
|
||||||
(t/testing "list icons by collection"
|
(t/testing "list icons by library"
|
||||||
(let [data {::sq/type :icons
|
(let [data {::sq/type :icons
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id coll)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
|
||||||
|
@ -119,7 +126,7 @@
|
||||||
|
|
||||||
(t/testing "single icon"
|
(t/testing "single icon"
|
||||||
(let [data {::sq/type :icon
|
(let [data {::sq/type :icon
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id icon-id}
|
:id icon-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
@ -129,17 +136,17 @@
|
||||||
|
|
||||||
(t/testing "delete icons"
|
(t/testing "delete icons"
|
||||||
(let [data {::sm/type :delete-icon
|
(let [data {::sm/type :delete-icon
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id icon-id}
|
:id icon-id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (get-in out [:result])))))
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/testing "query icon after delete"
|
(t/testing "query icon after delete"
|
||||||
(let [data {::sq/type :icon
|
(let [data {::sq/type :icon
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id icon-id}
|
:id icon-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
|
@ -154,9 +161,10 @@
|
||||||
|
|
||||||
(t/testing "query icons after delete"
|
(t/testing "query icons after delete"
|
||||||
(let [data {::sq/type :icons
|
(let [data {::sq/type :icons
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id coll)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
(t/is (= 0 (count result))))))
|
(t/is (= 0 (count result))))))
|
||||||
|
|
|
@ -16,14 +16,16 @@
|
||||||
(t/use-fixtures :once th/state-init)
|
(t/use-fixtures :once th/state-init)
|
||||||
(t/use-fixtures :each th/database-reset)
|
(t/use-fixtures :each th/database-reset)
|
||||||
|
|
||||||
(t/deftest image-collections-crud
|
(t/deftest image-libraries-crud
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
profile @(th/create-profile db/pool 2)]
|
prof @(th/create-profile db/pool 2)
|
||||||
|
team (:default-team prof)]
|
||||||
|
|
||||||
(t/testing "create collection"
|
(t/testing "create library"
|
||||||
(let [data {::sm/type :create-image-collection
|
(let [data {::sm/type :create-image-library
|
||||||
:name "sample collection"
|
:name "sample library"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
|
@ -31,38 +33,49 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (:profile-id result)))
|
(t/is (= (:id team) (:team-id result)))
|
||||||
(t/is (= (:name data) (:name result))))))
|
(t/is (= (:name data) (:name result))))))
|
||||||
|
|
||||||
(t/testing "update collection"
|
(t/testing "rename library"
|
||||||
(let [data {::sm/type :rename-image-collection
|
(let [data {::sm/type :rename-image-library
|
||||||
:name "sample collection renamed"
|
:name "renamed"
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id id}
|
:id id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/is (= id (get-in out [:result :id])))
|
(t/testing "query single library"
|
||||||
(t/is (= (:id profile) (get-in out [:result :profile-id])))
|
(let [data {::sq/type :image-library
|
||||||
(t/is (= (:name data) (get-in out [:result :name])))))
|
:profile-id (:id prof)
|
||||||
|
:id id}
|
||||||
(t/testing "query collections"
|
|
||||||
(let [data {::sq/type :image-collections
|
|
||||||
:profile-id (:id profile)}
|
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
|
|
||||||
(t/is (= 1 (count (:result out))))
|
(let [result (:result out)]
|
||||||
(t/is (= (:id profile) (get-in out [:result 0 :profile-id])))
|
(t/is (= id (:id result)))
|
||||||
(t/is (= id (get-in out [:result 0 :id])))))
|
(t/is (= "renamed" (:name result))))))
|
||||||
|
|
||||||
(t/testing "delete collection"
|
(t/testing "query libraries"
|
||||||
(let [data {::sm/type :delete-image-collection
|
(let [data {::sq/type :image-libraries
|
||||||
:profile-id (:id profile)
|
:team-id (:id team)
|
||||||
|
: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}
|
:id id}
|
||||||
|
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
@ -71,9 +84,10 @@
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (:result out)))))
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/testing "query collections after delete"
|
(t/testing "query libraries after delete"
|
||||||
(let [data {::sq/type :image-collections
|
(let [data {::sq/type :image-libraries
|
||||||
:profile-id (:id profile)}
|
:profile-id (:id prof)
|
||||||
|
:team-id (:id team)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
@ -82,19 +96,20 @@
|
||||||
))
|
))
|
||||||
|
|
||||||
(t/deftest images-crud
|
(t/deftest images-crud
|
||||||
(let [profile @(th/create-profile db/pool 1)
|
(let [prof @(th/create-profile db/pool 1)
|
||||||
coll @(th/create-image-collection db/pool (:id profile) 1)
|
team (:default-team prof)
|
||||||
|
lib @(th/create-image-library db/pool (:id team) 1)
|
||||||
image-id (uuid/next)]
|
image-id (uuid/next)]
|
||||||
|
|
||||||
(t/testing "upload image to collection"
|
(t/testing "upload image to library"
|
||||||
(let [content {:name "sample.jpg"
|
(let [content {:name "sample.jpg"
|
||||||
:path "tests/uxbox/tests/_files/sample.jpg"
|
:path "tests/uxbox/tests/_files/sample.jpg"
|
||||||
:mtype "image/jpeg"
|
:mtype "image/jpeg"
|
||||||
:size 312043}
|
:size 312043}
|
||||||
data {::sm/type :upload-image
|
data {::sm/type :upload-image
|
||||||
:id image-id
|
:id image-id
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)
|
:library-id (:id lib)
|
||||||
:name "testfile"
|
:name "testfile"
|
||||||
:content content}
|
:content content}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
@ -114,10 +129,10 @@
|
||||||
(t/is (string? (get-in out [:result :uri])))
|
(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 collection"
|
(t/testing "list images by library"
|
||||||
(let [data {::sq/type :images
|
(let [data {::sq/type :images
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id lib)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
|
||||||
|
@ -135,7 +150,7 @@
|
||||||
|
|
||||||
(t/testing "single image"
|
(t/testing "single image"
|
||||||
(let [data {::sq/type :image
|
(let [data {::sq/type :image
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id image-id}
|
:id image-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
@ -154,17 +169,17 @@
|
||||||
|
|
||||||
(t/testing "delete images"
|
(t/testing "delete images"
|
||||||
(let [data {::sm/type :delete-image
|
(let [data {::sm/type :delete-image
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id image-id}
|
:id image-id}
|
||||||
out (th/try-on! (sm/handle data))]
|
out (th/try-on! (sm/handle data))]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (get-in out [:result])))))
|
(t/is (nil? (:result out)))))
|
||||||
|
|
||||||
(t/testing "query image after delete"
|
(t/testing "query image after delete"
|
||||||
(let [data {::sq/type :image
|
(let [data {::sq/type :image
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:id image-id}
|
:id image-id}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
|
|
||||||
|
@ -179,8 +194,8 @@
|
||||||
|
|
||||||
(t/testing "query images after delete"
|
(t/testing "query images after delete"
|
||||||
(let [data {::sq/type :images
|
(let [data {::sq/type :images
|
||||||
:profile-id (:id profile)
|
:profile-id (:id prof)
|
||||||
:collection-id (:id coll)}
|
:library-id (:id lib)}
|
||||||
out (th/try-on! (sq/handle data))]
|
out (th/try-on! (sq/handle data))]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue