mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 16:31:39 +02:00
Merge remote-tracking branch 'origin/staging' into main
This commit is contained in:
commit
ee6350189f
198 changed files with 12712 additions and 5323 deletions
|
@ -22,7 +22,7 @@
|
|||
<mj-text font-size="24px" font-weight="600">Hello {{name}}!</mj-text>
|
||||
<mj-text>
|
||||
Thanks for signing up for your Penpot account! Please verify your
|
||||
email using the link below adn get started building mockups and
|
||||
email using the link below and get started building mockups and
|
||||
prototypes today!
|
||||
</mj-text>
|
||||
<mj-button href="{{ public-uri }}/#/auth/verify-token?token={{token}}">
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">Thanks for signing up for your Penpot account! Please verify your email using the link below adn get started building mockups and prototypes today!</div>
|
||||
<div style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">Thanks for signing up for your Penpot account! Please verify your email using the link below and get started building mockups and prototypes today!</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -465,4 +465,4 @@
|
|||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Hello {{name}}!
|
||||
|
||||
Thanks for signing up for your Penpot account! Please verify your email using the
|
||||
link below adn get started building mockups and prototypes today!
|
||||
link below and get started building mockups and prototypes today!
|
||||
|
||||
{{ public-uri }}/#/auth/verify-token?token={{token}}
|
||||
|
||||
|
|
|
@ -231,9 +231,9 @@
|
|||
(defn get-by-params
|
||||
([ds table params]
|
||||
(get-by-params ds table params nil))
|
||||
([ds table params {:keys [uncheked] :or {uncheked false} :as opts}]
|
||||
([ds table params {:keys [check-not-found] :or {check-not-found true} :as opts}]
|
||||
(let [res (exec-one! ds (sql/select table params opts))]
|
||||
(when (and (not uncheked) (or (not res) (is-deleted? res)))
|
||||
(when (and check-not-found (or (not res) (is-deleted? res)))
|
||||
(ex/raise :type :not-found
|
||||
:table table
|
||||
:hint "database object not found"))
|
||||
|
@ -267,13 +267,28 @@
|
|||
(instance? PGpoint v))
|
||||
|
||||
(defn pgarray?
|
||||
[v]
|
||||
(instance? PgArray v))
|
||||
([v] (instance? PgArray v))
|
||||
([v type]
|
||||
(and (instance? PgArray v)
|
||||
(= type (.getBaseTypeName ^PgArray v)))))
|
||||
|
||||
(defn pgarray-of-uuid?
|
||||
[v]
|
||||
(and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v))))
|
||||
|
||||
(defn decode-pgarray
|
||||
([v] (into [] (.getArray ^PgArray v)))
|
||||
([v in] (into in (.getArray ^PgArray v)))
|
||||
([v in xf] (into in xf (.getArray ^PgArray v))))
|
||||
|
||||
(defn pgarray->set
|
||||
[v]
|
||||
(set (.getArray ^PgArray v)))
|
||||
|
||||
(defn pgarray->vector
|
||||
[v]
|
||||
(vec (.getArray ^PgArray v)))
|
||||
|
||||
(defn pgpoint
|
||||
[p]
|
||||
(PGpoint. (:x p) (:y p)))
|
||||
|
@ -285,7 +300,6 @@
|
|||
(.createArrayOf conn ^String type (into-array Object objects))
|
||||
(.createArrayOf conn ^String type objects))))
|
||||
|
||||
|
||||
(defn decode-pgpoint
|
||||
[^PGpoint v]
|
||||
(gpt/point (.-x v) (.-y v)))
|
||||
|
@ -369,15 +383,6 @@
|
|||
(.setType "jsonb")
|
||||
(.setValue (json/encode-str data))))
|
||||
|
||||
(defn pgarray->set
|
||||
[v]
|
||||
(set (.getArray ^PgArray v)))
|
||||
|
||||
(defn pgarray->vector
|
||||
[v]
|
||||
(vec (.getArray ^PgArray v)))
|
||||
|
||||
|
||||
;; --- Locks
|
||||
|
||||
(defn- xact-check-param
|
||||
|
|
|
@ -114,9 +114,14 @@
|
|||
(s/def ::storage map?)
|
||||
(s/def ::assets map?)
|
||||
(s/def ::feedback fn?)
|
||||
(s/def ::error-report-handler fn?)
|
||||
(s/def ::audit-http-handler fn?)
|
||||
|
||||
(defmethod ig/pre-init-spec ::router [_]
|
||||
(s/keys :req-un [::rpc ::session ::mtx/metrics ::oauth ::storage ::assets ::feedback]))
|
||||
(s/keys :req-un [::rpc ::session ::mtx/metrics
|
||||
::oauth ::storage ::assets ::feedback
|
||||
::error-report-handler
|
||||
::audit-http-handler]))
|
||||
|
||||
(defmethod ig/init-key ::router
|
||||
[_ {:keys [session rpc oauth metrics assets feedback] :as cfg}]
|
||||
|
@ -136,9 +141,7 @@
|
|||
["/webhooks"
|
||||
["/sns" {:post (:sns-webhook cfg)}]]
|
||||
|
||||
["/api" {:middleware [
|
||||
;; Temporary disabled
|
||||
#_[middleware/etag]
|
||||
["/api" {:middleware [[middleware/etag]
|
||||
[middleware/format-response-body]
|
||||
[middleware/params]
|
||||
[middleware/multipart-params]
|
||||
|
@ -149,10 +152,12 @@
|
|||
|
||||
["/feedback" {:middleware [(:middleware session)]
|
||||
:post feedback}]
|
||||
|
||||
["/auth/oauth/:provider" {:post (:handler oauth)}]
|
||||
["/auth/oauth/:provider/callback" {:get (:callback-handler oauth)}]
|
||||
|
||||
["/audit/events" {:middleware [(:middleware session)]
|
||||
:post (:audit-http-handler cfg)}]
|
||||
|
||||
["/rpc" {:middleware [(:middleware session)]}
|
||||
["/query/:type" {:get (:query-handler rpc)
|
||||
:post (:query-handler rpc)}]
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
[buddy.core.codecs :as bc]
|
||||
[buddy.core.hash :as bh]
|
||||
[clojure.java.io :as io]
|
||||
[ring.core.protocols :as rp]
|
||||
[ring.middleware.cookies :refer [wrap-cookies]]
|
||||
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
|
||||
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
||||
|
@ -74,33 +73,15 @@
|
|||
{:name ::parse-request-body
|
||||
:compile (constantly wrap-parse-request-body)})
|
||||
|
||||
(defn- transit-streamable-body
|
||||
[data opts]
|
||||
(reify rp/StreamableResponseBody
|
||||
(write-body-to-stream [_ response output-stream]
|
||||
(try
|
||||
(let [tw (t/writer output-stream opts)]
|
||||
(t/write! tw data))
|
||||
(finally
|
||||
(.close ^java.io.OutputStream output-stream))))))
|
||||
|
||||
(defn- impl-format-response-body
|
||||
[response _request]
|
||||
(let [body (:body response)
|
||||
opts {:type :json-verbose}]
|
||||
opts {:type :json}]
|
||||
(cond
|
||||
(coll? body)
|
||||
(-> response
|
||||
(update :headers assoc "content-type" "application/transit+json")
|
||||
(assoc :body (transit-streamable-body body opts)))
|
||||
|
||||
;; ;; Temporary disabled
|
||||
;; (-> response
|
||||
;; (update :headers assoc "content-type" "application/transit+json")
|
||||
;; (assoc :body
|
||||
;; (if (= :post (:request-method request))
|
||||
;; (transit-streamable-body body opts)
|
||||
;; (t/encode body opts))))
|
||||
(assoc :body (t/encode body opts)))
|
||||
|
||||
(nil? body)
|
||||
(assoc response :status 204 :body "")
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[integrant.core :as ig]
|
||||
[lambdaisland.uri :as u]))
|
||||
[lambdaisland.uri :as u]
|
||||
[promesa.exec :as px]))
|
||||
|
||||
(defn parse-client-ip
|
||||
[{:keys [headers] :as request}]
|
||||
|
@ -67,6 +68,65 @@
|
|||
|
||||
(update event :props #(-> % clean-common clean-profile-id clean-complex-data))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HTTP Handler
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare persist-http-events)
|
||||
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::type ::us/string)
|
||||
(s/def ::props (s/map-of ::us/keyword any?))
|
||||
(s/def ::timestamp dt/instant?)
|
||||
(s/def ::context (s/map-of ::us/keyword any?))
|
||||
|
||||
(s/def ::event
|
||||
(s/keys :req-un [::type ::name ::props ::timestamp ::profile-id]
|
||||
:opt-un [::context]))
|
||||
|
||||
(s/def ::events (s/every ::event))
|
||||
|
||||
(defmethod ig/init-key ::http-handler
|
||||
[_ {:keys [executor enabled] :as cfg}]
|
||||
(fn [{:keys [params _headers _cookies profile-id] :as request}]
|
||||
(when enabled
|
||||
(let [events (->> (:events params)
|
||||
(remove #(not= profile-id (:profile-id %)))
|
||||
(us/conform ::events))
|
||||
ip-addr (parse-client-ip request)
|
||||
cfg (-> cfg
|
||||
(assoc :source "frontend")
|
||||
(assoc :events events)
|
||||
(assoc :ip-addr ip-addr))]
|
||||
(px/run! executor #(persist-http-events cfg))))
|
||||
{:status 204 :body ""}))
|
||||
|
||||
(defn- persist-http-events
|
||||
[{:keys [pool events ip-addr source] :as cfg}]
|
||||
(try
|
||||
(let [columns [:id :name :source :type :tracked-at :profile-id :ip-addr :props :context]
|
||||
prepare-xf (map (fn [event]
|
||||
[(uuid/next)
|
||||
(:name event)
|
||||
source
|
||||
(:type event)
|
||||
(:timestamp event)
|
||||
(:profile-id event)
|
||||
(db/inet ip-addr)
|
||||
(db/tjson (:props event))
|
||||
(db/tjson (d/without-nils (:context event)))]))
|
||||
events (us/conform ::events events)
|
||||
rows (into [] prepare-xf events)]
|
||||
(db/insert-multi! pool :audit-log columns rows))
|
||||
(catch Throwable e
|
||||
(let [xdata (ex-data e)]
|
||||
(if (= :spec-validation (:code xdata))
|
||||
(l/error ::l/raw (str "spec validation on persist-events:\n"
|
||||
(:explain xdata)))
|
||||
(l/error :hint "error on persist-events"
|
||||
:cause e))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Collector
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -103,7 +163,9 @@
|
|||
(recur)))
|
||||
|
||||
(fn [& {:keys [cmd] :as params}]
|
||||
(let [params (dissoc params :cmd)]
|
||||
(let [params (-> params
|
||||
(dissoc :cmd)
|
||||
(assoc :tracked-at (dt/now)))]
|
||||
(case cmd
|
||||
:stop (a/close! input)
|
||||
:submit (when-not (a/offer! input params)
|
||||
|
@ -117,13 +179,14 @@
|
|||
(:name event)
|
||||
(:type event)
|
||||
(:profile-id event)
|
||||
(:tracked-at event)
|
||||
(some-> (:ip-addr event) db/inet)
|
||||
(db/tjson (:props event))])]
|
||||
|
||||
(db/tjson (:props event))
|
||||
"backend"])]
|
||||
(aa/with-thread executor
|
||||
(db/with-atomic [conn pool]
|
||||
(db/insert-multi! conn :audit-log
|
||||
[:id :name :type :profile-id :ip-addr :props]
|
||||
[:id :name :type :profile-id :tracked-at :ip-addr :props :source]
|
||||
(sequence (map event->row) events))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -144,16 +207,22 @@
|
|||
|
||||
(defmethod ig/init-key ::archive-task
|
||||
[_ {:keys [uri enabled] :as cfg}]
|
||||
(fn [_]
|
||||
(when (and enabled (not uri))
|
||||
(ex/raise :type :internal
|
||||
:code :task-not-configured
|
||||
:hint "archive task not configured, missing uri"))
|
||||
(loop []
|
||||
(let [res (archive-events cfg)]
|
||||
(when (= res :continue)
|
||||
(aa/thread-sleep 200)
|
||||
(recur))))))
|
||||
(fn [props]
|
||||
;; NOTE: this let allows overwrite default configured values from
|
||||
;; the repl, when manually invoking the task.
|
||||
(let [enabled (or enabled (:enabled props false))
|
||||
uri (or uri (:uri props))
|
||||
cfg (assoc cfg :uri uri)]
|
||||
(when (and enabled (not uri))
|
||||
(ex/raise :type :internal
|
||||
:code :task-not-configured
|
||||
:hint "archive task not configured, missing uri"))
|
||||
(when enabled
|
||||
(loop []
|
||||
(let [res (archive-events cfg)]
|
||||
(when (= res :continue)
|
||||
(aa/thread-sleep 200)
|
||||
(recur))))))))
|
||||
|
||||
(def sql:retrieve-batch-of-audit-log
|
||||
"select * from audit_log
|
||||
|
@ -164,22 +233,27 @@
|
|||
|
||||
(defn archive-events
|
||||
[{:keys [pool uri tokens] :as cfg}]
|
||||
(letfn [(decode-row [{:keys [props ip-addr] :as row}]
|
||||
(letfn [(decode-row [{:keys [props ip-addr context] :as row}]
|
||||
(cond-> row
|
||||
(db/pgobject? props)
|
||||
(assoc :props (db/decode-transit-pgobject props))
|
||||
|
||||
(db/pgobject? context)
|
||||
(assoc :context (db/decode-transit-pgobject context))
|
||||
|
||||
(db/pgobject? ip-addr "inet")
|
||||
(assoc :ip-addr (db/decode-inet ip-addr))))
|
||||
|
||||
(row->event [{:keys [name type created-at profile-id props ip-addr]}]
|
||||
(cond-> {:type type
|
||||
:name name
|
||||
:timestamp created-at
|
||||
:profile-id profile-id
|
||||
:props props}
|
||||
(some? ip-addr)
|
||||
(update :context assoc :source-ip ip-addr)))
|
||||
(row->event [row]
|
||||
(select-keys row [:type
|
||||
:name
|
||||
:source
|
||||
:created-at
|
||||
:tracked-at
|
||||
:profile-id
|
||||
:ip-addr
|
||||
:props
|
||||
:context]))
|
||||
|
||||
(send [events]
|
||||
(let [token (tokens :generate {:iss "authentication"
|
||||
|
|
|
@ -28,11 +28,24 @@
|
|||
{:name "actions_profile_register_count"
|
||||
:help "A global counter of user registrations."
|
||||
:type :counter}
|
||||
|
||||
:profile-activation
|
||||
{:name "actions_profile_activation_count"
|
||||
:help "A global counter of profile activations"
|
||||
:type :counter}
|
||||
|
||||
:update-file-changes
|
||||
{:name "rpc_update_file_changes_total"
|
||||
:help "A total number of changes submitted to update-file."
|
||||
:type :counter}
|
||||
|
||||
:update-file-bytes-processed
|
||||
{:name "rpc_update_file_bytes_processed_total"
|
||||
:help "A total number of bytes processed by update-file."
|
||||
:type :counter}}}
|
||||
|
||||
|
||||
|
||||
:app.migrations/all
|
||||
{:main (ig/ref :app.migrations/migrations)}
|
||||
|
||||
|
@ -95,6 +108,7 @@
|
|||
:storage (ig/ref :app.storage/storage)
|
||||
:sns-webhook (ig/ref :app.http.awsns/handler)
|
||||
:feedback (ig/ref :app.http.feedback/handler)
|
||||
:audit-http-handler (ig/ref :app.loggers.audit/http-handler)
|
||||
:error-report-handler (ig/ref :app.loggers.mattermost/handler)}
|
||||
|
||||
:app.http.assets/handlers
|
||||
|
@ -289,6 +303,11 @@
|
|||
:app.loggers.zmq/receiver
|
||||
{:endpoint (cf/get :loggers-zmq-uri)}
|
||||
|
||||
:app.loggers.audit/http-handler
|
||||
{:enabled (cf/get :audit-enabled false)
|
||||
:pool (ig/ref :app.db/pool)
|
||||
:executor (ig/ref :app.worker/executor)}
|
||||
|
||||
:app.loggers.audit/collector
|
||||
{:enabled (cf/get :audit-enabled false)
|
||||
:pool (ig/ref :app.db/pool)
|
||||
|
|
|
@ -92,18 +92,14 @@
|
|||
_ (when (seq labels)
|
||||
(.labelNames instance (into-array String labels)))
|
||||
instance (.register instance registry)]
|
||||
(reify
|
||||
clojure.lang.IDeref
|
||||
(deref [_] instance)
|
||||
|
||||
clojure.lang.IFn
|
||||
(invoke [_ cmd]
|
||||
(.inc ^Counter instance))
|
||||
|
||||
(invoke [_ cmd labels]
|
||||
(.. ^Counter instance
|
||||
(labels (into-array String labels))
|
||||
(inc))))))
|
||||
{::instance instance
|
||||
::fn (fn [{:keys [by labels] :or {by 1}}]
|
||||
(if labels
|
||||
(.. ^Counter instance
|
||||
(labels (into-array String labels))
|
||||
(inc by))
|
||||
(.inc ^Counter instance by)))}))
|
||||
|
||||
(defn make-gauge
|
||||
[{:keys [name help registry reg labels] :as props}]
|
||||
|
@ -115,21 +111,16 @@
|
|||
(.labelNames instance (into-array String labels)))
|
||||
instance (.register instance registry)]
|
||||
|
||||
(reify
|
||||
clojure.lang.IDeref
|
||||
(deref [_] instance)
|
||||
|
||||
clojure.lang.IFn
|
||||
(invoke [_ cmd]
|
||||
(case cmd
|
||||
:inc (.inc ^Gauge instance)
|
||||
:dec (.dec ^Gauge instance)))
|
||||
|
||||
(invoke [_ cmd labels]
|
||||
(let [labels (into-array String [labels])]
|
||||
(case cmd
|
||||
:inc (.. ^Gauge instance (labels labels) (inc))
|
||||
:dec (.. ^Gauge instance (labels labels) (dec))))))))
|
||||
{::instance instance
|
||||
::fn (fn [{:keys [cmd by labels] :or {by 1}}]
|
||||
(if labels
|
||||
(let [labels (into-array String [labels])]
|
||||
(case cmd
|
||||
:inc (.. ^Gauge instance (labels labels) (inc by))
|
||||
:dec (.. ^Gauge instance (labels labels) (dec by))))
|
||||
(case cmd
|
||||
:inc (.inc ^Gauge instance by)
|
||||
:dec (.dec ^Gauge instance by))))}))
|
||||
|
||||
(def default-quantiles
|
||||
[[0.75 0.02]
|
||||
|
@ -150,18 +141,14 @@
|
|||
_ (when (seq labels)
|
||||
(.labelNames instance (into-array String labels)))
|
||||
instance (.register instance registry)]
|
||||
(reify
|
||||
clojure.lang.IDeref
|
||||
(deref [_] instance)
|
||||
|
||||
clojure.lang.IFn
|
||||
(invoke [_ cmd val]
|
||||
(.observe ^Summary instance val))
|
||||
|
||||
(invoke [_ cmd val labels]
|
||||
(.. ^Summary instance
|
||||
(labels (into-array String labels))
|
||||
(observe val))))))
|
||||
{::instance instance
|
||||
::fn (fn [{:keys [val labels]}]
|
||||
(if labels
|
||||
(.. ^Summary instance
|
||||
(labels (into-array String labels))
|
||||
(observe val))
|
||||
(.observe ^Summary instance val)))}))
|
||||
|
||||
(def default-histogram-buckets
|
||||
[1 5 10 25 50 75 100 250 500 750 1000 2500 5000 7500])
|
||||
|
@ -177,18 +164,14 @@
|
|||
_ (when (seq labels)
|
||||
(.labelNames instance (into-array String labels)))
|
||||
instance (.register instance registry)]
|
||||
(reify
|
||||
clojure.lang.IDeref
|
||||
(deref [_] instance)
|
||||
|
||||
clojure.lang.IFn
|
||||
(invoke [_ cmd val]
|
||||
(.observe ^Histogram instance val))
|
||||
|
||||
(invoke [_ cmd val labels]
|
||||
(.. ^Histogram instance
|
||||
(labels (into-array String labels))
|
||||
(observe val))))))
|
||||
{::instance instance
|
||||
::fn (fn [{:keys [val labels]}]
|
||||
(if labels
|
||||
(.. ^Histogram instance
|
||||
(labels (into-array String labels))
|
||||
(observe val))
|
||||
(.observe ^Histogram instance val)))}))
|
||||
|
||||
(defn create
|
||||
[{:keys [type] :as props}]
|
||||
|
@ -205,19 +188,19 @@
|
|||
(with-meta
|
||||
(fn
|
||||
([a]
|
||||
(mobj :inc)
|
||||
((::fn mobj) nil)
|
||||
(origf a))
|
||||
([a b]
|
||||
(mobj :inc)
|
||||
((::fn mobj) nil)
|
||||
(origf a b))
|
||||
([a b c]
|
||||
(mobj :inc)
|
||||
((::fn mobj) nil)
|
||||
(origf a b c))
|
||||
([a b c d]
|
||||
(mobj :inc)
|
||||
((::fn mobj) nil)
|
||||
(origf a b c d))
|
||||
([a b c d & more]
|
||||
(mobj :inc)
|
||||
((::fn mobj) nil)
|
||||
(apply origf a b c d more)))
|
||||
(assoc mdata ::original origf))))
|
||||
([rootf mobj labels]
|
||||
|
@ -226,13 +209,13 @@
|
|||
(with-meta
|
||||
(fn
|
||||
([a]
|
||||
(mobj :inc labels)
|
||||
((::fn mobj) {:labels labels})
|
||||
(origf a))
|
||||
([a b]
|
||||
(mobj :inc labels)
|
||||
((::fn mobj) {:labels labels})
|
||||
(origf a b))
|
||||
([a b & more]
|
||||
(mobj :inc labels)
|
||||
((::fn mobj) {:labels labels})
|
||||
(apply origf a b more)))
|
||||
(assoc mdata ::original origf)))))
|
||||
|
||||
|
@ -245,15 +228,15 @@
|
|||
([a]
|
||||
(with-measure
|
||||
:expr (origf a)
|
||||
:cb #(mobj :observe %)))
|
||||
:cb #((::fn mobj) {:val %})))
|
||||
([a b]
|
||||
(with-measure
|
||||
:expr (origf a b)
|
||||
:cb #(mobj :observe %)))
|
||||
:cb #((::fn mobj) {:val %})))
|
||||
([a b & more]
|
||||
(with-measure
|
||||
:expr (apply origf a b more)
|
||||
:cb #(mobj :observe %))))
|
||||
:cb #((::fn mobj) {:val %}))))
|
||||
(assoc mdata ::original origf))))
|
||||
|
||||
([rootf mobj labels]
|
||||
|
@ -264,26 +247,26 @@
|
|||
([a]
|
||||
(with-measure
|
||||
:expr (origf a)
|
||||
:cb #(mobj :observe % labels)))
|
||||
:cb #((::fn mobj) {:val % :labels labels})))
|
||||
([a b]
|
||||
(with-measure
|
||||
:expr (origf a b)
|
||||
:cb #(mobj :observe % labels)))
|
||||
:cb #((::fn mobj) {:val % :labels labels})))
|
||||
([a b & more]
|
||||
(with-measure
|
||||
:expr (apply origf a b more)
|
||||
:cb #(mobj :observe % labels))))
|
||||
:cb #((::fn mobj) {:val % :labels labels}))))
|
||||
(assoc mdata ::original origf)))))
|
||||
|
||||
(defn instrument-vars!
|
||||
[vars {:keys [wrap] :as props}]
|
||||
(let [obj (create props)]
|
||||
(cond
|
||||
(instance? Counter @obj)
|
||||
(instance? Counter (::instance obj))
|
||||
(doseq [var vars]
|
||||
(alter-var-root var (or wrap wrap-counter) obj))
|
||||
|
||||
(instance? Summary @obj)
|
||||
(instance? Summary (::instance obj))
|
||||
(doseq [var vars]
|
||||
(alter-var-root var (or wrap wrap-summary) obj))
|
||||
|
||||
|
@ -294,13 +277,13 @@
|
|||
[f {:keys [wrap] :as props}]
|
||||
(let [obj (create props)]
|
||||
(cond
|
||||
(instance? Counter @obj)
|
||||
(instance? Counter (::instance obj))
|
||||
((or wrap wrap-counter) f obj)
|
||||
|
||||
(instance? Summary @obj)
|
||||
(instance? Summary (::instance obj))
|
||||
((or wrap wrap-summary) f obj)
|
||||
|
||||
(instance? Histogram @obj)
|
||||
(instance? Histogram (::instance obj))
|
||||
((or wrap wrap-summary) f obj)
|
||||
|
||||
:else
|
||||
|
|
|
@ -193,6 +193,15 @@
|
|||
|
||||
{:name "0061-mod-file-table"
|
||||
:fn (mg/resource "app/migrations/sql/0061-mod-file-table.sql")}
|
||||
|
||||
{:name "0062-fix-metadata-media"
|
||||
:fn (mg/resource "app/migrations/sql/0062-fix-metadata-media.sql")}
|
||||
|
||||
{:name "0063-add-share-link-table"
|
||||
:fn (mg/resource "app/migrations/sql/0063-add-share-link-table.sql")}
|
||||
|
||||
{:name "0064-mod-audit-log-table"
|
||||
:fn (mg/resource "app/migrations/sql/0064-mod-audit-log-table.sql")}
|
||||
])
|
||||
|
||||
|
||||
|
|
12
backend/src/app/migrations/sql/0063-add-share-link-table.sql
Normal file
12
backend/src/app/migrations/sql/0063-add-share-link-table.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE share_link (
|
||||
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
file_id uuid NOT NULL REFERENCES file(id) ON DELETE CASCADE DEFERRABLE,
|
||||
owner_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL DEFERRABLE,
|
||||
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
||||
|
||||
pages uuid[],
|
||||
flags text[]
|
||||
);
|
||||
|
||||
CREATE INDEX share_link_file_id_idx ON share_link(file_id);
|
||||
CREATE INDEX share_link_owner_id_idx ON share_link(owner_id);
|
13
backend/src/app/migrations/sql/0064-mod-audit-log-table.sql
Normal file
13
backend/src/app/migrations/sql/0064-mod-audit-log-table.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
ALTER TABLE audit_log
|
||||
ADD COLUMN tracked_at timestamptz NULL DEFAULT clock_timestamp(),
|
||||
ADD COLUMN source text NULL,
|
||||
ADD COLUMN context jsonb NULL;
|
||||
|
||||
ALTER TABLE audit_log
|
||||
ALTER COLUMN source SET STORAGE external,
|
||||
ALTER COLUMN context SET STORAGE external;
|
||||
|
||||
UPDATE audit_log SET source = 'backend', tracked_at=created_at;
|
||||
|
||||
-- ALTER TABLE audit_log ALTER COLUMN source SET NOT NULL;
|
||||
-- ALTER TABLE audit_log ALTER COLUMN tracked_at SET NOT NULL;
|
|
@ -117,14 +117,14 @@
|
|||
profile-id (or (:profile-id params')
|
||||
(:profile-id result)
|
||||
(::audit/profile-id resultm))
|
||||
props (d/merge params (::audit/props resultm))]
|
||||
props (d/merge params' (::audit/props resultm))]
|
||||
(audit :cmd :submit
|
||||
:type (::type cfg)
|
||||
:name (or (::audit/name resultm)
|
||||
(::sv/name mdata))
|
||||
:profile-id profile-id
|
||||
:ip-addr (audit/parse-client-ip request)
|
||||
:props (audit/profile->props props))))
|
||||
:props props)))
|
||||
|
||||
result))))
|
||||
|
||||
|
@ -175,6 +175,7 @@
|
|||
'app.rpc.mutations.management
|
||||
'app.rpc.mutations.ldap
|
||||
'app.rpc.mutations.fonts
|
||||
'app.rpc.mutations.share-link
|
||||
'app.rpc.mutations.verify-token)
|
||||
(map (partial process-method cfg))
|
||||
(into {}))))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.metrics :as mtx]
|
||||
[app.rpc.permissions :as perms]
|
||||
[app.rpc.queries.files :as files]
|
||||
[app.rpc.queries.projects :as proj]
|
||||
|
@ -291,7 +292,7 @@
|
|||
(simpl/del-object backend file)))
|
||||
|
||||
(defn- update-file
|
||||
[{:keys [conn] :as cfg} {:keys [file changes changes-with-metadata session-id profile-id] :as params}]
|
||||
[{:keys [conn metrics] :as cfg} {:keys [file changes changes-with-metadata session-id profile-id] :as params}]
|
||||
(when (> (:revn params)
|
||||
(:revn file))
|
||||
|
||||
|
@ -301,14 +302,22 @@
|
|||
:context {:incoming-revn (:revn params)
|
||||
:stored-revn (:revn file)}))
|
||||
|
||||
(let [changes (if changes-with-metadata
|
||||
(let [mtx1 (get-in metrics [:definitions :update-file-changes])
|
||||
mtx2 (get-in metrics [:definitions :update-file-bytes-processed])
|
||||
|
||||
changes (if changes-with-metadata
|
||||
(mapcat :changes changes-with-metadata)
|
||||
changes)
|
||||
|
||||
;; Trace the number of changes processed
|
||||
_ ((::mtx/fn mtx1) {:by (count changes)})
|
||||
|
||||
ts (dt/now)
|
||||
file (-> (files/retrieve-data cfg file)
|
||||
(update :revn inc)
|
||||
(update :data (fn [data]
|
||||
;; Trace the length of bytes of processed data
|
||||
((::mtx/fn mtx2) {:by (alength data)})
|
||||
(-> data
|
||||
(blob/decode)
|
||||
(assoc :id (:id file))
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.http.oauth :refer [extract-props]]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.media :as media]
|
||||
[app.metrics :as mtx]
|
||||
[app.rpc.mutations.projects :as projects]
|
||||
[app.rpc.mutations.teams :as teams]
|
||||
[app.rpc.queries.profile :as profile]
|
||||
|
@ -150,7 +151,8 @@
|
|||
transaction is completed."
|
||||
[metrics]
|
||||
(fn []
|
||||
((get-in metrics [:definitions :profile-register]) :inc)))
|
||||
(let [mobj (get-in metrics [:definitions :profile-register])]
|
||||
((::mtx/fn mobj) {:by 1}))))
|
||||
|
||||
(defn register-profile
|
||||
[{:keys [conn tokens session metrics] :as cfg} {:keys [token] :as params}]
|
||||
|
|
67
backend/src/app/rpc/mutations/share_link.clj
Normal file
67
backend/src/app/rpc/mutations/share_link.clj
Normal file
|
@ -0,0 +1,67 @@
|
|||
;; 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) UXBOX Labs SL
|
||||
|
||||
(ns app.rpc.mutations.share-link
|
||||
"Share link related rpc mutation methods."
|
||||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.rpc.queries.files :as files]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
;; --- Helpers & Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::flags (s/every ::us/string :kind set?))
|
||||
(s/def ::pages (s/every ::us/uuid :kind set?))
|
||||
|
||||
;; --- Mutation: Create Share Link
|
||||
|
||||
(declare create-share-link)
|
||||
|
||||
(s/def ::create-share-link
|
||||
(s/keys :req-un [::profile-id ::file-id ::flags]
|
||||
:opt-un [::pages]))
|
||||
|
||||
(sv/defmethod ::create-share-link
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(files/check-edition-permissions! conn profile-id file-id)
|
||||
(create-share-link conn params)))
|
||||
|
||||
(defn create-share-link
|
||||
[conn {:keys [profile-id file-id pages flags]}]
|
||||
(let [pages (db/create-array conn "uuid" pages)
|
||||
flags (->> (map name flags)
|
||||
(db/create-array conn "text"))
|
||||
slink (db/insert! conn :share-link
|
||||
{:id (uuid/next)
|
||||
:file-id file-id
|
||||
:flags flags
|
||||
:pages pages
|
||||
:owner-id profile-id})]
|
||||
(-> slink
|
||||
(update :pages db/decode-pgarray #{})
|
||||
(update :flags db/decode-pgarray #{}))))
|
||||
|
||||
;; --- Mutation: Delete Share Link
|
||||
|
||||
(declare delete-share-link)
|
||||
|
||||
(s/def ::delete-share-link
|
||||
(s/keys :req-un [::profile-id ::id]))
|
||||
|
||||
(sv/defmethod ::delete-share-link
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [slink (db/get-by-id conn :share-link id)]
|
||||
(files/check-edition-permissions! conn profile-id (:file-id slink))
|
||||
(db/delete! conn :share-link {:id id})
|
||||
nil)))
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.exceptions :as ex]
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.metrics :as mtx]
|
||||
[app.rpc.mutations.teams :as teams]
|
||||
[app.rpc.queries.profile :as profile]
|
||||
[app.util.services :as sv]
|
||||
|
@ -42,7 +43,8 @@
|
|||
transaction is completed."
|
||||
[metrics]
|
||||
(fn []
|
||||
((get-in metrics [:definitions :profile-activation]) :inc)))
|
||||
(let [mobj (get-in metrics [:definitions :profile-activation])]
|
||||
((::mtx/fn mobj) {:by 1}))))
|
||||
|
||||
(defmethod process-token :verify-email
|
||||
[{:keys [conn session metrics] :as cfg} _ {:keys [profile-id] :as claims}]
|
||||
|
|
|
@ -37,6 +37,41 @@
|
|||
:is-admin false
|
||||
:can-edit false)))
|
||||
|
||||
(defn make-edition-predicate-fn
|
||||
"A simple factory for edition permission predicate functions."
|
||||
[qfn]
|
||||
(us/assert fn? qfn)
|
||||
(fn [& args]
|
||||
(let [rows (apply qfn args)]
|
||||
(when-not (or (empty? rows)
|
||||
(not (or (some :can-edit rows)
|
||||
(some :is-admin rows)
|
||||
(some :is-owner rows))))
|
||||
rows))))
|
||||
|
||||
(defn make-read-predicate-fn
|
||||
"A simple factory for read permission predicate functions."
|
||||
[qfn]
|
||||
(us/assert fn? qfn)
|
||||
(fn [& args]
|
||||
(let [rows (apply qfn args)]
|
||||
(when (seq rows)
|
||||
rows))))
|
||||
|
||||
(defn make-check-fn
|
||||
"Helper that converts a predicate permission function to a check
|
||||
function (function that raises an exception)."
|
||||
[pred]
|
||||
(fn [& args]
|
||||
(when-not (seq (apply pred args))
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found
|
||||
:hint "not found"))))
|
||||
|
||||
|
||||
;; TODO: the following functions are deprecated and replaced with the
|
||||
;; new ones. Should not be used.
|
||||
|
||||
(defn make-edition-check-fn
|
||||
"A simple factory for edition permission check functions."
|
||||
[qfn]
|
||||
|
|
|
@ -61,16 +61,23 @@
|
|||
|
||||
(defn- retrieve-file-permissions
|
||||
[conn profile-id file-id]
|
||||
(db/exec! conn [sql:file-permissions
|
||||
file-id profile-id
|
||||
file-id profile-id
|
||||
file-id profile-id]))
|
||||
(when (and profile-id file-id)
|
||||
(db/exec! conn [sql:file-permissions
|
||||
file-id profile-id
|
||||
file-id profile-id
|
||||
file-id profile-id])))
|
||||
|
||||
(def has-edit-permissions?
|
||||
(perms/make-edition-predicate-fn retrieve-file-permissions))
|
||||
|
||||
(def has-read-permissions?
|
||||
(perms/make-read-predicate-fn retrieve-file-permissions))
|
||||
|
||||
(def check-edition-permissions!
|
||||
(perms/make-edition-check-fn retrieve-file-permissions))
|
||||
(perms/make-check-fn has-edit-permissions?))
|
||||
|
||||
(def check-read-permissions!
|
||||
(perms/make-read-check-fn retrieve-file-permissions))
|
||||
(perms/make-check-fn has-read-permissions?))
|
||||
|
||||
|
||||
;; --- Query: Files search
|
||||
|
|
|
@ -14,24 +14,98 @@
|
|||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
;; --- Query: View Only Bundle
|
||||
|
||||
(defn- decode-share-link-row
|
||||
[row]
|
||||
(-> row
|
||||
(update :flags db/decode-pgarray #{})
|
||||
(update :pages db/decode-pgarray #{})))
|
||||
|
||||
(defn- retrieve-project
|
||||
[conn id]
|
||||
(db/get-by-id conn :project id {:columns [:id :name :team-id]}))
|
||||
|
||||
(defn- retrieve-share-link
|
||||
[{:keys [conn]} file-id id]
|
||||
(some-> (db/get-by-params conn :share-link
|
||||
{:id id :file-id file-id}
|
||||
{:check-not-found false})
|
||||
(decode-share-link-row)))
|
||||
|
||||
(defn- retrieve-bundle
|
||||
[{:keys [conn] :as cfg} file-id]
|
||||
(let [file (files/retrieve-file cfg file-id)
|
||||
project (retrieve-project conn (:project-id file))
|
||||
libs (files/retrieve-file-libraries cfg false file-id)
|
||||
users (teams/retrieve-users conn (:team-id project))
|
||||
|
||||
links (->> (db/query conn :share-link {:file-id file-id})
|
||||
(mapv decode-share-link-row))
|
||||
|
||||
fonts (db/query conn :team-font-variant
|
||||
{:team-id (:team-id project)
|
||||
:deleted-at nil})]
|
||||
{:file file
|
||||
:users users
|
||||
:fonts fonts
|
||||
:project project
|
||||
:share-links links
|
||||
:libraries libs}))
|
||||
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::share-id ::us/uuid)
|
||||
|
||||
(s/def ::view-only-bundle
|
||||
(s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id]))
|
||||
|
||||
(sv/defmethod ::view-only-bundle {:auth false}
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [cfg (assoc cfg :conn conn)
|
||||
bundle (retrieve-bundle cfg file-id)
|
||||
slink (retrieve-share-link cfg file-id share-id)]
|
||||
|
||||
;; When we have neither profile nor share, we just return a not
|
||||
;; found response to the user.
|
||||
(when (and (not profile-id)
|
||||
(not slink))
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found))
|
||||
|
||||
;; When we have only profile, we need to check read permissiones
|
||||
;; on file.
|
||||
(when (and profile-id (not slink))
|
||||
(files/check-read-permissions! conn profile-id file-id))
|
||||
|
||||
(cond-> bundle
|
||||
;; If we have current profile, put
|
||||
(some? profile-id)
|
||||
(as-> $ (let [edit? (boolean (files/has-edit-permissions? conn profile-id file-id))
|
||||
read? (boolean (files/has-read-permissions? conn profile-id file-id))]
|
||||
(-> (assoc $ :permissions {:read read? :edit edit?})
|
||||
(cond-> (not edit?) (dissoc :share-links)))))
|
||||
|
||||
(some? slink)
|
||||
(assoc :share slink)
|
||||
|
||||
(and (some? slink)
|
||||
(not (contains? (:flags slink) "view-all-pages")))
|
||||
(update-in [:file :data] (fn [data]
|
||||
(let [allowed-pages (:pages slink)]
|
||||
(-> data
|
||||
(update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages)))
|
||||
(update :pages-index (fn [index] (select-keys index allowed-pages)))))))))))
|
||||
|
||||
;; --- Query: Viewer Bundle (by Page ID)
|
||||
|
||||
;; DEPRECATED: should be removed in 1.9.x
|
||||
|
||||
(declare check-shared-token!)
|
||||
(declare retrieve-shared-token)
|
||||
|
||||
(def ^:private
|
||||
sql:project
|
||||
"select p.id, p.name, p.team_id
|
||||
from project as p
|
||||
where p.id = ?
|
||||
and p.deleted_at is null")
|
||||
|
||||
(defn- retrieve-project
|
||||
[conn id]
|
||||
(db/exec-one! conn [sql:project id]))
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::token ::us/string)
|
||||
|
||||
|
@ -81,6 +155,3 @@
|
|||
[conn file-id page-id]
|
||||
(let [sql "select * from file_share_token where file_id=? and page_id=?"]
|
||||
(db/exec-one! conn [sql file-id page-id])))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
(defmethod handle-deletion :team-font-variant
|
||||
[{:keys [conn storage]} {:keys [id] :as props}]
|
||||
(let [font (db/get-by-id conn :team-font-variant id {:uncheked true})
|
||||
(let [font (db/get-by-id conn :team-font-variant id {:check-not-found false})
|
||||
storage (assoc storage :conn conn)]
|
||||
(when (:deleted-at font)
|
||||
(db/delete! conn :team-font-variant {:id id})
|
||||
|
|
|
@ -16,18 +16,18 @@
|
|||
(t/use-fixtures :each th/database-reset)
|
||||
|
||||
(t/deftest retrieve-bundle
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
prof2 (th/create-profile* 2 {:is-active true})
|
||||
team-id (:default-team-id prof)
|
||||
proj-id (:default-project-id prof)
|
||||
(let [prof (th/create-profile* 1 {:is-active true})
|
||||
prof2 (th/create-profile* 2 {:is-active true})
|
||||
team-id (:default-team-id prof)
|
||||
proj-id (:default-project-id prof)
|
||||
|
||||
file (th/create-file* 1 {:profile-id (:id prof)
|
||||
:project-id proj-id
|
||||
:is-shared false})
|
||||
token (atom nil)]
|
||||
file (th/create-file* 1 {:profile-id (:id prof)
|
||||
:project-id proj-id
|
||||
:is-shared false})
|
||||
share-id (atom nil)]
|
||||
|
||||
(t/testing "authenticated with page-id"
|
||||
(let [data {::th/type :viewer-bundle
|
||||
(let [data {::th/type :view-only-bundle
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
|
@ -38,64 +38,67 @@
|
|||
(t/is (nil? (:error out)))
|
||||
|
||||
(let [result (:result out)]
|
||||
(t/is (contains? result :token))
|
||||
(t/is (contains? result :page))
|
||||
(t/is (contains? result :share-links))
|
||||
(t/is (contains? result :permissions))
|
||||
(t/is (contains? result :libraries))
|
||||
(t/is (contains? result :file))
|
||||
(t/is (contains? result :project)))))
|
||||
|
||||
(t/testing "generate share token"
|
||||
(let [data {::th/type :create-file-share-token
|
||||
(let [data {::th/type :create-share-link
|
||||
:profile-id (:id prof)
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
:pages #{(get-in file [:data :pages 0])}
|
||||
:flags #{}}
|
||||
out (th/mutation! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
(t/is (string? (:token result)))
|
||||
(reset! token (:token result)))))
|
||||
(t/is (uuid? (:id result)))
|
||||
(reset! share-id (:id result)))))
|
||||
|
||||
(t/testing "not authenticated with page-id"
|
||||
(let [data {::th/type :viewer-bundle
|
||||
(let [data {::th/type :view-only-bundle
|
||||
:profile-id (:id prof2)
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
out (th/query! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
(let [error (:error out)
|
||||
(let [error (:error out)
|
||||
error-data (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type error-data) :not-found))
|
||||
(t/is (= (:code error-data) :object-not-found)))))
|
||||
|
||||
;; (t/testing "authenticated with token & profile"
|
||||
;; (let [data {::sq/type :viewer-bundle
|
||||
;; :profile-id (:id prof2)
|
||||
;; :token @token
|
||||
;; :file-id (:id file)
|
||||
;; :page-id (get-in file [:data :pages 0])}
|
||||
;; out (th/try-on! (sq/handle data))]
|
||||
(t/testing "authenticated with token & profile"
|
||||
(let [data {::th/type :view-only-bundle
|
||||
:profile-id (:id prof2)
|
||||
:share-id @share-id
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
out (th/query! data)]
|
||||
|
||||
;; ;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
|
||||
;; (let [result (:result out)]
|
||||
;; (t/is (contains? result :page))
|
||||
;; (t/is (contains? result :file))
|
||||
;; (t/is (contains? result :project)))))
|
||||
(let [result (:result out)]
|
||||
(t/is (contains? result :share))
|
||||
(t/is (contains? result :file))
|
||||
(t/is (contains? result :project)))))
|
||||
|
||||
;; (t/testing "authenticated with token"
|
||||
;; (let [data {::sq/type :viewer-bundle
|
||||
;; :token @token
|
||||
;; :file-id (:id file)
|
||||
;; :page-id (get-in file [:data :pages 0])}
|
||||
;; out (th/try-on! (sq/handle data))]
|
||||
(t/testing "authenticated with token"
|
||||
(let [data {::th/type :view-only-bundle
|
||||
:share-id @share-id
|
||||
:file-id (:id file)
|
||||
:page-id (get-in file [:data :pages 0])}
|
||||
out (th/query! data)]
|
||||
|
||||
;; ;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(let [result (:result out)]
|
||||
(t/is (contains? result :file))
|
||||
(t/is (contains? result :share))
|
||||
(t/is (contains? result :project)))))
|
||||
|
||||
;; (let [result (:result out)]
|
||||
;; (t/is (contains? result :page))
|
||||
;; (t/is (contains? result :file))
|
||||
;; (t/is (contains? result :project)))))
|
||||
))
|
||||
|
|
|
@ -228,9 +228,12 @@
|
|||
([params] (update-file* *pool* params))
|
||||
([conn {:keys [file-id changes session-id profile-id revn]
|
||||
:or {session-id (uuid/next) revn 0}}]
|
||||
(let [file (db/get-by-id conn :file file-id)
|
||||
msgbus (:app.msgbus/msgbus *system*)]
|
||||
(#'files/update-file {:conn conn :msgbus msgbus}
|
||||
(let [file (db/get-by-id conn :file file-id)
|
||||
msgbus (:app.msgbus/msgbus *system*)
|
||||
metrics (:app.metrics/metrics *system*)]
|
||||
(#'files/update-file {:conn conn
|
||||
:msgbus msgbus
|
||||
:metrics metrics}
|
||||
{:file file
|
||||
:revn revn
|
||||
:changes changes
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue