♻️ Rename collections to libraries.

And make them team dependent.
This commit is contained in:
Andrey Antukh 2020-02-24 10:57:32 +01:00
parent 0ad2b13d76
commit 3ce9c8820f
19 changed files with 1068 additions and 766 deletions

View file

@ -12,8 +12,6 @@
[uxbox.util.migrations :as mg]
[uxbox.util.template :as tmpl]))
;; --- Migrations
(def +migrations+
{:name "uxbox-main"
:steps
@ -29,20 +27,13 @@
{:desc "Initial tasks related tables"
:name "0004-tasks"
:fn (mg/resource "migrations/0004.tasks.sql")}
{:desc "Initial images tables"
:name "0005-images"
:fn (mg/resource "migrations/0005.images.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")}
]})
{:desc "Initial libraries tables"
:name "0005-libraries"
:fn (mg/resource "migrations/0005.libraries.sql")}]})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Entry point
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn migrate
[]

View file

@ -10,91 +10,87 @@
(ns uxbox.services.mutations.colors
(:require
[clojure.spec.alpha :as s]
[datoteka.core :as fs]
[datoteka.storages :as ds]
[promesa.core :as p]
[promesa.exec :as px]
[uxbox.common.exceptions :as ex]
[uxbox.common.spec :as us]
[uxbox.config :as cfg]
[uxbox.db :as db]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.tasks :as tasks]
[uxbox.services.queries.colors :refer [decode-row]]
[uxbox.services.queries.teams :as teams]
[uxbox.services.mutations :as sm]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
[uxbox.util.uuid :as uuid]
[vertx.util :as vu]))
[uxbox.util.uuid :as uuid]))
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(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)
;; --- Mutation: Create Collection
;; --- Mutation: Create Library
(declare create-color-collection)
(declare create-library)
(s/def ::create-color-collection
(s/keys :req-un [::profile-id ::name]
(s/def ::create-color-library
(s/keys :req-un [::profile-id ::team-id ::name]
:opt-un [::id]))
(sm/defmutation ::create-color-collection
[{:keys [id profile-id name] :as params}]
(sm/defmutation ::create-color-library
[{:keys [profile-id team-id] :as params}]
(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
"insert into color_collection (id, profile_id, name)
(def ^:private sql:create-library
"insert into color_library (id, team_id, name)
values ($1, $2, $3)
returning *;")
(defn- create-color-collection
[conn {:keys [id profile-id name] :as params}]
(defn- create-library
[conn {:keys [id team-id name]}]
(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
"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/def ::rename-color-library
(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}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id id)
(db/query-one conn [sql:rename-collection id name])))
(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 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
@ -112,34 +108,48 @@
;; (p/then' su/raise-not-found-if-nil))))
;; (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
;; [{:keys [profile-id id collection-id] :as params}]
;; [{:keys [profile-id id library-id] :as params}]
;; (db/with-atomic [conn db/pool]
;; (-> (retrieve-color conn {:profile-id profile-id :id id})
;; (p/then (fn [color]
;; (let [color (-> (dissoc color :id)
;; (assoc :collection-id collection-id))]
;; (assoc :library-id library-id))]
;; (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]))
(sm/defmutation ::delete-color-collection
[{:keys [profile-id id] :as params}]
(sm/defmutation ::delete-color-library
[{:keys [id profile-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))))
(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 :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)
(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]))
(sm/defmutation ::create-color
[{:keys [profile-id collection-id] :as params}]
[{:keys [profile-id library-id] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id collection-id)
(create-color conn params)))
(p/let [lib (select-library-for-update conn library-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(create-color conn params))))
(def ^:private sql:create-color
"insert into color (id, profile_id, name, collection_id, content)
values ($1, $2, $3, $4, $5) returning *")
"insert into color (id, name, library_id, content)
values ($1, $2, $3, $4) returning *")
(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))]
(-> (db/query-one conn [sql:create-color id profile-id name collection-id content])
(p/then' decode-row))))
(db/query-one conn [sql:create-color id name library-id content])))
;; --- Mutation: Update Color
;; --- Mutation: Rename Color
(def ^:private sql:update-color
"update color
set name = $3,
collection_id = $4
where id = $1
and profile_id = $2
returning *")
(declare select-color-for-update)
(declare rename-color)
(s/def ::update-color
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
(s/def ::rename-color
(s/keys :req-un [::id ::profile-id ::name]))
(sm/defmutation ::update-color
[{:keys [id name profile-id collection-id] :as params}]
(sm/defmutation ::rename-color
[{:keys [id profile-id name] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id collection-id)
(-> (db/query-one db/pool [sql:update-color id profile-id name collection-id])
(p/then' su/raise-not-found-if-nil))))
(p/let [clr (select-color-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id clr))
(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
"update color
set deleted_at = clock_timestamp()
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))
where id = $1")
(defn- delete-color
[conn id]
(-> (db/query-one conn [sql:mark-color-deleted id])
(p/then' su/constantly-nil)))

View file

@ -5,39 +5,32 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.mutations.icons
(:require
[clojure.spec.alpha :as s]
[datoteka.core :as fs]
[datoteka.storages :as ds]
[promesa.core :as p]
[promesa.exec :as px]
[uxbox.common.exceptions :as ex]
[uxbox.common.spec :as us]
[uxbox.config :as cfg]
[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.queries.icons :refer [decode-row]]
[uxbox.services.queries.teams :as teams]
[uxbox.services.util :as su]
[uxbox.tasks :as tasks]
[uxbox.util.blob :as blob]
[uxbox.util.data :as data]
[uxbox.util.uuid :as uuid]
[uxbox.util.storage :as ust]
[vertx.util :as vu]))
[uxbox.util.uuid :as uuid]))
;; --- Helpers & Specs
(s/def ::height ::us/integer)
(s/def ::id ::us/uuid)
(s/def ::library-id ::us/uuid)
(s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::collection-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::width ::us/integer)
(s/def ::height ::us/integer)
(s/def ::view-box
(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/keys :req-un [::profile-id ::name]
(s/def ::create-icon-library
(s/keys :req-un [::profile-id ::team-id ::name]
:opt-un [::id]))
(sm/defmutation ::create-icon-collection
[{:keys [id profile-id name] :as params}]
(sm/defmutation ::create-icon-library
[{:keys [profile-id team-id id name] :as params}]
(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
"insert into icon_collection (id, profile_id, name)
(def ^:private sql:create-library
"insert into icon_library (id, team_id, name)
values ($1, $2, $3)
returning *;")
(defn- create-icon-collection
[conn {:keys [id profile-id name] :as params}]
(defn- create-library
[conn {:keys [team-id id name] :as params}]
(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
"select id, profile_id
from icon_collection
where id=$1 and deleted_at is null
for update")
(declare select-library-for-update)
(declare rename-library)
(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 icon_collection
set name = $2
where id = $1
returning *")
(s/def ::rename-icon-collection
(s/def ::rename-icon-library
(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}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id id)
(db/query-one conn [sql:rename-collection id name])))
(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 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))))
;; (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
;; [{:keys [profile-id id collection-id] :as params}]
;; [{:keys [profile-id id library-id] :as params}]
;; (db/with-atomic [conn db/pool]
;; (-> (retrieve-icon conn {:profile-id profile-id :id id})
;; (p/then (fn [icon]
;; (let [icon (-> (dissoc icon :id)
;; (assoc :collection-id collection-id))]
;; (assoc :library-id library-id))]
;; (create-icon conn icon)))))))
;; --- Delete Collection
(def ^:private sql:mark-collection-deleted
"update icon_collection
;; --- Mutation: Delete Library
(declare delete-library)
(s/def ::delete-icon-library
(s/keys :req-un [::profile-id ::id]))
(sm/defmutation ::delete-icon-library
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(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()
where id = $1
returning id")
(s/def ::delete-icon-collection
(s/keys :req-un [::profile-id ::id]))
(sm/defmutation ::delete-icon-collection
[{: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))))
(defn- delete-library
[conn id]
(-> (db/query-one conn [sql:mark-library-deleted id])
(p/then' su/constantly-nil)))
@ -165,58 +174,72 @@
(declare 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]))
(sm/defmutation ::create-icon
[{:keys [profile-id collection-id] :as params}]
[{:keys [profile-id library-id] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id collection-id)
(create-icon conn params)))
(p/let [lib (select-library-for-update conn library-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(create-icon conn params))))
(def ^:private sql:create-icon
"insert into icon (id, profile_id, name, collection_id, content, metadata)
values ($1, $2, $3, $4, $5, $6) returning *")
"insert into icon (id, name, library_id, content, metadata)
values ($1, $2, $3, $4, $5) returning *")
(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))]
(-> (db/query-one conn [sql:create-icon id profile-id name
collection-id content (blob/encode metadata)])
(-> (db/query-one conn [sql:create-icon id name library-id
content (blob/encode metadata)])
(p/then' decode-row))))
;; --- Mutation: Update Icon
;; --- Mutation: Rename Icon
(def ^:private sql:update-icon
"update icon
set name = $3,
collection_id = $4
where id = $1
and profile_id = $2
returning *")
(declare select-icon-for-update)
(declare rename-icon)
(s/def ::update-icon
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
(s/def ::rename-icon
(s/keys :req-un [::id ::profile-id ::name]))
(sm/defmutation ::update-icon
[{:keys [id name profile-id collection-id] :as params}]
(sm/defmutation ::rename-icon
[{:keys [id profile-id name] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id collection-id)
(-> (db/query-one db/pool [sql:update-icon id profile-id name collection-id])
(p/then' su/raise-not-found-if-nil))))
(p/let [clr (select-icon-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id clr))
(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
(def ^:private sql:mark-icon-deleted
"update icon
set deleted_at = clock_timestamp()
where id = $1
and profile_id = $2
returning id")
(declare delete-icon)
(s/def ::delete-icon
(s/keys :req-un [::profile-id ::id]))
@ -224,14 +247,22 @@
(sm/defmutation ::delete-icon
[{:keys [id profile-id] :as params}]
(db/with-atomic [conn db/pool]
(-> (db/query-one conn [sql:mark-icon-deleted id profile-id])
(p/then' su/raise-not-found-if-nil))
(p/let [icn (select-icon-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id icn))
;; Schedule object deletion
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
:props {:id id :type :icon}})
;; Schedule object deletion
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
: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)))

View file

@ -5,15 +5,13 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.mutations.images
(:require
[clojure.spec.alpha :as s]
[datoteka.core :as fs]
[datoteka.storages :as ds]
[promesa.core :as p]
[promesa.exec :as px]
[uxbox.common.exceptions :as ex]
[uxbox.common.spec :as us]
[uxbox.config :as cfg]
@ -21,10 +19,9 @@
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.tasks :as tasks]
[uxbox.services.queries.teams :as teams]
[uxbox.services.mutations :as sm]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
[uxbox.util.data :as data]
[uxbox.util.uuid :as uuid]
[uxbox.util.storage :as ust]
[vertx.util :as vu]))
@ -32,102 +29,108 @@
(def thumbnail-options
{:width 800
:height 800
:quality 80
:quality 85
:format "webp"})
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(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-collection
(s/keys :req-un [::profile-id ::name]
(s/def ::create-image-library
(s/keys :req-un [::profile-id ::team-id ::name]
:opt-un [::id]))
(sm/defmutation ::create-image-collection
[{:keys [id profile-id name] :as params}]
(sm/defmutation ::create-image-library
[{:keys [profile-id team-id] :as params}]
(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
"insert into image_collection (id, profile_id, name)
(def ^:private sql:create-library
"insert into image_library (id, team_id, name)
values ($1, $2, $3)
returning *;")
(defn- create-image-collection
[conn {:keys [id profile-id name] :as params}]
(defn- create-library
[conn {:keys [id team-id name]}]
(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
"select id, profile_id
from image_collection
where id=$1 and deleted_at is null
(s/def ::rename-image-library
(s/keys :req-un [::id ::profile-id ::name]))
(sm/defmutation ::rename-image-library
[{:keys [profile-id id name] :as params}]
(db/with-atomic [conn db/pool]
(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")
(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))))
;; --- Rename Collection
(def ^:private sql:rename-image-collection
"update image_collection
(def ^:private sql:rename-library
"update image_library
set name = $2
where id = $1
returning *;")
where id = $1")
(s/def ::rename-image-collection
(s/keys :req-un [::id ::profile-id ::us/name]))
(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)))
(sm/defmutation ::rename-image-collection
[{:keys [id profile-id name] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id id)
(db/query-one conn [sql:rename-image-collection id name])))
(defn- rename-library
[conn id name]
(-> (db/query-one conn [sql:rename-library id name])
(p/then' su/constantly-nil)))
;; --- Delete Collection
;; --- Delete Library
(s/def ::delete-image-collection
(declare delete-library)
(s/def ::delete-image-library
(s/keys :req-un [::profile-id ::id]))
(def ^:private sql:mark-image-collection-as-deleted
"update image_collection
set deleted_at = clock_timestamp()
where id = $1
returning id")
(sm/defmutation ::delete-image-collection
(sm/defmutation ::delete-image-library
[{:keys [id profile-id] :as params}]
(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
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
:props {:id id :type :image-collection}})
;; Schedule object deletion
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
:props {:id id :type :image-library}})
(-> (db/query-one conn [sql:mark-image-collection-as-deleted id])
(p/then' su/raise-not-found-if-nil)
(p/then' su/constantly-nil))))
(delete-library conn id))))
(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 ::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]))
(sm/defmutation ::upload-image
[{:keys [collection-id profile-id] :as params}]
[{:keys [library-id profile-id] :as params}]
(db/with-atomic [conn db/pool]
(check-collection-edition-permissions! conn profile-id collection-id)
(create-image conn params)))
(p/let [lib (select-library-for-update conn library-id)]
(teams/check-edition-permissions! conn profile-id (:team-id lib))
(create-image conn params))))
(def ^:private sql:insert-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)
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 *")
(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))
(ex/raise :type :validation
:code :image-type-not-allowed
@ -184,8 +188,7 @@
sqlv [sql:insert-image
id
collection-id
profile-id
library-id
name
(str image-path)
(:width image-opts)
@ -202,7 +205,7 @@
(p/then' #(images/resolve-urls % :thumb-path :thumb-uri)))))
(defn persist-image-on-fs
[{:keys [name path] :as upload}]
[{:keys [name path]}]
(vu/blocking
(let [filename (fs/name name)]
(ust/save! media/media-storage filename path))))
@ -212,48 +215,68 @@
(vu/blocking
(let [input-path (ust/lookup media/media-storage input-path)
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))
(str "thumbnail-" filename))]
(ust/save! media/media-storage thumb-name thumb-data))))
;; --- Update Image
;; --- Mutation: Rename Image
(s/def ::update-image
(s/keys :req-un [::id ::profile-id ::name ::collection-id]))
(declare select-image-for-update)
(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
set name = $3,
collection_id = $2
where id = $1
and profile_id = $4
returning *;")
set name = $2
where id = $1")
(sm/defmutation ::update-image
[{:keys [id name profile-id collection-id] :as params}]
(-> (db/query-one db/pool [sql:update-image id
collection-id name profile-id])
(defn- select-image-for-update
[conn id]
(-> (db/query-one conn [sql:select-image-for-update id])
(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
;; (declare retrieve-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
;; [{:keys [profile-id id collection-id] :as params}]
;; [{:keys [profile-id id library-id] :as params}]
;; (letfn [(copy-image [conn {:keys [path] :as 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]
;; (-> image
;; (assoc :path (str path) :collection-id collection-id)
;; (assoc :path (str path) :library-id library-id)
;; (dissoc :id))))
;; (p/then (partial store-image-in-db conn))))]
@ -262,14 +285,10 @@
;; (p/then su/raise-not-found-if-nil)
;; (p/then (partial copy-image conn))))))
;; --- Delete Image
(def ^:private sql:mark-image-deleted
"update image
set deleted_at = clock_timestamp()
where id = $1
and profile_id = $2
returning id")
(declare delete-image)
(s/def ::delete-image
(s/keys :req-un [::id ::profile-id]))
@ -277,14 +296,22 @@
(sm/defmutation ::delete-image
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(-> (db/query-one conn [sql:mark-image-deleted id profile-id])
(p/then' su/raise-not-found-if-nil))
(p/let [img (select-image-for-update conn id)]
(teams/check-edition-permissions! conn profile-id (:team-id img))
;; Schedule object deletion
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
:props {:id id :type :image}})
;; Schedule object deletion
(tasks/schedule! conn {:name "delete-object"
:delay cfg/default-deletion-delay
: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)))

View file

@ -46,20 +46,43 @@
values ($1, $2, '', $3)
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
[conn {:keys [id profile-id name default?] :as params}]
(let [id (or id (uuid/next))
default? (if (boolean? default?) default? false)]
(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
[conn {:keys [team-id profile-id] :as params}]
(-> (db/query-one conn [sql:create-team-profile team-id profile-id])
(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))))))

View file

@ -17,6 +17,7 @@
[uxbox.db :as db]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.services.queries.teams :as teams]
[uxbox.services.queries :as sq]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
@ -28,55 +29,85 @@
(s/def ::id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::collection-id (s/nilable ::us/uuid))
(defn decode-row
[{:keys [metadata] :as row}]
(when row
(cond-> row
metadata (assoc :metadata (blob/decode metadata)))))
(s/def ::team-id ::us/uuid)
(s/def ::library-id (s/nilable ::us/uuid))
;; --- Query: Collections
;; --- Query: Colors Librarys
(def ^:private sql:collections
"select *,
(select count(*) from color where collection_id = ic.id) as num_colors
from color_collection as ic
where (ic.profile_id = $1 or
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
and ic.deleted_at is null
order by ic.created_at desc")
(def ^:private sql:libraries
"select lib.*,
(select count(*) from color where library_id = lib.id) as num_colors
from color_library as lib
where lib.team_id = $1
and lib.deleted_at is null
order by lib.created_at desc")
(s/def ::color-collections
(s/keys :req-un [::profile-id]))
(s/def ::color-libraries
(s/keys :req-un [::profile-id ::team-id]))
(sq/defquery ::color-collections
[{:keys [profile-id] :as params}]
(let [sqlv [sql:collections profile-id]]
(db/query db/pool sqlv)))
(sq/defquery ::color-libraries
[{:keys [profile-id team-id]}]
(db/with-atomic [conn db/pool]
(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
"select *
from color as i
where (i.profile_id = $1 or
i.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
and i.deleted_at is null
and i.collection_id = $2
order by i.created_at desc")
(declare retrieve-library)
(s/def ::color-library
(s/keys :req-un [::profile-id ::id]))
(sq/defquery ::color-library
[{:keys [profile-id id]}]
(db/with-atomic [conn db/pool]
(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/keys :req-un [::profile-id ::collection-id]))
(s/keys :req-un [::profile-id ::library-id]))
(sq/defquery ::colors
[{:keys [profile-id collection-id] :as params}]
(-> (db/query db/pool [sql:colors profile-id collection-id])
(p/then' #(mapv decode-row %))))
[{: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-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]))
(sq/defquery ::color
[{:keys [id] :as params}]
(-> (retrieve-color db/pool id)
(p/then' su/raise-not-found-if-nil)))
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(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
[conn id]
(let [sql "select * from color
where id = $1
and deleted_at is null;"]
(-> (db/query-one conn [sql id])
(p/then' su/raise-not-found-if-nil))))
(-> (db/query-one conn [sql:single-color id])
(p/then' su/raise-not-found-if-nil)))

View file

@ -17,6 +17,7 @@
[uxbox.db :as db]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.services.queries.teams :as teams]
[uxbox.services.queries :as sq]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
@ -27,8 +28,9 @@
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::collection-id (s/nilable ::us/uuid))
(s/def ::library-id ::us/uuid)
(defn decode-row
[{:keys [metadata] :as row}]
@ -38,45 +40,82 @@
;; --- Query: Collections
;; --- Query: Icons Librarys
(def ^:private sql:collections
"select *,
(select count(*) from icon where collection_id = ic.id) as num_icons
from icon_collection as ic
where (ic.profile_id = $1 or
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
and ic.deleted_at is null
order by ic.created_at desc")
(def ^:private sql:libraries
"select lib.*,
(select count(*) from icon where library_id = lib.id) as num_icons
from icon_library as lib
where lib.team_id = $1
and lib.deleted_at is null
order by lib.created_at desc")
(s/def ::icon-collections
(s/keys :req-un [::profile-id]))
(s/def ::icon-libraries
(s/keys :req-un [::profile-id ::team-id]))
(sq/defquery ::icon-collections
[{:keys [profile-id] :as params}]
(let [sqlv [sql:collections profile-id]]
(db/query db/pool sqlv)))
(sq/defquery ::icon-libraries
[{:keys [profile-id team-id]}]
(db/with-atomic [conn db/pool]
(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
"select *
from icon as i
where (i.profile_id = $1 or
i.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
and i.deleted_at is null
and i.collection_id = $2
order by i.created_at desc")
(declare retrieve-library)
(s/def ::icon-library
(s/keys :req-un [::profile-id ::id]))
(sq/defquery ::icon-library
[{:keys [profile-id id]}]
(db/with-atomic [conn db/pool]
(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/keys :req-un [::profile-id ::collection-id]))
(s/keys :req-un [::profile-id ::library-id]))
(sq/defquery ::icons
[{:keys [profile-id collection-id] :as params}]
(-> (db/query db/pool [sql:icons profile-id collection-id])
(p/then' #(mapv decode-row %))))
[{: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-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)
@ -88,15 +127,23 @@
(s/keys :req-un [::profile-id ::id]))
(sq/defquery ::icon
[{:keys [id] :as params}]
(-> (retrieve-icon db/pool id)
(p/then' su/raise-not-found-if-nil)))
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(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
[conn id]
(let [sql "select * from icon
where id = $1
and deleted_at is null;"]
(-> (db/query-one conn [sql id])
(p/then' su/raise-not-found-if-nil))))
(-> (db/query-one conn [sql:single-icon id])
(p/then' su/raise-not-found-if-nil)))

View file

@ -5,47 +5,105 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.images
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
[promesa.exec :as px]
[uxbox.common.exceptions :as ex]
[uxbox.common.spec :as us]
[uxbox.db :as db]
[uxbox.media :as media]
[uxbox.images :as images]
[uxbox.services.queries.teams :as teams]
[uxbox.services.queries :as sq]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
[uxbox.util.data :as data]
[uxbox.util.uuid :as uuid]
[vertx.core :as vc]))
[uxbox.services.util :as su]))
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(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
"select *,
(select count(*) from image where collection_id = ic.id) as num_images
from image_collection as ic
where (ic.profile_id = $1 or
ic.profile_id = '00000000-0000-0000-0000-000000000000'::uuid)
and ic.deleted_at is null
order by ic.created_at desc;")
(def ^:private sql:libraries
"select lib.*,
(select count(*) from image where library_id = lib.id) as num_images
from image_library as lib
where lib.team_id = $1
and lib.deleted_at is null
order by lib.created_at desc")
(s/def ::image-collections
(s/keys :req-un [::profile-id]))
(s/def ::image-libraries
(s/keys :req-un [::profile-id ::team-id]))
(sq/defquery ::image-collections
[{:keys [profile-id] :as params}]
(db/query db/pool [sql:collections profile-id]))
(sq/defquery ::image-libraries
[{:keys [profile-id team-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]))
(sq/defquery ::image
[{:keys [id] :as params}]
(-> (retrieve-image db/pool id)
(p/then' #(images/resolve-urls % :path :uri))
(p/then' #(images/resolve-urls % :thumb-path :thumb-uri))))
[{:keys [profile-id id] :as params}]
(db/with-atomic [conn db/pool]
(p/let [img (retrieve-image conn id)]
(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
[conn id]
(let [sql "select * from image
where id = $1
and deleted_at is null;"]
(-> (db/query-one conn [sql id])
(p/then' su/raise-not-found-if-nil))))
(-> (db/query-one conn [sql:single-image 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)))))))

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