Integrate objects-map and introduce file feature flags

This commit is contained in:
Andrey Antukh 2022-10-06 18:47:16 +02:00 committed by Andrés Moya
parent 69f084e1df
commit 951b3eb4fe
21 changed files with 406 additions and 264 deletions

View file

@ -352,10 +352,13 @@
[v] [v]
(and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v)))) (and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v))))
;; TODO rename to decode-pgarray-into
(defn decode-pgarray (defn decode-pgarray
([v] (some->> ^PgArray v .getArray vec)) ([v] (decode-pgarray v []))
([v in] (some->> ^PgArray v .getArray (into in))) ([v in]
([v in xf] (some->> ^PgArray v .getArray (into in xf)))) (into in (some-> ^PgArray v .getArray)))
([v in xf]
(into in xf (some-> ^PgArray v .getArray))))
(defn pgarray->set (defn pgarray->set
[v] [v]

View file

@ -254,6 +254,9 @@
{:name "0081-add-deleted-at-index-to-file-table" {:name "0081-add-deleted-at-index-to-file-table"
:fn (mg/resource "app/migrations/sql/0081-add-deleted-at-index-to-file-table.sql")} :fn (mg/resource "app/migrations/sql/0081-add-deleted-at-index-to-file-table.sql")}
{:name "0082-add-features-column-to-file-table"
:fn (mg/resource "app/migrations/sql/0082-add-features-column-to-file-table.sql")}
]) ])

View file

@ -0,0 +1,2 @@
ALTER TABLE file
ADD COLUMN features text[] DEFAULT NULL;

View file

@ -8,6 +8,8 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.files.features :as ffeat]
[app.common.logging :as l]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.migrations :as pmg] [app.common.pages.migrations :as pmg]
[app.common.spec :as us] [app.common.spec :as us]
@ -24,6 +26,7 @@
[app.rpc.semaphore :as rsem] [app.rpc.semaphore :as rsem]
[app.storage.impl :as simpl] [app.storage.impl :as simpl]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.objects-map :as omap]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt] [app.util.time :as dt]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
@ -44,10 +47,11 @@
;; --- Mutation: Create File ;; --- Mutation: Create File
(s/def ::features ::us/set-of-strings)
(s/def ::is-shared ::us/boolean) (s/def ::is-shared ::us/boolean)
(s/def ::create-file (s/def ::create-file
(s/keys :req-un [::profile-id ::name ::project-id] (s/keys :req-un [::profile-id ::name ::project-id]
:opt-un [::id ::is-shared ::components-v2])) :opt-un [::id ::is-shared ::features ::components-v2]))
(sv/defmethod ::create-file (sv/defmethod ::create-file
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
@ -68,11 +72,20 @@
(defn create-file (defn create-file
[conn {:keys [id name project-id is-shared data revn [conn {:keys [id name project-id is-shared data revn
modified-at deleted-at ignore-sync-until modified-at deleted-at ignore-sync-until
components-v2] components-v2 features]
:or {is-shared false revn 0} :or {is-shared false revn 0}
:as params}] :as params}]
(let [id (or id (:id data) (uuid/next)) (let [id (or id (:id data) (uuid/next))
data (or data (ctf/make-file-data id components-v2))
;; BACKWARD COMPATIBILITY with the components-v2 param
features (cond-> (or features #{})
components-v2 (conj "components/v2"))
data (or data
(binding [ffeat/*current* features]
(ctf/make-file-data id)))
features (db/create-array conn "text" features)
file (db/insert! conn :file file (db/insert! conn :file
(d/without-nils (d/without-nils
{:id id {:id id
@ -81,6 +94,7 @@
:revn revn :revn revn
:is-shared is-shared :is-shared is-shared
:data (blob/encode data) :data (blob/encode data)
:features features
:ignore-sync-until ignore-sync-until :ignore-sync-until ignore-sync-until
:modified-at modified-at :modified-at modified-at
:deleted-at deleted-at}))] :deleted-at deleted-at}))]
@ -88,7 +102,7 @@
(->> (assoc params :file-id id :role :owner) (->> (assoc params :file-id id :role :owner)
(create-file-role conn)) (create-file-role conn))
(assoc file :data data))) (-> file files/decode-row)))
;; --- Mutation: Rename File ;; --- Mutation: Rename File
@ -309,24 +323,59 @@
(s/def ::update-file (s/def ::update-file
(s/and (s/and
(s/keys :req-un [::id ::session-id ::profile-id ::revn] (s/keys :req-un [::id ::session-id ::profile-id ::revn]
:opt-un [::changes ::changes-with-metadata ::components-v2]) :opt-un [::changes ::changes-with-metadata ::components-v2 ::features])
(fn [o] (fn [o]
(or (contains? o :changes) (or (contains? o :changes)
(contains? o :changes-with-metadata))))) (contains? o :changes-with-metadata)))))
(def ^:private sql:retrieve-file
"SELECT f.*, p.team_id
FROM file AS f
JOIN project AS p ON (p.id = f.project_id)
WHERE f.id = ?
AND (f.deleted_at IS NULL OR
f.deleted_at > now())
FOR KEY SHARE")
(sv/defmethod ::update-file (sv/defmethod ::update-file
{::rsem/queue :update-file} {::rsem/queue :update-file}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [id profile-id components-v2] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(db/xact-lock! conn id) (db/xact-lock! conn id)
(let [{:keys [id] :as file} (db/get-by-id conn :file id {:for-key-share true}) (let [file (db/exec-one! conn [sql:retrieve-file id])
team-id (retrieve-team-id conn (:project-id file))] features' (:features params #{})
(files/check-edition-permissions! conn profile-id id) features (db/decode-pgarray (:features file) features')
;; BACKWARD COMPATIBILITY with the components-v2 parameter
features (cond-> features
components-v2 (conj "components/v2"))
file (assoc file :features features)]
(when-not file
(ex/raise :type :not-found
:code :object-not-found
:hint (format "file with id '%s' does not exists" id)))
;; If features are specified from params and the final feature
;; set is different than the persisted one, update it on the
;; database.
(when (not= features features')
(let [features (db/create-array conn "text" features)]
(db/update! conn :file
{:features features}
{:id id})))
(binding [ffeat/*current* features
ffeat/*wrap-objects-fn* (if (features "storate/objects-map")
omap/wrap
identity)]
(files/check-edition-permissions! conn profile-id (:id file))
(with-meta (with-meta
(update-file (assoc cfg :conn conn) (update-file (assoc cfg :conn conn)
(assoc params :file file)) (assoc params :file file))
{::audit/props {:project-id (:project-id file) {::audit/props {:project-id (:project-id file)
:team-id team-id}})))) :team-id (:team-id file)}})))))
(defn- take-snapshot? (defn- take-snapshot?
"Defines the rule when file `data` snapshot should be saved." "Defines the rule when file `data` snapshot should be saved."
@ -347,7 +396,7 @@
(defn- update-file (defn- update-file
[{:keys [conn metrics] :as cfg} [{:keys [conn metrics] :as cfg}
{:keys [file changes changes-with-metadata session-id profile-id components-v2] :as params}] {:keys [file changes changes-with-metadata session-id profile-id] :as params}]
(when (> (:revn params) (when (> (:revn params)
(:revn file)) (:revn file))
@ -378,7 +427,8 @@
(assoc :id (:id file)) (assoc :id (:id file))
(pmg/migrate-data)) (pmg/migrate-data))
components-v2
(contains? ffeat/*current* "components/v2")
(ctf/migrate-to-components-v2) (ctf/migrate-to-components-v2)
:always :always
@ -455,7 +505,8 @@
:changes changes}) :changes changes})
(when (and (:is-shared file) (seq lchanges)) (when (and (:is-shared file) (seq lchanges))
(let [team-id (retrieve-team-id conn (:project-id file))] (let [team-id (or (:team-id file)
(retrieve-team-id conn (:project-id file)))]
;; Asynchronously publish message to the msgbus ;; Asynchronously publish message to the msgbus
(mbus/pub! msgbus (mbus/pub! msgbus
:topic team-id :topic team-id

View file

@ -227,29 +227,34 @@
(d/index-by :object-id :data)))))) (d/index-by :object-id :data))))))
(defn retrieve-file (defn retrieve-file
[{:keys [pool] :as cfg} id components-v2] [{:keys [pool] :as cfg} id features]
(let [file (->> (db/get-by-id pool :file id) (let [file (->> (db/get-by-id pool :file id)
(decode-row) (decode-row)
(pmg/migrate-file))] (pmg/migrate-file))]
(if components-v2 (if (contains? features "components/v2")
(update file :data ctf/migrate-to-components-v2) (update file :data ctf/migrate-to-components-v2)
(if (get-in file [:data :options :components-v2]) (if (dm/get-in file [:data :options :components-v2])
(ex/raise :type :restriction (ex/raise :type :restriction
:code :feature-disabled :code :feature-disabled
:hint "tried to open a components-v2 file with feature disabled") :hint "tried to open a components/v2 file with feature disabled")
file)))) file))))
(s/def ::features ::us/set-of-strings)
(s/def ::file (s/def ::file
(s/keys :req-un [::profile-id ::id] (s/keys :req-un [::profile-id ::id]
:opt-un [::components-v2])) :opt-un [::features ::components-v2]))
(sv/defmethod ::file (sv/defmethod ::file
"Retrieve a file by its ID. Only authenticated users." "Retrieve a file by its ID. Only authenticated users."
[{:keys [pool] :as cfg} {:keys [profile-id id components-v2] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id id features components-v2] :as params}]
(let [perms (get-permissions pool profile-id id)] (let [perms (get-permissions pool profile-id id)
;; BACKWARD COMPATIBILTY with the components-v2 parameter
features (cond-> (or features #{})
components-v2 (conj features "components/v2"))]
(check-read-permissions! perms) (check-read-permissions! perms)
(let [file (retrieve-file cfg id components-v2) (let [file (retrieve-file cfg id features)
thumbs (retrieve-object-thumbnails cfg id)] thumbs (retrieve-object-thumbnails cfg id)]
(-> file (-> file
(assoc :thumbnails thumbs) (assoc :thumbnails thumbs)
@ -278,7 +283,7 @@
(s/def ::page (s/def ::page
(s/and (s/and
(s/keys :req-un [::profile-id ::file-id] (s/keys :req-un [::profile-id ::file-id]
:opt-un [::page-id ::object-id ::components-v2]) :opt-un [::page-id ::object-id ::features ::components-v2])
(fn [obj] (fn [obj]
(if (contains? obj :object-id) (if (contains? obj :object-id)
(contains? obj :page-id) (contains? obj :page-id)
@ -294,11 +299,15 @@
mandatory. mandatory.
Mainly used for rendering purposes." Mainly used for rendering purposes."
[{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id components-v2] :as props}] [{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id features components-v2] :as props}]
(check-read-permissions! pool profile-id file-id) (check-read-permissions! pool profile-id file-id)
(let [file (retrieve-file cfg file-id components-v2) (let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
features (cond-> (or features #{})
components-v2 (conj features "components/v2"))
file (retrieve-file cfg file-id features)
page-id (or page-id (-> file :data :pages first)) page-id (or page-id (-> file :data :pages first))
page (get-in file [:data :pages-index page-id])] page (dm/get-in file [:data :pages-index page-id])]
(cond-> (prune-thumbnails page) (cond-> (prune-thumbnails page)
(uuid? object-id) (uuid? object-id)
@ -384,14 +393,17 @@
(s/def ::file-data-for-thumbnail (s/def ::file-data-for-thumbnail
(s/keys :req-un [::profile-id ::file-id] (s/keys :req-un [::profile-id ::file-id]
:opt-un [::components-v2])) :opt-un [::components-v2 ::features]))
(sv/defmethod ::file-data-for-thumbnail (sv/defmethod ::file-data-for-thumbnail
"Retrieves the data for generate the thumbnail of the file. Used "Retrieves the data for generate the thumbnail of the file. Used
mainly for render thumbnails on dashboard." mainly for render thumbnails on dashboard."
[{:keys [pool] :as cfg} {:keys [profile-id file-id components-v2] :as props}] [{:keys [pool] :as cfg} {:keys [profile-id file-id features components-v2] :as props}]
(check-read-permissions! pool profile-id file-id) (check-read-permissions! pool profile-id file-id)
(let [file (retrieve-file cfg file-id components-v2)] (let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
features (cond-> (or features #{})
components-v2 (conj features "components/v2"))
file (retrieve-file cfg file-id features)]
{:file-id file-id {:file-id file-id
:revn (:revn file) :revn (:revn file)
:page (get-file-thumbnail-data cfg file)})) :page (get-file-thumbnail-data cfg file)}))
@ -567,8 +579,9 @@
;; --- Helpers ;; --- Helpers
(defn decode-row (defn decode-row
[{:keys [data changes] :as row}] [{:keys [data changes features] :as row}]
(when row (when row
(cond-> row (cond-> row
features (assoc :features (db/decode-pgarray features))
changes (assoc :changes (blob/decode changes)) changes (assoc :changes (blob/decode changes))
data (assoc :data (blob/decode data))))) data (assoc :data (blob/decode data)))))

View file

@ -23,8 +23,8 @@
(db/get-by-id pool :project id {:columns [:id :name :team-id]})) (db/get-by-id pool :project id {:columns [:id :name :team-id]}))
(defn- retrieve-bundle (defn- retrieve-bundle
[{:keys [pool] :as cfg} file-id profile-id components-v2] [{:keys [pool] :as cfg} file-id profile-id features]
(p/let [file (files/retrieve-file cfg file-id components-v2) (p/let [file (files/retrieve-file cfg file-id features)
project (retrieve-project pool (:project-id file)) project (retrieve-project pool (:project-id file))
libs (files/retrieve-file-libraries cfg false file-id) libs (files/retrieve-file-libraries cfg false file-id)
users (comments/get-file-comments-users pool file-id profile-id) users (comments/get-file-comments-users pool file-id profile-id)
@ -45,22 +45,30 @@
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::profile-id ::us/uuid) (s/def ::profile-id ::us/uuid)
(s/def ::share-id ::us/uuid) (s/def ::share-id ::us/uuid)
(s/def ::features ::us/set-of-strings)
;; TODO: deprecated, should be removed when version >= 1.18
(s/def ::components-v2 ::us/boolean) (s/def ::components-v2 ::us/boolean)
(s/def ::view-only-bundle (s/def ::view-only-bundle
(s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id ::components-v2])) (s/keys :req-un [::file-id]
:opt-un [::profile-id ::share-id ::features ::components-v2]))
(sv/defmethod ::view-only-bundle {:auth false} (sv/defmethod ::view-only-bundle {:auth false}
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id components-v2] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id file-id share-id features components-v2] :as params}]
(p/let [slink (slnk/retrieve-share-link pool file-id share-id) (p/let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
features (cond-> (or features #{})
components-v2 (conj features "components/v2"))
slink (slnk/retrieve-share-link pool file-id share-id)
perms (files/get-permissions pool profile-id file-id share-id) perms (files/get-permissions pool profile-id file-id share-id)
thumbs (files/retrieve-object-thumbnails cfg file-id) thumbs (files/retrieve-object-thumbnails cfg file-id)
bundle (p/-> (retrieve-bundle cfg file-id profile-id components-v2) bundle (p/-> (retrieve-bundle cfg file-id profile-id features)
(assoc :permissions perms) (assoc :permissions perms)
(assoc-in [:file :thumbnails] thumbs))] (assoc-in [:file :thumbnails] thumbs))]
;; When we have neither profile nor share, we just return a not ;; When we have neither profile nor share, we just return a not
;; found response to the user. ;; found response to the user.
(do
(when (and (not profile-id) (when (and (not profile-id)
(not slink)) (not slink))
(ex/raise :type :not-found (ex/raise :type :not-found
@ -81,4 +89,5 @@
(let [allowed-pages (:pages slink)] (let [allowed-pages (:pages slink)]
(-> data (-> data
(update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages))) (update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages)))
(update :pages-index (fn [index] (select-keys index allowed-pages)))))))))) (update :pages-index (fn [index] (select-keys index allowed-pages)))))))))))

View file

@ -148,38 +148,41 @@
(defn create-profile* (defn create-profile*
([i] (create-profile* *pool* i {})) ([i] (create-profile* *pool* i {}))
([i params] (create-profile* *pool* i params)) ([i params] (create-profile* *pool* i params))
([conn i params] ([pool i params]
(let [params (merge {:id (mk-uuid "profile" i) (let [params (merge {:id (mk-uuid "profile" i)
:fullname (str "Profile " i) :fullname (str "Profile " i)
:email (str "profile" i ".test@nodomain.com") :email (str "profile" i ".test@nodomain.com")
:password "123123" :password "123123"
:is-demo false} :is-demo false}
params)] params)]
(with-open [conn (db/open pool)]
(->> params (->> params
(cmd.auth/create-profile conn) (cmd.auth/create-profile conn)
(cmd.auth/create-profile-relations conn))))) (cmd.auth/create-profile-relations conn))))))
(defn create-project* (defn create-project*
([i params] (create-project* *pool* i params)) ([i params] (create-project* *pool* i params))
([conn i {:keys [profile-id team-id] :as params}] ([pool i {:keys [profile-id team-id] :as params}]
(us/assert uuid? profile-id) (us/assert uuid? profile-id)
(us/assert uuid? team-id) (us/assert uuid? team-id)
(with-open [conn (db/open pool)]
(->> (merge {:id (mk-uuid "project" i) (->> (merge {:id (mk-uuid "project" i)
:name (str "project" i)} :name (str "project" i)}
params) params)
(#'projects/create-project conn)))) (#'projects/create-project conn)))))
(defn create-file* (defn create-file*
([i params] ([i params]
(create-file* *pool* i params)) (create-file* *pool* i params))
([conn i {:keys [profile-id project-id] :as params}] ([pool i {:keys [profile-id project-id] :as params}]
(us/assert uuid? profile-id) (us/assert uuid? profile-id)
(us/assert uuid? project-id) (us/assert uuid? project-id)
(with-open [conn (db/open pool)]
(#'files/create-file conn (#'files/create-file conn
(merge {:id (mk-uuid "file" i) (merge {:id (mk-uuid "file" i)
:name (str "file" i) :name (str "file" i)
:components-v2 true} :components-v2 true}
params)))) params)))))
(defn mark-file-deleted* (defn mark-file-deleted*
([params] (mark-file-deleted* *pool* params)) ([params] (mark-file-deleted* *pool* params))
@ -188,17 +191,20 @@
(defn create-team* (defn create-team*
([i params] (create-team* *pool* i params)) ([i params] (create-team* *pool* i params))
([conn i {:keys [profile-id] :as params}] ([pool i {:keys [profile-id] :as params}]
(us/assert uuid? profile-id) (us/assert uuid? profile-id)
(with-open [conn (db/open pool)]
(let [id (mk-uuid "team" i)] (let [id (mk-uuid "team" i)]
(teams/create-team conn {:id id (teams/create-team conn {:id id
:profile-id profile-id :profile-id profile-id
:name (str "team" i)})))) :name (str "team" i)})))))
(defn create-file-media-object* (defn create-file-media-object*
([params] (create-file-media-object* *pool* params)) ([params] (create-file-media-object* *pool* params))
([conn {:keys [name width height mtype file-id is-local media-id] ([pool {:keys [name width height mtype file-id is-local media-id]
:or {name "sample" width 100 height 100 mtype "image/svg+xml" is-local true}}] :or {name "sample" width 100 height 100 mtype "image/svg+xml" is-local true}}]
(with-open [conn (db/open pool)]
(db/insert! conn :file-media-object (db/insert! conn :file-media-object
{:id (uuid/next) {:id (uuid/next)
:file-id file-id :file-id file-id
@ -207,54 +213,61 @@
:media-id media-id :media-id media-id
:width width :width width
:height height :height height
:mtype mtype}))) :mtype mtype}))))
(defn link-file-to-library* (defn link-file-to-library*
([params] (link-file-to-library* *pool* params)) ([params] (link-file-to-library* *pool* params))
([conn {:keys [file-id library-id] :as params}] ([pool {:keys [file-id library-id] :as params}]
(#'files/link-file-to-library conn {:file-id file-id :library-id library-id}))) (with-open [conn (db/open pool)]
(#'files/link-file-to-library conn {:file-id file-id :library-id library-id}))))
(defn create-complaint-for (defn create-complaint-for
[conn {:keys [id created-at type]}] [pool {:keys [id created-at type]}]
(with-open [conn (db/open pool)]
(db/insert! conn :profile-complaint-report (db/insert! conn :profile-complaint-report
{:profile-id id {:profile-id id
:created-at (or created-at (dt/now)) :created-at (or created-at (dt/now))
:type (name type) :type (name type)
:content (db/tjson {})})) :content (db/tjson {})})))
(defn create-global-complaint-for (defn create-global-complaint-for
[conn {:keys [email type created-at]}] [pool {:keys [email type created-at]}]
(with-open [conn (db/open pool)]
(db/insert! conn :global-complaint-report (db/insert! conn :global-complaint-report
{:email email {:email email
:type (name type) :type (name type)
:created-at (or created-at (dt/now)) :created-at (or created-at (dt/now))
:content (db/tjson {})})) :content (db/tjson {})})))
(defn create-team-role* (defn create-team-role*
([params] (create-team-role* *pool* params)) ([params] (create-team-role* *pool* params))
([conn {:keys [team-id profile-id role] :or {role :owner}}] ([pool {:keys [team-id profile-id role] :or {role :owner}}]
(with-open [conn (db/open pool)]
(#'teams/create-team-role conn {:team-id team-id (#'teams/create-team-role conn {:team-id team-id
:profile-id profile-id :profile-id profile-id
:role role}))) :role role}))))
(defn create-project-role* (defn create-project-role*
([params] (create-project-role* *pool* params)) ([params] (create-project-role* *pool* params))
([conn {:keys [project-id profile-id role] :or {role :owner}}] ([pool {:keys [project-id profile-id role] :or {role :owner}}]
(with-open [conn (db/open pool)]
(#'projects/create-project-role conn {:project-id project-id (#'projects/create-project-role conn {:project-id project-id
:profile-id profile-id :profile-id profile-id
:role role}))) :role role}))))
(defn create-file-role* (defn create-file-role*
([params] (create-file-role* *pool* params)) ([params] (create-file-role* *pool* params))
([conn {:keys [file-id profile-id role] :or {role :owner}}] ([pool {:keys [file-id profile-id role] :or {role :owner}}]
(with-open [conn (db/open pool)]
(#'files/create-file-role conn {:file-id file-id (#'files/create-file-role conn {:file-id file-id
:profile-id profile-id :profile-id profile-id
:role role}))) :role role}))))
(defn update-file* (defn update-file*
([params] (update-file* *pool* params)) ([params] (update-file* *pool* params))
([conn {:keys [file-id changes session-id profile-id revn] ([pool {:keys [file-id changes session-id profile-id revn]
:or {session-id (uuid/next) revn 0}}] :or {session-id (uuid/next) revn 0}}]
(with-open [conn (db/open pool)]
(let [file (db/get-by-id conn :file file-id) (let [file (db/get-by-id conn :file file-id)
msgbus (:app.msgbus/msgbus *system*) msgbus (:app.msgbus/msgbus *system*)
metrics (:app.metrics/metrics *system*)] metrics (:app.metrics/metrics *system*)]
@ -266,7 +279,7 @@
:components-v2 true :components-v2 true
:changes changes :changes changes
:session-id session-id :session-id session-id
:profile-id profile-id})))) :profile-id profile-id})))))
;; --- RPC HELPERS ;; --- RPC HELPERS

View file

@ -0,0 +1,10 @@
;; 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/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.files.features)
(def ^:dynamic *current* #{})
(def ^:dynamic *wrap-objects-fn* identity)

View file

@ -347,27 +347,12 @@
;; -- Components ;; -- Components
(defmethod process-change :add-component (defmethod process-change :add-component
[data {:keys [id name path main-instance-id main-instance-page shapes]}] [data params]
(ctkl/add-component data (ctkl/add-component data params))
id
name
path
main-instance-id
main-instance-page
shapes))
(defmethod process-change :mod-component (defmethod process-change :mod-component
[data {:keys [id name path objects]}] [data params]
(update-in data [:components id] (ctkl/mod-component data params))
#(cond-> %
(some? name)
(assoc :name name)
(some? path)
(assoc :path path)
(some? objects)
(assoc :objects objects))))
(defmethod process-change :del-component (defmethod process-change :del-component
[data {:keys [id skip-undelete?]}] [data {:keys [id skip-undelete?]}]

View file

@ -8,6 +8,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.features :as ffeat]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
@ -50,9 +51,11 @@
(defn with-objects (defn with-objects
[changes objects] [changes objects]
(let [file-data (-> (ctf/make-file-data (uuid/next) uuid/zero true) (let [fdata (binding [ffeat/*current* #{"components/v2"}]
(assoc-in [:pages-index uuid/zero :objects] objects))] (ctf/make-file-data (uuid/next) uuid/zero))
(vary-meta changes assoc ::file-data file-data fdata (assoc-in fdata [:pages-index uuid/zero :objects] objects)]
(vary-meta changes assoc
::file-data fdata
::applied-changes-count 0))) ::applied-changes-count 0)))
(defn with-library-data (defn with-library-data

View file

@ -2,31 +2,53 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) UXBOX Labs SL ;; Copyright (c) KELEIDOS INC
(ns app.common.types.components-list (ns app.common.types.components-list
(:require (:require
[app.common.data :as d])) [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.features :as feat]))
(defn components-seq (defn components-seq
[file-data] [file-data]
(vals (:components file-data))) (vals (:components file-data)))
(defn add-component (defn add-component
[file-data id name path main-instance-id main-instance-page shapes] [file-data {:keys [id name path main-instance-id main-instance-page shapes]}]
(let [components-v2 (get-in file-data [:options :components-v2])] (let [components-v2 (dm/get-in file-data [:options :components-v2])
wrap-object-fn feat/*wrap-objects-fn*]
(cond-> file-data (cond-> file-data
:always :always
(assoc-in [:components id] (assoc-in [:components id]
{:id id {:id id
:name name :name name
:path path :path path
:objects (d/index-by :id shapes)}) :objects (->> shapes
(d/index-by :id)
(wrap-object-fn))})
components-v2 components-v2
(update-in [:components id] assoc :main-instance-id main-instance-id (update-in [:components id] assoc
:main-instance-id main-instance-id
:main-instance-page main-instance-page)))) :main-instance-page main-instance-page))))
(defn mod-component
[file-data {:keys [id name path objects]}]
(let [wrap-objects-fn feat/*wrap-objects-fn*]
(update-in file-data [:components id]
(fn [component]
(let [objects (some-> objects wrap-objects-fn)]
(cond-> component
(some? name)
(assoc :name name)
(some? path)
(assoc :path path)
(some? objects)
(assoc :objects objects)))))))
(defn get-component (defn get-component
[file-data component-id] [file-data component-id]
(get-in file-data [:components component-id])) (get-in file-data [:components component-id]))

View file

@ -6,6 +6,7 @@
(ns app.common.types.container (ns app.common.types.container
(:require (:require
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.spec :as us] [app.common.spec :as us]
@ -41,8 +42,8 @@
(us/assert uuid? id) (us/assert uuid? id)
(-> (if (= type :page) (-> (if (= type :page)
(get-in file [:pages-index id]) (dm/get-in file [:pages-index id])
(get-in file [:components id])) (dm/get-in file [:components id]))
(assoc :type type))) (assoc :type type)))
(defn get-shape (defn get-shape

View file

@ -7,6 +7,8 @@
(ns app.common.types.file (ns app.common.types.file
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.features :as ffeat]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages.common :refer [file-version]] [app.common.pages.common :refer [file-version]]
@ -65,16 +67,16 @@
:pages-index {}}) :pages-index {}})
(defn make-file-data (defn make-file-data
([file-id components-v2] ([file-id]
(make-file-data file-id (uuid/next) components-v2)) (make-file-data file-id (uuid/next)))
([file-id page-id components-v2] ([file-id page-id]
(let [page (ctp/make-empty-page page-id "Page-1")] (let [page (ctp/make-empty-page page-id "Page-1")]
(cond-> (-> empty-file-data (cond-> (-> empty-file-data
(assoc :id file-id) (assoc :id file-id)
(ctpl/add-page page)) (ctpl/add-page page))
components-v2 (contains? ffeat/*current* "components/v2")
(assoc-in [:options :components-v2] true))))) (assoc-in [:options :components-v2] true)))))
;; Helpers ;; Helpers
@ -108,7 +110,7 @@
([libraries component-id] ([libraries component-id]
(some #(ctkl/get-component (:data %) component-id) (vals libraries))) (some #(ctkl/get-component (:data %) component-id) (vals libraries)))
([libraries library-id component-id] ([libraries library-id component-id]
(ctkl/get-component (get-in libraries [library-id :data]) component-id))) (ctkl/get-component (dm/get-in libraries [library-id :data]) component-id)))
(defn delete-component (defn delete-component
"Delete a component and store it to be able to be recovered later. "Delete a component and store it to be able to be recovered later.
@ -118,7 +120,7 @@
(delete-component file-data component-id false)) (delete-component file-data component-id false))
([file-data component-id skip-undelete?] ([file-data component-id skip-undelete?]
(let [components-v2 (get-in file-data [:options :components-v2]) (let [components-v2 (dm/get-in file-data [:options :components-v2])
add-to-deleted-components add-to-deleted-components
(fn [file-data] (fn [file-data]
@ -144,12 +146,12 @@
(defn get-deleted-component (defn get-deleted-component
"Retrieve a component that has been deleted but still is in the safe store." "Retrieve a component that has been deleted but still is in the safe store."
[file-data component-id] [file-data component-id]
(get-in file-data [:deleted-components component-id])) (dm/get-in file-data [:deleted-components component-id]))
(defn restore-component (defn restore-component
"Recover a deleted component and put it again in place." "Recover a deleted component and put it again in place."
[file-data component-id] [file-data component-id]
(let [component (-> (get-in file-data [:deleted-components component-id]) (let [component (-> (dm/get-in file-data [:deleted-components component-id])
(dissoc :main-instance-x :main-instance-y))] (dissoc :main-instance-x :main-instance-y))]
(cond-> file-data (cond-> file-data
(some? component) (some? component)
@ -242,7 +244,7 @@
[file-data] [file-data]
(let [components (ctkl/components-seq file-data)] (let [components (ctkl/components-seq file-data)]
(if (or (empty? components) (if (or (empty? components)
(get-in file-data [:options :components-v2])) (dm/get-in file-data [:options :components-v2]))
(assoc-in file-data [:options :components-v2] true) (assoc-in file-data [:options :components-v2] true)
(let [grid-gap 50 (let [grid-gap 50
@ -342,12 +344,12 @@
copy-component copy-component
(fn [file-data] (fn [file-data]
(ctkl/add-component file-data (ctkl/add-component file-data
(:id component) {:id (:id component)
(:name component) :name (:name component)
(:path component) :path (:path component)
(:id main-instance-shape) :main-instance-id (:id main-instance-shape)
page-id :main-instance-page page-id
(vals (:objects component)))) :shapes (vals (:objects component))}))
; Change all existing instances to point to the local file ; Change all existing instances to point to the local file
remap-instances remap-instances
@ -500,10 +502,10 @@
component-file (when component-file-id (get libraries component-file-id nil)) component-file (when component-file-id (get libraries component-file-id nil))
component (when component-id component (when component-id
(if component-file (if component-file
(get-in component-file [:data :components component-id]) (dm/get-in component-file [:data :components component-id])
(get components component-id))) (get components component-id)))
component-shape (when (and component (:shape-ref shape)) component-shape (when (and component (:shape-ref shape))
(get-in component [:objects (:shape-ref shape)]))] (dm/get-in component [:objects (:shape-ref shape)]))]
(str/format " %s--> %s%s%s" (str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#" (cond (:component-root? shape) "#"
(:component-id shape) "@" (:component-id shape) "@"
@ -518,7 +520,7 @@
component-file-id (:component-file shape) component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil)) component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file component (if component-file
(get-in component-file [:data :components component-id]) (dm/get-in component-file [:data :components component-id])
(get components component-id))] (get components component-id))]
(str/format " (%s%s)" (str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file))) (when component-file (str/format "<%s> " (:name component-file)))

View file

@ -7,6 +7,7 @@
(ns app.common.types.page (ns app.common.types.page
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.files.features :as ffeat]
[app.common.types.page.flow :as ctpf] [app.common.types.page.flow :as ctpf]
[app.common.types.page.grid :as ctpg] [app.common.types.page.grid :as ctpg]
[app.common.types.page.guide :as ctpu] [app.common.types.page.guide :as ctpu]
@ -48,9 +49,11 @@
(defn make-empty-page (defn make-empty-page
[id name] [id name]
(assoc empty-page-data (let [wrap-fn ffeat/*wrap-objects-fn*]
:id id (-> empty-page-data
:name name)) (assoc :id id)
(assoc :name name)
(update :objects wrap-fn))))
;; --- Helpers for flow ;; --- Helpers for flow

View file

@ -2,32 +2,29 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) UXBOX Labs SL ;; Copyright (c) KALEIDOS INC
(ns app.common.types.pages-list (ns app.common.types.pages-list
(:require (:require
[app.common.data :as d] [app.common.data.macros :as dm]
[app.common.pages.helpers :as cph])) [app.common.pages.helpers :as cph]))
(defn get-page (defn get-page
[file-data id] [file-data id]
(get-in file-data [:pages-index id])) (dm/get-in file-data [:pages-index id]))
(defn add-page (defn add-page
[file-data page] [file-data {:keys [id index] :as page}]
(let [index (:index page)
page (dissoc page :index)
; It's legitimate to add a page that is already there,
; for example in an idempotent changes operation.
add-if-not-exists (fn [pages id]
(cond
(d/seek #(= % id) pages) pages
(nil? index) (conj pages id)
:else (cph/insert-at-index pages index [id])))]
(-> file-data (-> file-data
(update :pages add-if-not-exists (:id page)) ;; It's legitimate to add a page that is already there, for
(update :pages-index assoc (:id page) page)))) ;; example in an idempotent changes operation.
(update :pages (fn [pages]
(let [exists? (some (partial = id) pages)]
(cond
exists? pages
(nil? index) (conj pages id)
:else (cph/insert-at-index pages index [id])))))
(update :pages-index assoc id (dissoc page :index))))
(defn pages-seq (defn pages-seq
[file-data] [file-data]
@ -42,4 +39,3 @@
(-> file-data (-> file-data
(update :pages (fn [pages] (filterv #(not= % page-id) pages))) (update :pages (fn [pages] (filterv #(not= % page-id) pages)))
(update :pages-index dissoc page-id))) (update :pages-index dissoc page-id)))

View file

@ -6,16 +6,22 @@
(ns app.common.pages-test (ns app.common.pages-test
(:require (:require
[clojure.test :as t] [app.common.files.features :as ffeat]
[clojure.pprint :refer [pprint]]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]
[clojure.pprint :refer [pprint]]
[clojure.test :as t]))
(defn- make-file-data
[file-id page-id]
(binding [ffeat/*current* #{"components/v2"}]
(ctf/make-file-data file-id page-id)))
(t/deftest process-change-set-option (t/deftest process-change-set-option
(let [file-id (uuid/custom 2 2) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
data (ctf/make-file-data file-id page-id true)] data (make-file-data file-id page-id)]
(t/testing "Sets option single" (t/testing "Sets option single"
(let [chg {:type :set-option (let [chg {:type :set-option
:page-id page-id :page-id page-id
@ -81,7 +87,7 @@
(t/deftest process-change-add-obj (t/deftest process-change-add-obj
(let [file-id (uuid/custom 2 2) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
data (ctf/make-file-data file-id page-id true) data (make-file-data file-id page-id)
id-a (uuid/custom 2 1) id-a (uuid/custom 2 1)
id-b (uuid/custom 2 2) id-b (uuid/custom 2 2)
id-c (uuid/custom 2 3)] id-c (uuid/custom 2 3)]
@ -135,7 +141,7 @@
(t/deftest process-change-mod-obj (t/deftest process-change-mod-obj
(let [file-id (uuid/custom 2 2) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
data (ctf/make-file-data file-id page-id true)] data (make-file-data file-id page-id)]
(t/testing "simple mod-obj" (t/testing "simple mod-obj"
(let [chg {:type :mod-obj (let [chg {:type :mod-obj
:page-id page-id :page-id page-id
@ -162,7 +168,7 @@
(let [file-id (uuid/custom 2 2) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
id (uuid/custom 2 1) id (uuid/custom 2 1)
data (ctf/make-file-data file-id page-id true) data (make-file-data file-id page-id)
data (-> data data (-> data
(assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id]) (assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id])
(assoc-in [:pages-index page-id :objects id] (assoc-in [:pages-index page-id :objects id]
@ -206,7 +212,7 @@
file-id (uuid/custom 2 2) file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
data (ctf/make-file-data file-id page-id true) data (make-file-data file-id page-id)
data (update-in data [:pages-index page-id :objects] data (update-in data [:pages-index page-id :objects]
#(-> % #(-> %
@ -450,7 +456,7 @@
:obj {:type :rect :obj {:type :rect
:name "Shape 3"}} :name "Shape 3"}}
] ]
data (ctf/make-file-data file-id page-id true) data (make-file-data file-id page-id)
data (cp/process-changes data changes)] data (cp/process-changes data changes)]
(t/testing "preserve order on multiple shape mov 1" (t/testing "preserve order on multiple shape mov 1"
@ -557,7 +563,7 @@
:parent-id group-1-id :parent-id group-1-id
:shapes [shape-1-id shape-2-id]}] :shapes [shape-1-id shape-2-id]}]
data (ctf/make-file-data file-id page-id true) data (make-file-data file-id page-id)
data (cp/process-changes data changes)] data (cp/process-changes data changes)]
(t/testing "case 1" (t/testing "case 1"

View file

@ -6,9 +6,10 @@
(ns app.common.test-helpers.files (ns app.common.test-helpers.files
(:require (:require
[app.common.files.features :as ffeat]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.types.components-list :as ctkl]
[app.common.types.colors-list :as ctcl] [app.common.types.colors-list :as ctcl]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl] [app.common.types.pages-list :as ctpl]
@ -17,6 +18,11 @@
[app.common.types.typographies-list :as ctyl] [app.common.types.typographies-list :as ctyl]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
(defn- make-file-data
[file-id page-id]
(binding [ffeat/*current* #{"components/v2"}]
(ctf/make-file-data file-id page-id)))
(def ^:private idmap (atom {})) (def ^:private idmap (atom {}))
(defn reset-idmap! (defn reset-idmap!
@ -33,7 +39,7 @@
([file-id page-id props] ([file-id page-id props]
(merge {:id file-id (merge {:id file-id
:name (get props :name "File1") :name (get props :name "File1")
:data (ctf/make-file-data file-id page-id true)} :data (make-file-data file-id page-id)}
props))) props)))
(defn sample-shape (defn sample-shape
@ -81,12 +87,12 @@
#(reduce (fn [page shape] (ctst/set-shape page shape)) #(reduce (fn [page shape] (ctst/set-shape page shape))
% %
updated-shapes)) updated-shapes))
(ctkl/add-component (:id component-shape) (ctkl/add-component {:id (:id component-shape)
(:name component-shape) :name (:name component-shape)
"" :path ""
shape-id :main-instance-id shape-id
page-id :main-instance-page page-id
component-shapes)))))) :shapes component-shapes}))))))
(defn sample-instance (defn sample-instance
[file label page-id library component-id] [file label page-id library component-id]

View file

@ -765,9 +765,14 @@
(let [{:keys [on-success on-error] (let [{:keys [on-success on-error]
:or {on-success identity :or {on-success identity
on-error rx/throw}} (meta params) on-error rx/throw}} (meta params)
name (name (gensym (str (tr "dashboard.new-file-prefix") " "))) name (name (gensym (str (tr "dashboard.new-file-prefix") " ")))
components-v2 (features/active-feature? state :components-v2) features (cond-> #{}
params (assoc params :name name :components-v2 components-v2)] (features/active-feature? state :components-v2)
(conj "components/v2"))
params (-> params
(assoc :name name)
(assoc :features features))]
(->> (rp/mutation! :create-file params) (->> (rp/mutation! :create-file params)
(rx/tap on-success) (rx/tap on-success)

View file

@ -7,6 +7,7 @@
(ns app.main.data.workspace.persistence (ns app.main.data.workspace.persistence
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.logging :as log] [app.common.logging :as log]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.changes-spec :as pcs] [app.common.pages.changes-spec :as pcs]
@ -137,14 +138,16 @@
(ptk/reify ::persist-changes (ptk/reify ::persist-changes
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [components-v2 (features/active-feature? state :components-v2) (let [features (cond-> #{}
(features/active-feature? state :components-v2)
(conj "components/v2"))
sid (:session-id state) sid (:session-id state)
file (get state :workspace-file) file (get state :workspace-file)
params {:id (:id file) params {:id (:id file)
:revn (:revn file) :revn (:revn file)
:session-id sid :session-id sid
:changes-with-metadata (into [] changes) :changes-with-metadata (into [] changes)
:components-v2 components-v2}] :features features}]
(when (= file-id (:id params)) (when (= file-id (:id params))
(->> (rp/mutation :update-file params) (->> (rp/mutation :update-file params)
@ -180,15 +183,17 @@
(ptk/reify ::persist-synchronous-changes (ptk/reify ::persist-synchronous-changes
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [components-v2 (features/active-feature? state :components-v2) (let [features (cond-> #{}
(features/active-feature? state :components-v2)
(conj "components/v2"))
sid (:session-id state) sid (:session-id state)
file (get-in state [:workspace-libraries file-id]) file (dm/get-in state [:workspace-libraries file-id])
params {:id (:id file) params {:id (:id file)
:revn (:revn file) :revn (:revn file)
:session-id sid :session-id sid
:changes changes :changes changes
:components-v2 components-v2}] :features features}]
(when (:id params) (when (:id params)
(->> (rp/mutation :update-file params) (->> (rp/mutation :update-file params)
@ -220,6 +225,9 @@
(ptk/reify ::changes-persisted (ptk/reify ::changes-persisted
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
;; NOTE: we don't set the file features context here because
;; there are no useful context for code that need to be executed
;; on the frontend side
(let [changes (group-by :page-id changes)] (let [changes (group-by :page-id changes)]
(if (= file-id (:current-file-id state)) (if (= file-id (:current-file-id state))
(-> state (-> state
@ -238,7 +246,6 @@
(update-in [:workspace-libraries file-id :data] (update-in [:workspace-libraries file-id :data]
cp/process-changes changes))))))) cp/process-changes changes)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Fetching & Uploading ;; Data Fetching & Uploading
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -278,8 +285,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [share-id (-> state :viewer-local :share-id) (let [share-id (-> state :viewer-local :share-id)
components-v2 (features/active-feature? state :components-v2)] features (cond-> #{}
(->> (rx/zip (rp/query! :file-raw {:id file-id :components-v2 components-v2}) (features/active-feature? state :components-v2)
(conj "components/v2"))]
(->> (rx/zip (rp/query! :file-raw {:id file-id :features features})
(rp/query! :team-users {:file-id file-id}) (rp/query! :team-users {:file-id file-id})
(rp/query! :project {:id project-id}) (rp/query! :project {:id project-id})
(rp/query! :file-libraries {:file-id file-id}) (rp/query! :file-libraries {:file-id file-id})

View file

@ -72,4 +72,3 @@
(doseq [f features-list] (doseq [f features-list]
(when (not= f :components-v2) (when (not= f :components-v2)
(toggle-feature! f))))) (toggle-feature! f)))))