♻️ Refactor time related namespaces

Mainly removes the custom app.util.time namespace
from frontend and backend and normalize all to use
the app.common.time namespace
This commit is contained in:
Andrey Antukh
2025-07-23 09:47:16 +02:00
parent 9a0c36c442
commit 283eb0419c
133 changed files with 1272 additions and 1560 deletions

View File

@@ -45,10 +45,16 @@
:potok/reify-type
{:level :error}
:missing-protocol-method
{:level :off}
:unresolved-namespace
{:level :warning
:exclude [data_readers]}
:unused-value
{:level :off}
:single-key-in
{:level :warning}
@@ -64,6 +70,9 @@
:redundant-nested-call
{:level :off}
:redundant-str-call
{:level :off}
:earmuffed-var-not-dynamic
{:level :off}

View File

@@ -30,7 +30,7 @@
[app.srepl.helpers :as srepl.helpers]
[app.srepl.main :as srepl]
[app.util.blob :as blob]
[app.util.time :as dt]
[app.common.time :as ct]
[clj-async-profiler.core :as prof]
[clojure.contrib.humanize :as hum]
[clojure.java.io :as io]

View File

@@ -13,6 +13,7 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.config :as cf]
[app.db :as db]
@@ -28,7 +29,6 @@
[app.tokens :as tokens]
[app.util.inet :as inet]
[app.util.json :as json]
[app.util.time :as dt]
[buddy.sign.jwk :as jwk]
[buddy.sign.jwt :as jwt]
[clojure.set :as set]
@@ -514,7 +514,7 @@
[cfg info request]
(let [info (assoc info
:iss :prepared-register
:exp (dt/in-future {:hours 48}))
:exp (ct/in-future {:hours 48}))
params {:token (tokens/generate (::setup/props cfg) info)
:provider (:provider (:path-params request))
@@ -571,7 +571,7 @@
token (or (:invitation-token info)
(tokens/generate (::setup/props cfg)
{:iss :auth
:exp (dt/in-future "15m")
:exp (ct/in-future "15m")
:profile-id (:id profile)}))
props (audit/profile->props profile)
context (d/without-nils {:external-session-id (:external-session-id info)})]
@@ -619,7 +619,7 @@
:invitation-token (:invitation-token params)
:external-session-id esid
:props props
:exp (dt/in-future "4h")}
:exp (ct/in-future "4h")}
state (tokens/generate (::setup/props cfg)
(d/without-nils params))
uri (build-auth-uri cfg state)]

View File

@@ -16,6 +16,7 @@
[app.common.files.validate :as fval]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -28,7 +29,6 @@
[app.storage :as sto]
[app.util.blob :as blob]
[app.util.pointer-map :as pmap]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.set :as set]
[cuerdas.core :as str]
@@ -540,14 +540,13 @@
Returns nil"
[{:keys [::timestamp] :as cfg} file & {:as opts}]
(assert (ct/inst? timestamp) "expected valid timestamp")
(assert (dt/instant? timestamp) "expected valid timestamp")
(let [file (-> file
(assoc :created-at timestamp)
(assoc :modified-at timestamp)
(cond-> (not (::overwrite cfg))
(assoc :ignore-sync-until (dt/plus timestamp (dt/duration {:seconds 5}))))
(update :revn inc)
(assoc :ignore-sync-until (ct/plus timestamp (ct/duration {:seconds 5}))))
(update :features
(fn [features]
(-> (::features cfg #{})

View File

@@ -17,6 +17,7 @@
[app.common.fressian :as fres]
[app.common.logging :as l]
[app.common.spec :as us]
[app.common.time :as ct]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -30,7 +31,6 @@
[app.storage.tmp :as tmp]
[app.tasks.file-gc]
[app.util.events :as events]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.java.io :as jio]
[clojure.set :as set]
@@ -434,7 +434,7 @@
(defn read-import!
"Do the importation of the specified resource in penpot custom binary
format."
[{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (dt/now)} :as options}]
[{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (ct/now)} :as options}]
(dm/assert!
"expected input stream"
@@ -442,7 +442,7 @@
(dm/assert!
"expected valid instant"
(dt/instant? timestamp))
(ct/inst? timestamp))
(let [version (read-header! input)]
(read-import (assoc options ::version version ::bfc/timestamp timestamp))))
@@ -682,7 +682,7 @@
(io/coercible? output))
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
ab (volatile! false)
cs (volatile! nil)]
(try
@@ -720,7 +720,7 @@
(satisfies? jio/IOFactory input))
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
cs (volatile! nil)]
(l/info :hint "import: started" :id (str id))
@@ -742,6 +742,6 @@
(finally
(l/info :hint "import: terminated"
:id (str id)
:elapsed (dt/format-duration (tp))
:elapsed (ct/format-duration (tp))
:error? (some? @cs))))))

View File

@@ -13,6 +13,7 @@
[app.common.data :as d]
[app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -23,7 +24,6 @@
[app.storage :as sto]
[app.storage.tmp :as tmp]
[app.util.events :as events]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.set :as set]
[cuerdas.core :as str]
@@ -344,7 +344,7 @@
(defn export-team!
[cfg team-id]
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
cfg (create-database cfg)]
(l/inf :hint "start"
@@ -378,15 +378,15 @@
(l/inf :hint "end"
:operation "export"
:id (str id)
:elapsed (dt/format-duration elapsed)))))))
:elapsed (ct/format-duration elapsed)))))))
(defn import-team!
[cfg path]
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
cfg (-> (create-database cfg path)
(assoc ::bfc/timestamp (dt/now)))]
(assoc ::bfc/timestamp (ct/now)))]
(l/inf :hint "start"
:operation "import"
@@ -434,4 +434,4 @@
(l/inf :hint "end"
:operation "import"
:id (str id)
:elapsed (dt/format-duration elapsed)))))))
:elapsed (ct/format-duration elapsed)))))))

View File

@@ -20,6 +20,7 @@
[app.common.media :as cmedia]
[app.common.schema :as sm]
[app.common.thumbnails :as cth]
[app.common.time :as ct]
[app.common.types.color :as ctcl]
[app.common.types.component :as ctc]
[app.common.types.file :as ctf]
@@ -35,7 +36,6 @@
[app.storage :as sto]
[app.storage.impl :as sto.impl]
[app.util.events :as events]
[app.util.time :as dt]
[clojure.java.io :as jio]
[cuerdas.core :as str]
[datoteka.fs :as fs]
@@ -92,7 +92,7 @@
(defn- default-now
[o]
(or o (dt/now)))
(or o (ct/now)))
;; --- ENCODERS
@@ -937,10 +937,10 @@
[file-id])))
(defn- import-files
[{:keys [::bfc/timestamp ::bfc/input] :or {timestamp (dt/now)} :as cfg}]
[{:keys [::bfc/timestamp ::bfc/input] :or {timestamp (ct/now)} :as cfg}]
(assert (instance? ZipFile input) "expected zip file")
(assert (dt/instant? timestamp) "expected valid instant")
(assert (ct/inst? timestamp) "expected valid instant")
(let [manifest (-> (read-manifest input)
(validate-manifest))
@@ -1000,7 +1000,7 @@
"expected instance of jio/IOFactory for `input`")
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
ab (volatile! false)
cs (volatile! nil)]
(try
@@ -1046,7 +1046,7 @@
"expected instance of jio/IOFactory for `input`")
(let [id (uuid/next)
tp (dt/tpoint)
tp (ct/tpoint)
cs (volatile! nil)]
(l/info :hint "import: started" :id (str id))
@@ -1061,7 +1061,7 @@
(finally
(l/info :hint "import: terminated"
:id (str id)
:elapsed (dt/format-duration (tp))
:elapsed (ct/format-duration (tp))
:error? (some? @cs))))))
(defn get-manifest

View File

@@ -12,10 +12,10 @@
[app.common.exceptions :as ex]
[app.common.flags :as flags]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.common.version :as v]
[app.util.overrides]
[app.util.time :as dt]
[clojure.core :as c]
[clojure.java.io :as io]
[cuerdas.core :as str]
@@ -59,10 +59,10 @@
:smtp-default-reply-to "Penpot <no-reply@example.com>"
:smtp-default-from "Penpot <no-reply@example.com>"
:profile-complaint-max-age (dt/duration {:days 7})
:profile-complaint-max-age (ct/duration {:days 7})
:profile-complaint-threshold 2
:profile-bounce-max-age (dt/duration {:days 7})
:profile-bounce-max-age (ct/duration {:days 7})
:profile-bounce-threshold 10
:telemetry-uri "https://telemetry.penpot.app/"
@@ -102,10 +102,10 @@
[:telemetry-with-taiga {:optional true} ::sm/boolean] ;; DELETE
[:auto-file-snapshot-every {:optional true} ::sm/int]
[:auto-file-snapshot-timeout {:optional true} ::dt/duration]
[:auto-file-snapshot-timeout {:optional true} ::ct/duration]
[:media-max-file-size {:optional true} ::sm/int]
[:deletion-delay {:optional true} ::dt/duration] ;; REVIEW
[:deletion-delay {:optional true} ::ct/duration] ;; REVIEW
[:telemetry-enabled {:optional true} ::sm/boolean]
[:default-blob-version {:optional true} ::sm/int]
[:allow-demo-users {:optional true} ::sm/boolean]
@@ -148,10 +148,10 @@
[:auth-data-cookie-domain {:optional true} :string]
[:auth-token-cookie-name {:optional true} :string]
[:auth-token-cookie-max-age {:optional true} ::dt/duration]
[:auth-token-cookie-max-age {:optional true} ::ct/duration]
[:registration-domain-whitelist {:optional true} [::sm/set :string]]
[:email-verify-threshold {:optional true} ::dt/duration]
[:email-verify-threshold {:optional true} ::ct/duration]
[:github-client-id {:optional true} :string]
[:github-client-secret {:optional true} :string]
@@ -186,9 +186,9 @@
[:ldap-starttls {:optional true} ::sm/boolean]
[:ldap-user-query {:optional true} :string]
[:profile-bounce-max-age {:optional true} ::dt/duration]
[:profile-bounce-max-age {:optional true} ::ct/duration]
[:profile-bounce-threshold {:optional true} ::sm/int]
[:profile-complaint-max-age {:optional true} ::dt/duration]
[:profile-complaint-max-age {:optional true} ::ct/duration]
[:profile-complaint-threshold {:optional true} ::sm/int]
[:redis-uri {:optional true} ::sm/uri]
@@ -298,7 +298,7 @@
(defn get-deletion-delay
[]
(or (c/get config :deletion-delay)
(dt/duration {:days 7})))
(ct/duration {:days 7})))
(defn get
"A configuration getter. Helps code be more testable."

View File

@@ -13,11 +13,11 @@
[app.common.json :as json]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.db.sql :as sql]
[app.metrics :as mtx]
[app.util.time :as dt]
[clojure.java.io :as io]
[clojure.set :as set]
[integrant.core :as ig]
@@ -379,9 +379,9 @@
(defn is-row-deleted?
[{:keys [deleted-at]}]
(and (dt/instant? deleted-at)
(and (ct/inst? deleted-at)
(< (inst-ms deleted-at)
(inst-ms (dt/now)))))
(inst-ms (ct/now)))))
(defn get*
"Retrieve a single row from database that matches a simple filters. Do
@@ -605,7 +605,7 @@
(string? o)
(pginterval o)
(dt/duration? o)
(ct/duration? o)
(interval (inst-ms o))
:else

View File

@@ -7,8 +7,8 @@
(ns app.features.logical-deletion
"A code related to handle logical deletion mechanism"
(:require
[app.config :as cf]
[app.util.time :as dt]))
[app.common.time :as ct]
[app.config :as cf]))
(def ^:private canceled-status
#{"canceled" "unpaid"})
@@ -20,10 +20,10 @@
(if-let [{:keys [type status]} (get team :subscription)]
(cond
(and (= "unlimited" type) (not (contains? canceled-status status)))
(dt/duration {:days 30})
(ct/duration {:days 30})
(and (= "enterprise" type) (not (contains? canceled-status status)))
(dt/duration {:days 90})
(ct/duration {:days 90})
:else
(cf/get-deletion-delay))

View File

@@ -9,18 +9,18 @@
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.time :as ct]
[app.common.uri :as u]
[app.db :as db]
[app.storage :as sto]
[app.util.time :as dt]
[integrant.core :as ig]
[yetti.response :as-alias yres]))
(def ^:private cache-max-age
(dt/duration {:hours 24}))
(ct/duration {:hours 24}))
(def ^:private signature-max-age
(dt/duration {:hours 24 :minutes 15}))
(ct/duration {:hours 24 :minutes 15}))
(defn get-id
[{:keys [path-params]}]

View File

@@ -15,6 +15,7 @@
[app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.pprint :as pp]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -31,7 +32,6 @@
[app.storage.tmp :as tmp]
[app.util.blob :as blob]
[app.util.template :as tmpl]
[app.util.time :as dt]
[cuerdas.core :as str]
[datoteka.io :as io]
[emoji.core :as emj]
@@ -137,7 +137,7 @@
file (some-> params :file :path io/read* t/decode)]
(if (and file project-id)
(let [fname (str "Imported: " (:name file) "(" (dt/now) ")")
(let [fname (str "Imported: " (:name file) "(" (ct/now) ")")
reuse-id? (contains? params :reuseid)
file-id (or (and reuse-id? (ex/ignoring (-> params :file :filename parse-uuid)))
(uuid/next))]
@@ -222,7 +222,7 @@
(-> (io/resource "app/templates/error-report.v3.tmpl")
(tmpl/render (-> content
(assoc :id id)
(assoc :created-at (dt/format-instant created-at :rfc1123))))))]
(assoc :created-at (ct/format-inst created-at :rfc1123))))))]
(if-let [report (get-report request)]
(let [result (case (:version report)
@@ -246,7 +246,7 @@
(defn error-list-handler
[{:keys [::db/pool]} _request]
(let [items (->> (db/exec! pool [sql:error-reports])
(map #(update % :created-at dt/format-instant :rfc1123)))]
(map #(update % :created-at ct/format-inst :rfc1123)))]
{::yres/status 200
::yres/body (-> (io/resource "app/templates/error-list.tmpl")
(tmpl/render {:items items}))

View File

@@ -10,6 +10,7 @@
[app.common.data :as d]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.config :as cf]
[app.db :as db]
@@ -18,7 +19,6 @@
[app.main :as-alias main]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.time :as dt]
[cuerdas.core :as str]
[integrant.core :as ig]
[yetti.request :as yreq]))
@@ -35,10 +35,10 @@
(def default-auth-data-cookie-name "auth-data")
;; Default value for cookie max-age
(def default-cookie-max-age (dt/duration {:days 7}))
(def default-cookie-max-age (ct/duration {:days 7}))
;; Default age for automatic session renewal
(def default-renewal-max-age (dt/duration {:hours 6}))
(def default-renewal-max-age (ct/duration {:hours 6}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROTOCOLS
@@ -66,7 +66,7 @@
[:map {:title "session-params"}
[:user-agent ::sm/text]
[:profile-id ::sm/uuid]
[:created-at ::sm/inst]])
[:created-at ::ct/inst]])
(def ^:private valid-params?
(sm/validator schema:params))
@@ -95,7 +95,7 @@
params))
(update! [_ params]
(let [updated-at (dt/now)]
(let [updated-at (ct/now)]
(db/update! pool :http-session
{:updated-at updated-at}
{:id (:id params)})
@@ -118,7 +118,7 @@
params))
(update! [_ params]
(let [updated-at (dt/now)]
(let [updated-at (ct/now)]
(swap! cache update (:id params) assoc :updated-at updated-at)
(assoc params :updated-at updated-at)))
@@ -158,7 +158,7 @@
(let [uagent (yreq/get-header request "user-agent")
params {:profile-id profile-id
:user-agent uagent
:created-at (dt/now)}
:created-at (ct/now)}
token (gen-token props params)
session (write! manager token params)]
(l/trace :hint "create" :profile-id (str profile-id))
@@ -203,8 +203,8 @@
(defn- renew-session?
[{:keys [updated-at] :as session}]
(and (dt/instant? updated-at)
(let [elapsed (dt/diff updated-at (dt/now))]
(and (ct/inst? updated-at)
(let [elapsed (ct/diff updated-at (ct/now))]
(neg? (compare default-renewal-max-age elapsed)))))
(defn- wrap-soft-auth
@@ -256,14 +256,14 @@
(defn- assign-auth-token-cookie
[response {token :id updated-at :updated-at}]
(let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age)
created-at (or updated-at (dt/now))
renewal (dt/plus created-at default-renewal-max-age)
expires (dt/plus created-at max-age)
created-at (or updated-at (ct/now))
renewal (ct/plus created-at default-renewal-max-age)
expires (ct/plus created-at max-age)
secure? (contains? cf/flags :secure-session-cookies)
strict? (contains? cf/flags :strict-session-cookies)
cors? (contains? cf/flags :cors)
name (cf/get :auth-token-cookie-name default-auth-token-cookie-name)
comment (str "Renewal at: " (dt/format-instant renewal :rfc1123))
comment (str "Renewal at: " (ct/format-inst renewal :rfc1123))
cookie {:path "/"
:http-only true
:expires expires
@@ -279,11 +279,11 @@
domain (cf/get :auth-data-cookie-domain)
cname default-auth-data-cookie-name
created-at (or updated-at (dt/now))
renewal (dt/plus created-at default-renewal-max-age)
expires (dt/plus created-at max-age)
created-at (or updated-at (ct/now))
renewal (ct/plus created-at default-renewal-max-age)
expires (ct/plus created-at max-age)
comment (str "Renewal at: " (dt/format-instant renewal :rfc1123))
comment (str "Renewal at: " (ct/format-inst renewal :rfc1123))
secure? (contains? cf/flags :secure-session-cookies)
strict? (contains? cf/flags :strict-session-cookies)
cors? (contains? cf/flags :cors)
@@ -323,7 +323,7 @@
(defmethod ig/assert-key ::tasks/gc
[_ params]
(assert (db/pool? (::db/pool params)) "expected valid database pool")
(assert (dt/duration? (::tasks/max-age params))))
(assert (ct/duration? (::tasks/max-age params))))
(defmethod ig/expand-key ::tasks/gc
[k v]

View File

@@ -11,12 +11,12 @@
[app.common.logging :as l]
[app.common.pprint :as pp]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.http.session :as session]
[app.metrics :as mtx]
[app.msgbus :as mbus]
[app.util.time :as dt]
[app.util.websocket :as ws]
[integrant.core :as ig]
[promesa.exec.csp :as sp]
@@ -239,7 +239,7 @@
(defn- on-connect
[{:keys [::mtx/metrics]} {:keys [::ws/id] :as wsp}]
(let [created-at (dt/now)]
(let [created-at (ct/now)]
(l/trace :fn "on-connect" :conn-id id)
(swap! state assoc id wsp)
(mtx/run! metrics
@@ -253,7 +253,7 @@
(mtx/run! metrics :id :websocket-active-connections :dec 1)
(mtx/run! metrics
:id :websocket-session-timing
:val (/ (inst-ms (dt/diff created-at (dt/now))) 1000.0))))))
:val (/ (inst-ms (ct/diff created-at (ct/now))) 1000.0))))))
(defn- on-rcv-message
[{:keys [::mtx/metrics ::profile-id ::session-id]} message]

View File

@@ -11,6 +11,7 @@
[app.common.data.macros :as dm]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -23,7 +24,6 @@
[app.setup :as-alias setup]
[app.util.inet :as inet]
[app.util.services :as-alias sv]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]))
@@ -108,9 +108,9 @@
[::ip-addr {:optional true} ::sm/text]
[::props {:optional true} [:map-of :keyword :any]]
[::context {:optional true} [:map-of :keyword :any]]
[::tracked-at {:optional true} ::sm/inst]
[::tracked-at {:optional true} ::ct/inst]
[::webhooks/event? {:optional true} ::sm/boolean]
[::webhooks/batch-timeout {:optional true} ::dt/duration]
[::webhooks/batch-timeout {:optional true} ::ct/duration]
[::webhooks/batch-key {:optional true}
[:or ::sm/fn ::sm/text :keyword]]])
@@ -199,7 +199,7 @@
(defn- handle-event!
[cfg event]
(let [params (event->params event)
tnow (dt/now)]
tnow (ct/now)]
(when (contains? cf/flags :audit-log)
;; NOTE: this operation may cause primary key conflicts on inserts
@@ -273,7 +273,7 @@
(let [event (-> (d/without-nils event)
(check-event))]
(db/run! cfg (fn [cfg]
(let [tnow (dt/now)
(let [tnow (ct/now)
params (-> (event->params event)
(assoc :created-at tnow)
(update :tracked-at #(or % tnow)))]

View File

@@ -9,6 +9,7 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -16,7 +17,6 @@
[app.http.client :as http]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.time :as dt]
[integrant.core :as ig]
[lambdaisland.uri :as u]
[promesa.exec :as px]))
@@ -55,7 +55,7 @@
[{:keys [::uri] :as cfg} events]
(let [token (tokens/generate (::setup/props cfg)
{:iss "authentication"
:iat (dt/now)
:iat (ct/now)
:uid uuid/zero})
body (t/encode {:events events})
headers {"content-type" "application/transit+json"

View File

@@ -10,13 +10,13 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.logging :as l]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uri :as uri]
[app.config :as cf]
[app.db :as db]
[app.http.client :as http]
[app.loggers.audit :as audit]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.data.json :as json]
[cuerdas.core :as str]
@@ -124,7 +124,7 @@
{:id (:id whook)})))
(db/update! pool :webhook
{:updated-at (dt/now)
{:updated-at (ct/now)
:error-code nil
:error-count 0}
{:id (:id whook)})))
@@ -132,7 +132,7 @@
(report-delivery! [whook req rsp err]
(db/insert! pool :webhook-delivery
{:webhook-id (:id whook)
:created-at (dt/now)
:created-at (ct/now)
:error-code err
:req-data (db/tjson req)
:rsp-data (db/tjson rsp)}))]
@@ -155,7 +155,7 @@
(let [req {:uri (:uri whook)
:headers {"content-type" (:mtype whook)
"user-agent" (str/ffmt "penpot/%" (:main cf/version))}
:timeout (dt/duration "4s")
:timeout (ct/duration "4s")
:method :post
:body body}]
(try

View File

@@ -11,6 +11,7 @@
[app.auth.oidc.providers :as-alias oidc.providers]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as-alias db]
[app.email :as-alias email]
@@ -38,7 +39,7 @@
[app.storage.gc-touched :as-alias sto.gc-touched]
[app.storage.s3 :as-alias sto.s3]
[app.svgo :as-alias svgo]
[app.util.time :as dt]
[app.util.cron]
[app.worker :as-alias wrk]
[clojure.test :as test]
[clojure.tools.namespace.repl :as repl]
@@ -298,8 +299,8 @@
:app.http.assets/routes
{::http.assets/path (cf/get :assets-path)
::http.assets/cache-max-age (dt/duration {:hours 24})
::http.assets/cache-max-agesignature-max-age (dt/duration {:hours 24 :minutes 5})
::http.assets/cache-max-age (ct/duration {:hours 24})
::http.assets/cache-max-agesignature-max-age (ct/duration {:hours 24 :minutes 5})
::sto/storage (ig/ref ::sto/storage)}
::rpc/climit
@@ -480,33 +481,33 @@
{::wrk/registry (ig/ref ::wrk/registry)
::db/pool (ig/ref ::db/pool)
::wrk/entries
[{:cron #app/cron "0 0 0 * * ?" ;; daily
[{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :session-gc}
{:cron #app/cron "0 0 0 * * ?" ;; daily
{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :objects-gc}
{:cron #app/cron "0 0 0 * * ?" ;; daily
{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :storage-gc-deleted}
{:cron #app/cron "0 0 0 * * ?" ;; daily
{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :storage-gc-touched}
{:cron #app/cron "0 0 0 * * ?" ;; daily
{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :tasks-gc}
{:cron #app/cron "0 0 2 * * ?" ;; daily
{:cron #penpot/cron "0 0 2 * * ?" ;; daily
:task :file-gc-scheduler}
{:cron #app/cron "0 30 */3,23 * * ?"
{:cron #penpot/cron "0 30 */3,23 * * ?"
:task :telemetry}
(when (contains? cf/flags :audit-log-archive)
{:cron #app/cron "0 */5 * * * ?" ;; every 5m
{:cron #penpot/cron "0 */5 * * * ?" ;; every 5m
:task :audit-log-archive})
(when (contains? cf/flags :audit-log-gc)
{:cron #app/cron "30 */5 * * * ?" ;; every 5m
{:cron #penpot/cron "30 */5 * * * ?" ;; every 5m
:task :audit-log-gc})]}
::wrk/dispatcher

View File

@@ -14,11 +14,11 @@
[app.common.media :as cm]
[app.common.schema :as sm]
[app.common.schema.openapi :as-alias oapi]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as-alias db]
[app.storage :as-alias sto]
[app.storage.tmp :as tmp]
[app.util.time :as dt]
[buddy.core.bytes :as bb]
[buddy.core.codecs :as bc]
[clojure.java.shell :as sh]
@@ -243,7 +243,7 @@
(ex/raise :type :validation
:code :invalid-svg-file
:hint "uploaded svg does not provides dimensions"))
(merge input info {:ts (dt/now)}))
(merge input info {:ts (ct/now)}))
(let [instance (Info. (str path))
mtype' (.getProperty instance "Mime type")]
@@ -263,7 +263,7 @@
(assoc input
:width width
:height height
:ts (dt/now)))))))
:ts (ct/now)))))))
(defmethod process-error org.im4java.core.InfoException
[error]

View File

@@ -10,10 +10,10 @@
[app.common.data :as d]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t]
[app.config :as cfg]
[app.redis :as rds]
[app.util.time :as dt]
[app.worker :as wrk]
[integrant.core :as ig]
[promesa.core :as p]
@@ -56,7 +56,7 @@
[k v]
{k (-> (d/without-nils v)
(assoc ::buffer-size 128)
(assoc ::timeout (dt/duration {:seconds 30})))})
(assoc ::timeout (ct/duration {:seconds 30})))})
(def ^:private schema:params
[:map ::rds/redis ::wrk/executor])

View File

@@ -12,10 +12,10 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.metrics :as mtx]
[app.redis.script :as-alias rscript]
[app.util.cache :as cache]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.core :as c]
[clojure.java.io :as io]
@@ -114,7 +114,7 @@
(let [cpus (px/get-available-processors)
threads (max 1 (int (* cpus 0.2)))]
{k (-> (d/without-nils v)
(assoc ::timeout (dt/duration "10s"))
(assoc ::timeout (ct/duration "10s"))
(assoc ::io-threads (max 3 threads))
(assoc ::worker-threads (max 3 threads)))}))
@@ -125,7 +125,7 @@
[::uri ::sm/uri]
[::worker-threads ::sm/int]
[::io-threads ::sm/int]
[::timeout ::dt/duration]])
[::timeout ::ct/duration]])
(defmethod ig/assert-key ::redis
[_ params]
@@ -331,7 +331,7 @@
(p/rejected cause))))
(eval-script [sha]
(let [tpoint (dt/tpoint)]
(let [tpoint (ct/tpoint)]
(->> (.evalsha ^RedisScriptingAsyncCommands cmd
^String sha
^ScriptOutputType ScriptOutputType/MULTI
@@ -346,7 +346,7 @@
:name (name sname)
:sha sha
:params (str/join "," (::rscript/vals script))
:elapsed (dt/format-duration elapsed))
:elapsed (ct/format-duration elapsed))
result)))
(p/merr on-error))))

View File

@@ -12,6 +12,7 @@
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.http :as-alias http]
@@ -31,7 +32,6 @@
[app.storage :as-alias sto]
[app.util.inet :as inet]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]
[integrant.core :as ig]
@@ -103,7 +103,7 @@
data (-> params
(assoc ::handler-name handler-name)
(assoc ::ip-addr ip-addr)
(assoc ::request-at (dt/now))
(assoc ::request-at (ct/now))
(assoc ::external-session-id session-id)
(assoc ::external-event-origin event-origin)
(assoc ::session/id (::session/id request))
@@ -130,7 +130,7 @@
[{:keys [::mtx/metrics ::metrics-id]} f mdata]
(let [labels (into-array String [(::sv/name mdata)])]
(fn [cfg params]
(let [tp (dt/tpoint)]
(let [tp (ct/tpoint)]
(try
(f cfg params)
(finally

View File

@@ -11,11 +11,11 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.metrics :as mtx]
[app.rpc :as-alias rpc]
[app.util.cache :as cache]
[app.util.services :as-alias sv]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.edn :as edn]
[clojure.set :as set]
@@ -154,7 +154,7 @@
:id limit-id
:label limit-label
:queue queue
:elapsed (some-> elapsed dt/format-duration)
:elapsed (some-> elapsed ct/format-duration)
:params @limit-params)))
(def ^:private idseq (AtomicLong. 0))
@@ -171,7 +171,7 @@
mlabels (into-array String [(id->str limit-id)])
limit-id (id->str limit-id limit-key)
limiter (cache/get cache limit-id (partial create-limiter config))
tpoint (dt/tpoint)
tpoint (ct/tpoint)
req-id (.incrementAndGet ^AtomicLong idseq)]
(try
(let [stats (pbh/get-stats limiter)]

View File

@@ -7,6 +7,7 @@
(ns app.rpc.commands.access-token
(:require
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.main :as-alias main]
@@ -15,8 +16,7 @@
[app.rpc.quotes :as quotes]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.services :as sv]
[app.util.time :as dt]))
[app.util.services :as sv]))
(defn- decode-row
[row]
@@ -24,13 +24,13 @@
(defn create-access-token
[{:keys [::db/conn ::setup/props]} profile-id name expiration]
(let [created-at (dt/now)
(let [created-at (ct/now)
token-id (uuid/next)
token (tokens/generate props {:iss "access-token"
:tid token-id
:iat created-at})
expires-at (some-> expiration dt/in-future)
expires-at (some-> expiration ct/in-future)
token (db/insert! conn :access-token
{:id token-id
:name name
@@ -49,7 +49,7 @@
(def ^:private schema:create-access-token
[:map {:title "create-access-token"}
[:name [:string {:max 250 :min 1}]]
[:expiration {:optional true} ::dt/duration]])
[:expiration {:optional true} ::ct/duration]])
(sv/defmethod ::create-access-token
{::doc/added "1.18"

View File

@@ -10,6 +10,7 @@
[app.common.data :as d]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -20,8 +21,7 @@
[app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph]
[app.util.inet :as inet]
[app.util.services :as sv]
[app.util.time :as dt]))
[app.util.services :as sv]))
(def ^:private event-columns
[:id
@@ -49,7 +49,7 @@
(defn- adjust-timestamp
[{:keys [timestamp created-at] :as event}]
(let [margin (inst-ms (dt/diff timestamp created-at))]
(let [margin (inst-ms (ct/diff timestamp created-at))]
(if (or (neg? margin)
(> margin 3600000))
;; If event is in future or lags more than 1 hour, we reasign
@@ -63,7 +63,7 @@
[{:keys [::db/pool]} {:keys [::rpc/profile-id events] :as params}]
(let [request (-> params meta ::http/request)
ip-addr (inet/parse-request request)
tnow (dt/now)
tnow (ct/now)
xform (comp
(map (fn [event]
(-> event

View File

@@ -12,6 +12,7 @@
[app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -30,7 +31,6 @@
[app.setup.welcome-file :refer [create-welcome-file]]
[app.tokens :as tokens]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]))
@@ -42,7 +42,7 @@
(defn- elapsed-verify-threshold?
[profile]
(let [elapsed (dt/diff (:modified-at profile) (dt/now))
(let [elapsed (ct/diff (:modified-at profile) (ct/now))
verify-threshold (cf/get :email-verify-threshold)]
(pos? (compare elapsed verify-threshold))))
@@ -85,7 +85,7 @@
(ex/raise :type :validation
:code :wrong-credentials))
(when-let [deleted-at (:deleted-at profile)]
(when (dt/is-after? (dt/now) deleted-at)
(when (ct/is-after? (ct/now) deleted-at)
(ex/raise :type :validation
:code :wrong-credentials)))
@@ -244,7 +244,7 @@
:backend "penpot"
:iss :prepared-register
:profile-id (:id profile)
:exp (dt/in-future {:days 7})
:exp (ct/in-future {:days 7})
:props {:newsletter-updates (or accept-newsletter-updates false)}}
params (d/without-nils params)
@@ -344,7 +344,7 @@
[{:keys [::db/conn] :as cfg} profile]
(let [vtoken (tokens/generate (::setup/props cfg)
{:iss :verify-email
:exp (dt/in-future "72h")
:exp (ct/in-future "72h")
:profile-id (:id profile)
:email (:email profile)})
;; NOTE: this token is mainly used for possible complains
@@ -352,7 +352,7 @@
ptoken (tokens/generate (::setup/props cfg)
{:iss :profile-identity
:profile-id (:id profile)
:exp (dt/in-future {:days 30})})]
:exp (ct/in-future {:days 30})})]
(eml/send! {::eml/conn conn
::eml/factory eml/register
:public-uri (cf/get :public-uri)
@@ -466,7 +466,7 @@
(when (= action "resend-email-verification")
(db/update! conn :profile
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id (:id profile)})
(send-email-verification! cfg profile))
@@ -495,7 +495,7 @@
(letfn [(create-recovery-token [{:keys [id] :as profile}]
(let [token (tokens/generate (::setup/props cfg)
{:iss :password-recovery
:exp (dt/in-future "15m")
:exp (ct/in-future "15m")
:profile-id id})]
(assoc profile :token token)))
@@ -503,7 +503,7 @@
(let [ptoken (tokens/generate (::setup/props cfg)
{:iss :profile-identity
:profile-id (:id profile)
:exp (dt/in-future {:days 30})})]
:exp (ct/in-future {:days 30})})]
(eml/send! {::eml/conn conn
::eml/factory eml/password-recovery
:public-uri (cf/get :public-uri)
@@ -544,7 +544,7 @@
:else
(do
(db/update! conn :profile
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id (:id profile)})
(->> profile
(create-recovery-token)

View File

@@ -13,6 +13,7 @@
[app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.http.sse :as sse]
@@ -26,7 +27,6 @@
[app.rpc.doc :as-alias doc]
[app.tasks.file-gc]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[promesa.exec :as px]
[yetti.response :as yres]))
@@ -114,7 +114,7 @@
3 (px/invoke! executor (partial bf.v3/import-files! cfg)))]
(db/update! pool :project
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id project-id}
{::db/return-keys false})

View File

@@ -11,6 +11,7 @@
[app.common.exceptions :as ex]
[app.common.geom.point :as gpt]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as uri]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -29,7 +30,6 @@
[app.rpc.retry :as rtry]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -222,7 +222,7 @@
(defn upsert-comment-thread-status!
([conn profile-id thread-id]
(upsert-comment-thread-status! conn profile-id thread-id (dt/in-future "1s")))
(upsert-comment-thread-status! conn profile-id thread-id (ct/in-future "1s")))
([conn profile-id thread-id mod-at]
(db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id mod-at mod-at])))

View File

@@ -8,6 +8,7 @@
"A demo specific mutations."
(:require
[app.common.exceptions :as ex]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.loggers.audit :as audit]
@@ -16,7 +17,6 @@
[app.rpc.commands.profile :as profile]
[app.rpc.doc :as-alias doc]
[app.util.services :as sv]
[app.util.time :as dt]
[buddy.core.codecs :as bc]
[buddy.core.nonce :as bn]))
@@ -45,15 +45,13 @@
params {:email email
:fullname fullname
:is-active true
:deleted-at (dt/in-future (cf/get-deletion-delay))
:deleted-at (ct/in-future (cf/get-deletion-delay))
:password (profile/derive-password cfg password)
:props {}}]
(let [profile (db/tx-run! cfg (fn [{:keys [::db/conn]}]
(->> (auth/create-profile! conn params)
(auth/create-profile-rels! conn))))]
(with-meta {:email email
:password password}
{::audit/profile-id (:id profile)}))))
:props {}}
profile (db/tx-run! cfg (fn [{:keys [::db/conn]}]
(->> (auth/create-profile! conn params)
(auth/create-profile-rels! conn))))]
(with-meta {:email email
:password password}
{::audit/profile-id (:id profile)})))

View File

@@ -16,6 +16,7 @@
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.schema.desc-js-like :as-alias smdj]
[app.common.time :as ct]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.common.uri :as uri]
@@ -37,7 +38,6 @@
[app.util.blob :as blob]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]
[promesa.exec :as px]))
@@ -52,7 +52,7 @@
;; --- HELPERS
(def long-cache-duration
(dt/duration {:days 7}))
(ct/duration {:days 7}))
(defn decode-row
[{:keys [data changes features] :as row}]
@@ -187,10 +187,10 @@
[:name [:string {:max 250}]]
[:revn [::sm/int {:min 0}]]
[:vern [::sm/int {:min 0}]]
[:modified-at ::dt/instant]
[:modified-at ::ct/inst]
[:is-shared ::sm/boolean]
[:project-id ::sm/uuid]
[:created-at ::dt/instant]
[:created-at ::ct/inst]
[:data {:optional true} ::sm/any]])
(def schema:permissions-mixin
@@ -304,7 +304,7 @@
(defn get-file-etag
[{:keys [::rpc/profile-id]} {:keys [modified-at revn vern permissions]}]
(str profile-id "/" revn "/" vern "/" (hash fmg/available-migrations) "/"
(dt/format-instant modified-at :iso)
(ct/format-inst modified-at :iso)
"/"
(uri/map->query-string permissions)))
@@ -356,7 +356,7 @@
[:map {:title "FileFragment"}
[:id ::sm/uuid]
[:file-id ::sm/uuid]
[:created-at ::dt/instant]
[:created-at ::ct/inst]
[:content any?]])
(def schema:get-file-fragment
@@ -770,7 +770,7 @@
[conn {:keys [id name]}]
(db/update! conn :file
{:name name
:modified-at (dt/now)}
:modified-at (ct/now)}
{:id id}
{::db/return-keys true}))
@@ -783,8 +783,8 @@
[:id ::sm/uuid]
[:project-id ::sm/uuid]
[:name [:string {:max 250}]]
[:created-at ::dt/instant]
[:modified-at ::dt/instant]]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]]
::sm/params
[:map {:title "RenameFileParams"}
@@ -795,8 +795,8 @@
[:map {:title "SimplifiedFile"}
[:id ::sm/uuid]
[:name [:string {:max 250}]]
[:created-at ::dt/instant]
[:modified-at ::dt/instant]]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]]
::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id] :as params}]
@@ -839,7 +839,7 @@
(db/update! cfg :file
{:revn (inc (:revn file))
:data (blob/encode (:data file))
:modified-at (dt/now)
:modified-at (ct/now)
:has-media-trimmed false}
{:id file-id})
@@ -900,7 +900,7 @@
(db/delete! conn :file-library-rel {:library-file-id id})
(db/update! conn :file
{:is-shared false
:modified-at (dt/now)}
:modified-at (ct/now)}
{:id id})
(select-keys file [:id :name :is-shared]))
@@ -909,7 +909,7 @@
(let [file (assoc file :is-shared true)]
(db/update! conn :file
{:is-shared true
:modified-at (dt/now)}
:modified-at (ct/now)}
{:id id})
file)
@@ -945,7 +945,7 @@
[conn team file-id]
(let [delay (ldel/get-deletion-delay team)
file (db/update! conn :file
{:deleted-at (dt/in-future delay)}
{:deleted-at (ct/in-future delay)}
{:id file-id}
{::db/return-keys [:id :name :is-shared :deleted-at
:project-id :created-at :modified-at]})]
@@ -1043,7 +1043,7 @@
(defn update-sync
[conn {:keys [file-id library-id] :as params}]
(db/update! conn :file-library-rel
{:synced-at (dt/now)}
{:synced-at (ct/now)}
{:file-id file-id
:library-file-id library-id}
{::db/return-keys true}))
@@ -1068,14 +1068,14 @@
[conn {:keys [file-id date] :as params}]
(db/update! conn :file
{:ignore-sync-until date
:modified-at (dt/now)}
:modified-at (ct/now)}
{:id file-id}
{::db/return-keys true}))
(def ^:private schema:ignore-file-library-sync-status
[:map {:title "ignore-file-library-sync-status"}
[:file-id ::sm/uuid]
[:date ::dt/instant]])
[:date ::ct/inst]])
;; TODO: improve naming
(sv/defmethod ::ignore-file-library-sync-status

View File

@@ -9,6 +9,7 @@
[app.binfile.common :as bfc]
[app.common.features :as cfeat]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.file :as ctf]
[app.config :as cf]
[app.db :as db]
@@ -22,7 +23,6 @@
[app.rpc.quotes :as quotes]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set]))
(defn create-file-role!
@@ -63,7 +63,7 @@
(create-file-role! conn))
(db/update! conn :project
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id project-id})
file)))

View File

@@ -11,6 +11,7 @@
[app.common.files.migrations :as fmg]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -26,7 +27,6 @@
[app.storage :as sto]
[app.util.blob :as blob]
[app.util.services :as sv]
[app.util.time :as dt]
[cuerdas.core :as str]))
(defn decode-row
@@ -69,8 +69,8 @@
(defn- generate-snapshot-label
[]
(let [ts (-> (dt/now)
(dt/format-instant)
(let [ts (-> (ct/now)
(ct/format-inst)
(str/replace #"[T:\.]" "-")
(str/rtrim "Z"))]
(str "snapshot-" ts)))
@@ -89,9 +89,9 @@
deleted-at
(cond
(= deleted-at :default)
(dt/plus (dt/now) (cf/get-deletion-delay))
(ct/plus (ct/now) (cf/get-deletion-delay))
(dt/instant? deleted-at)
(ct/inst? deleted-at)
deleted-at
:else
@@ -304,7 +304,7 @@
(defn- delete-file-snapshot!
[conn snapshot-id]
(db/update! conn :file-change
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id snapshot-id}
{::db/return-keys false})
nil)

View File

@@ -10,6 +10,7 @@
[app.common.features :as cfeat]
[app.common.files.changes :as cpc]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -27,7 +28,6 @@
[app.util.blob :as blob]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set]))
;; --- MUTATION COMMAND: create-temp-file
@@ -72,7 +72,7 @@
params
(-> params
(assoc :profile-id profile-id)
(assoc :deleted-at (dt/in-future {:days 1}))
(assoc :deleted-at (ct/in-future {:days 1}))
(assoc :features features))]
(files.create/create-file cfg params)))
@@ -97,7 +97,7 @@
{:id (uuid/next)
:session-id session-id
:profile-id profile-id
:created-at (dt/now)
:created-at (ct/now)
:file-id id
:revn revn
:data nil

View File

@@ -13,6 +13,7 @@
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.thumbnails :as thc]
[app.common.time :as ct]
[app.common.types.shape-tree :as ctt]
[app.config :as cf]
[app.db :as db]
@@ -30,13 +31,12 @@
[app.storage :as sto]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[cuerdas.core :as str]))
;; --- FEATURES
(def long-cache-duration
(dt/duration {:days 7}))
(ct/duration {:days 7}))
;; --- COMMAND QUERY: get-file-object-thumbnails
@@ -247,7 +247,7 @@
(defn- create-file-object-thumbnail!
[{:keys [::sto/storage] :as cfg} file object-id media tag]
(let [file-id (:id file)
timestamp (dt/now)
timestamp (ct/now)
media (persist-thumbnail! storage media timestamp)
[th1 th2] (db/tx-run! cfg (fn [{:keys [::db/conn]}]
(let [th1 (db/exec-one! conn [sql:get-file-object-thumbnail file-id object-id tag])
@@ -302,7 +302,7 @@
{::sql/for-update true})]
(sto/touch-object! storage media-id)
(db/update! conn :file-tagged-object-thumbnail
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:file-id file-id
:object-id object-id
:tag tag})))
@@ -338,7 +338,7 @@
hash (sto/calculate-hash path)
data (-> (sto/content path)
(sto/wrap-with-hash hash))
tnow (dt/now)
tnow (ct/now)
media (sto/put-object! storage
{::sto/content data
::sto/deduplicate? true

View File

@@ -15,6 +15,7 @@
[app.common.files.validate :as val]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -36,7 +37,6 @@
[app.util.blob :as blob]
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.set :as set]
[promesa.exec :as px]))
@@ -123,7 +123,7 @@
[:update-file/global]]
::webhooks/event? true
::webhooks/batch-timeout (dt/duration "2m")
::webhooks/batch-timeout (ct/duration "2m")
::webhooks/batch-key (webhooks/key-fn ::rpc/profile-id :id)
::sm/params schema:update-file
@@ -156,9 +156,9 @@
(assoc :file file)
(assoc :changes changes))
cfg (assoc cfg ::timestamp (dt/now))
cfg (assoc cfg ::timestamp (ct/now))
tpoint (dt/tpoint)]
tpoint (ct/tpoint)]
(when (not= (:vern params)
@@ -199,7 +199,7 @@
(errors/request->context))]
(-> (update-file* cfg params)
(rph/with-defer #(let [elapsed (tpoint)]
(l/trace :hint "update-file" :time (dt/format-duration elapsed))))))))))
(l/trace :hint "update-file" :time (ct/format-duration elapsed))))))))))
(defn- update-file*
"Internal function, part of the update-file process, that encapsulates
@@ -244,8 +244,8 @@
:created-at timestamp
:updated-at timestamp
:deleted-at (if (::snapshot-data file)
(dt/plus timestamp (ldel/get-deletion-delay team))
(dt/plus timestamp (dt/duration {:hours 1})))
(ct/plus timestamp (ldel/get-deletion-delay team))
(ct/plus timestamp (ct/duration {:hours 1})))
:file-id (:id file)
:revn (:revn file)
:version (:version file)
@@ -306,7 +306,7 @@
[{:keys [::db/conn ::timestamp]} file]
(let [;; The timestamp can be nil because this function is also
;; intended to be used outside of this module
modified-at (or timestamp (dt/now))]
modified-at (or timestamp (ct/now))]
(db/update! conn :project
{:modified-at modified-at}
@@ -360,7 +360,7 @@
;; TODO: reuse operations if file is migrated
;; TODO: move encoding to a separated thread
file (if (take-snapshot? file)
(let [tpoint (dt/tpoint)
(let [tpoint (ct/tpoint)
snapshot (-> (:data file)
(feat.fdata/process-pointers deref)
(feat.fdata/process-objects (partial into {}))
@@ -372,7 +372,7 @@
:file-id (str (:id file))
:revn (:revn file)
:label label
:elapsed (dt/format-duration elapsed))
:elapsed (ct/format-duration elapsed))
(-> file
(assoc ::snapshot-data snapshot)
@@ -452,11 +452,11 @@
(when (contains? cf/flags :auto-file-snapshot)
(let [freq (or (cf/get :auto-file-snapshot-every) 20)
timeout (or (cf/get :auto-file-snapshot-timeout)
(dt/duration {:hours 1}))]
(ct/duration {:hours 1}))]
(or (= 1 freq)
(zero? (mod revn freq))
(> (inst-ms (dt/diff modified-at (dt/now)))
(> (inst-ms (ct/diff modified-at (ct/now)))
(inst-ms timeout))))))
(def ^:private sql:lagged-changes
@@ -496,5 +496,5 @@
:file-id (:id file)
:session-id session-id
:revn (:revn file)
:modified-at (dt/now)
:modified-at (ct/now)
:changes lchanges}))))

View File

@@ -9,6 +9,7 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.db.sql :as-alias sql]
@@ -26,7 +27,6 @@
[app.rpc.quotes :as quotes]
[app.storage :as sto]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[promesa.exec :as px]))
@@ -124,7 +124,7 @@
content (-> (sto/content resource)
(sto/wrap-with-hash hash))]
{::sto/content content
::sto/touched-at (dt/now)
::sto/touched-at (ct/now)
::sto/deduplicate? true
:content-type mtype
:bucket "team-font-variant"})))
@@ -217,7 +217,7 @@
{::sql/for-update true})
delay (ldel/get-deletion-delay team)
tnow (dt/in-future delay)]
tnow (ct/in-future delay)]
(teams/check-edition-permissions! (:permissions team))
@@ -261,7 +261,7 @@
(teams/check-edition-permissions! (:permissions team))
(db/update! conn :team-font-variant
{:deleted-at (dt/in-future delay)}
{:deleted-at (ct/in-future delay)}
{:id (:id variant)}
{::db/return-keys false})

View File

@@ -13,6 +13,7 @@
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -28,7 +29,6 @@
[app.setup.templates :as tmpl]
[app.storage.tmp :as tmp]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[promesa.exec :as px]))
@@ -104,7 +104,7 @@
(db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])
(binding [bfc/*state* (volatile! {:index {file-id (uuid/next)}})]
(duplicate-file (assoc cfg ::bfc/timestamp (dt/now))
(duplicate-file (assoc cfg ::bfc/timestamp (ct/now))
(-> params
(assoc :profile-id profile-id)
(assoc :reset-shared-flag true)))))))
@@ -164,7 +164,7 @@
(db/tx-run! cfg (fn [cfg]
;; Defer all constraints
(db/exec-one! cfg ["SET CONSTRAINTS ALL DEFERRED"])
(-> (assoc cfg ::bfc/timestamp (dt/now))
(-> (assoc cfg ::bfc/timestamp (ct/now))
(duplicate-project (assoc params :profile-id profile-id))))))
(defn duplicate-team
@@ -320,7 +320,7 @@
;; trully different modification date to each file.
(px/sleep 10)
(db/update! conn :project
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id project-id}))
nil))
@@ -425,7 +425,7 @@
(db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(db/update! conn :project
{:modified-at (dt/now)}
{:modified-at (ct/now)}
{:id project-id}
{::db/return-keys false})

View File

@@ -10,6 +10,7 @@
[app.common.exceptions :as ex]
[app.common.media :as cm]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -23,7 +24,6 @@
[app.storage :as sto]
[app.storage.tmp :as tmp]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[cuerdas.core :as str]
[datoteka.io :as io]
@@ -67,7 +67,7 @@
mobj (create-file-media-object cfg params)]
(db/update! conn :file
{:modified-at (dt/now)
{:modified-at (ct/now)
:has-media-trimmed false}
{:id file-id}
{::db/return-keys false})
@@ -192,7 +192,7 @@
mobj (create-file-media-object-from-url cfg (assoc params :profile-id profile-id))]
(db/update! pool :file
{:modified-at (dt/now)
{:modified-at (ct/now)
:has-media-trimmed false}
{:id file-id}
{::db/return-keys false})

View File

@@ -10,6 +10,7 @@
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.plugins :refer [schema:plugin-registry]]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -28,7 +29,6 @@
[app.storage :as sto]
[app.tokens :as tokens]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]
[promesa.exec :as px]))
@@ -70,8 +70,8 @@
[:is-blocked {:optional true} ::sm/boolean]
[:is-demo {:optional true} ::sm/boolean]
[:is-muted {:optional true} ::sm/boolean]
[:created-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::sm/inst]
[:created-at {:optional true} ::ct/inst]
[:modified-at {:optional true} ::ct/inst]
[:default-project-id {:optional true} ::sm/uuid]
[:default-team-id {:optional true} ::sm/uuid]
[:props {:optional true} schema:props]])
@@ -352,13 +352,13 @@
[{:keys [::db/conn] :as cfg} {:keys [profile email] :as params}]
(let [token (tokens/generate (::setup/props cfg)
{:iss :change-email
:exp (dt/in-future "15m")
:exp (ct/in-future "15m")
:profile-id (:id profile)
:email email})
ptoken (tokens/generate (::setup/props cfg)
{:iss :profile-identity
:profile-id (:id profile)
:exp (dt/in-future {:days 30})})]
:exp (ct/in-future {:days 30})})]
(when (not= email (:email profile))
(check-profile-existence! conn params))
@@ -444,7 +444,7 @@
::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id] :as params}]
(let [teams (get-owned-teams conn profile-id)
deleted-at (dt/now)]
deleted-at (ct/now)]
;; If we found owned teams with participants, we don't allow
;; delete profile until the user properly transfer ownership or

View File

@@ -9,6 +9,7 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.logical-deletion :as ldel]
@@ -21,7 +22,6 @@
[app.rpc.permissions :as perms]
[app.rpc.quotes :as quotes]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]))
;; --- Check Project Permissions
@@ -218,7 +218,7 @@
(sv/defmethod ::update-project-pin
{::doc/added "1.18"
::sm/params schema:update-project-pin
::webhooks/batch-timeout (dt/duration "5s")
::webhooks/batch-timeout (ct/duration "5s")
::webhooks/batch-key (webhooks/key-fn ::rpc/profile-id :id)
::webhooks/event? true
::db/transaction true}
@@ -257,7 +257,7 @@
[conn team project-id]
(let [delay (ldel/get-deletion-delay team)
project (db/update! conn :project
{:deleted-at (dt/in-future delay)}
{:deleted-at (ct/in-future delay)}
{:id project-id}
{::db/return-keys true})]

View File

@@ -11,6 +11,7 @@
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.team :as tt]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -30,7 +31,6 @@
[app.setup :as-alias setup]
[app.storage :as sto]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.set :as set]))
@@ -666,7 +666,7 @@
(let [delay (ldel/get-deletion-delay team)
team (db/update! conn :team
{:deleted-at (dt/in-future delay)}
{:deleted-at (ct/in-future delay)}
{:id id}
{::db/return-keys true})]

View File

@@ -12,6 +12,7 @@
[app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.team :as types.team]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -29,7 +30,6 @@
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.services :as sv]
[app.util.time :as dt]
[cuerdas.core :as str]))
;; --- Mutation: Create Team Invitation
@@ -62,7 +62,7 @@
(tokens/generate (::setup/props cfg)
{:iss :profile-identity
:profile-id profile-id
:exp (dt/in-future {:days 30})}))
:exp (ct/in-future {:days 30})}))
(def ^:private schema:create-invitation
[:map {:title "params:create-invitation"}
@@ -126,7 +126,7 @@
(teams/check-email-spam conn email true)
(let [id (uuid/next)
expire (dt/in-future "168h") ;; 7 days
expire (ct/in-future "168h") ;; 7 days
invitation (db/exec-one! conn [sql:upsert-team-invitation id
(:id team) (str/lower email)
(:id profile)
@@ -418,7 +418,7 @@
:code :insufficient-permissions))
(db/update! conn :team-invitation
{:role (name role) :updated-at (dt/now)}
{:role (name role) :updated-at (ct/now)}
{:team-id team-id :email-to (profile/clean-email email)})
nil))
@@ -471,7 +471,7 @@
(when-let [request (db/get* conn :team-access-request
{:team-id team-id
:requester-id profile-id})]
(when (dt/is-after? (:valid-until request) (dt/now))
(when (ct/is-after? (:valid-until request) (ct/now))
(ex/raise :type :validation
:code :request-already-sent
:hint "you have already made a request to join this team less than 24 hours ago"))))
@@ -487,8 +487,8 @@
"Create or update team access request for provided team and profile-id"
[conn team-id requester-id]
(check-existing-team-access-request conn team-id requester-id)
(let [valid-until (dt/in-future {:hours 24})
auto-join-until (dt/in-future {:days 7})
(let [valid-until (ct/in-future {:hours 24})
auto-join-until (ct/in-future {:days 7})
request-id (uuid/next)]
(db/exec-one! conn [sql:upsert-team-access-request
request-id team-id requester-id

View File

@@ -8,6 +8,7 @@
(:require
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.team :as types.team]
[app.config :as cf]
[app.db :as db]
@@ -23,8 +24,7 @@
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.tokens.spec.team-invitation :as-alias spec.team-invitation]
[app.util.services :as sv]
[app.util.time :as dt]))
[app.util.services :as sv]))
(defmulti process-token (fn [_ _ claims] (:iss claims)))
@@ -126,7 +126,7 @@
(def schema:team-invitation-claims
[:map {:title "TeamInvitationClaims"}
[:iss :keyword]
[:exp ::dt/instant]
[:exp ::ct/inst]
[:profile-id ::sm/uuid]
[:role ::types.team/role]
[:team-id ::sm/uuid]

View File

@@ -9,6 +9,7 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[app.db :as db]
@@ -19,7 +20,6 @@
[app.rpc.doc :as-alias doc]
[app.rpc.permissions :as perms]
[app.util.services :as sv]
[app.util.time :as dt]
[cuerdas.core :as str]))
(defn get-webhooks-permissions
@@ -54,7 +54,7 @@
(http/req! cfg
{:method :head
:uri (str (:uri params))
:timeout (dt/duration "3s")}
:timeout (ct/duration "3s")}
{:sync? true}))]
(if (ex/exception? response)
(if-let [hint (webhooks/interpret-exception response)]

View File

@@ -10,9 +10,9 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]))
@@ -95,7 +95,7 @@
"- Total: ~(::total params) (INCR ~(::incr params 1))\n")]
(wrk/submit! {::db/conn conn
::wrk/task :sendmail
::wrk/delay (dt/duration "30s")
::wrk/delay (ct/duration "30s")
::wrk/max-retries 4
::wrk/priority 200
::wrk/dedupe true

View File

@@ -47,6 +47,7 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as uri]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -58,7 +59,6 @@
[app.rpc.rlimit.result :as-alias lresult]
[app.util.inet :as inet]
[app.util.services :as-alias sv]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.edn :as edn]
[cuerdas.core :as str]
@@ -67,7 +67,7 @@
[promesa.exec :as px]))
(def ^:private default-timeout
(dt/duration 400))
(ct/duration 400))
(def ^:private default-options
{:codec rds/string-codec
@@ -94,6 +94,10 @@
(defmulti parse-limit (fn [[_ strategy _]] strategy))
(defmulti process-limit (fn [_ _ _ o] (::strategy o)))
(defn- ->seconds
[d]
(-> d inst-ms (/ 1000) int))
(sm/register!
{:type ::rpc/rlimit
:pred #(instance? clojure.lang.Agent %)})
@@ -115,7 +119,7 @@
[:map
[::capacity ::sm/int]
[::rate ::sm/int]
[::internal ::dt/duration]
[::internal ::ct/duration]
[::params [::sm/vec :any]]]
[:map
[::nreq ::sm/int]
@@ -157,7 +161,7 @@
(assert (valid-limit-tuple? vlimit) "expected valid limit tuple")
(if-let [[_ capacity rate interval] (re-find bucket-opts-re opts)]
(let [interval (dt/duration interval)
(let [interval (ct/duration interval)
rate (parse-long rate)
capacity (parse-long capacity)]
{::name name
@@ -166,7 +170,7 @@
::rate rate
::interval interval
::opts opts
::params [(dt/->seconds interval) rate capacity]
::params [(->seconds interval) rate capacity]
::key (str "ratelimit.bucket." (d/name name))})
(ex/raise :type :validation
:code :invalid-bucket-limit-opts
@@ -176,7 +180,7 @@
[redis user-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}]
(let [script (-> bucket-rate-limit-script
(assoc ::rscript/keys [(str key "." service "." user-id)])
(assoc ::rscript/vals (conj params (dt/->seconds now))))
(assoc ::rscript/vals (conj params (->seconds now))))
result (rds/eval redis script)
allowed? (boolean (nth result 0))
remaining (nth result 1)
@@ -191,16 +195,16 @@
:remaining remaining)
(-> limit
(assoc ::lresult/allowed allowed?)
(assoc ::lresult/reset (dt/plus now reset))
(assoc ::lresult/reset (ct/plus now reset))
(assoc ::lresult/remaining remaining))))
(defmethod process-limit :window
[redis user-id now {:keys [::nreq ::unit ::key ::service] :as limit}]
(let [ts (dt/truncate now unit)
ttl (dt/diff now (dt/plus ts {unit 1}))
(let [ts (ct/truncate now unit)
ttl (ct/diff now (ct/plus ts {unit 1}))
script (-> window-rate-limit-script
(assoc ::rscript/keys [(str key "." service "." user-id "." (dt/format-instant ts))])
(assoc ::rscript/vals [nreq (dt/->seconds ttl)]))
(assoc ::rscript/keys [(str key "." service "." user-id "." (ct/format-inst ts))])
(assoc ::rscript/vals [nreq (->seconds ttl)]))
result (rds/eval redis script)
allowed? (boolean (nth result 0))
remaining (nth result 1)]
@@ -214,7 +218,7 @@
(-> limit
(assoc ::lresult/allowed allowed?)
(assoc ::lresult/remaining remaining)
(assoc ::lresult/reset (dt/plus ts {unit 1})))))
(assoc ::lresult/reset (ct/plus ts {unit 1})))))
(defn- process-limits!
[redis user-id limits now]
@@ -223,7 +227,7 @@
(d/index-by ::name ::lresult/remaining)
(uri/map->query-string))
reset (->> results
(d/index-by ::name (comp dt/->seconds ::lresult/reset))
(d/index-by ::name (comp ->seconds ::lresult/reset))
(uri/map->query-string))
rejected (d/seek (complement ::lresult/allowed) results)]
@@ -261,7 +265,7 @@
(let [redis (rds/get-or-connect redis ::rpc/rlimit default-options)
uid (get-uid params)
;; FIXME: why not clasic try/catch?
result (ex/try! (process-limits! redis uid limits (dt/now)))]
result (ex/try! (process-limits! redis uid limits (ct/now)))]
(l/trc :hint "process-limits"
:service sname
@@ -321,7 +325,7 @@
(sm/check-fn schema:config))
(def ^:private check-refresh
(sm/check-fn ::dt/duration))
(sm/check-fn ::ct/duration))
(def ^:private check-limits
(sm/check-fn schema:limits))
@@ -351,7 +355,7 @@
config)))]
(when-let [config (some->> path slurp edn/read-string check-config)]
(let [refresh (->> config meta :refresh dt/duration check-refresh)
(let [refresh (->> config meta :refresh ct/duration check-refresh)
limits (->> config compile-pass-1 compile-pass-2 check-limits)]
{::refresh refresh
@@ -410,7 +414,7 @@
(l/info :hint "initializing rlimit config reader" :path (str path))
;; Initialize the state with initial refresh value
(send-via executor state (constantly {::refresh (dt/duration "5s")}))
(send-via executor state (constantly {::refresh (ct/duration "5s")}))
;; Force a refresh
(refresh-config (assoc cfg ::path path ::state state)))

View File

@@ -11,11 +11,11 @@
[app.common.exceptions :as ex]
[app.common.json :as json]
[app.common.logging :as l]
[app.common.time :as ct]
[app.config :as cf]
[app.srepl.cli :as cli]
[app.srepl.main]
[app.util.locks :as locks]
[app.util.time :as dt]
[clojure.core :as c]
[clojure.core.server :as ccs]
[clojure.main :as cm]
@@ -77,7 +77,7 @@
(loop []
(when (try
(let [data (read-line)
tpoint (dt/tpoint)]
tpoint (ct/tpoint)]
(l/dbg :hint "received" :data (if (= data ::eof) "EOF" data))

View File

@@ -10,13 +10,14 @@
[app.auth :as auth]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.rpc.commands.auth :as cmd.auth]
[app.rpc.commands.profile :as cmd.profile]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.time :as dt]
[cuerdas.core :as str]))
(defn coercer
@@ -101,7 +102,7 @@
(fn [{:keys [::db/conn] :as system}]
(let [res (if soft
(db/update! conn :profile
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:email email :deleted-at nil})
(db/delete! conn :profile
{:email email}))]
@@ -173,6 +174,21 @@
:num-editors (get-customer-slots system id)
:subscription (get props :subscription)})))
(def ^:private schema:timestamp
(sm/type-schema
{:type ::timestamp
:pred ct/inst?
:type-properties
{:title "inst"
:description "The same as :app.common.time/inst but encodes to epoch"
:error/message "should be an instant"
:gen/gen (->> (sg/small-int)
(sg/fmap (fn [v] (ct/inst v))))
:decode/string ct/inst
:encode/string inst-ms
:decode/json ct/inst
:encode/json inst-ms}}))
(def ^:private schema:customer-subscription
[:map {:title "CustomerSubscription"}
[:id ::sm/text]
@@ -198,15 +214,15 @@
"year"]]
[:quantity :int]
[:description [:maybe ::sm/text]]
[:created-at ::sm/timestamp]
[:start-date [:maybe ::sm/timestamp]]
[:ended-at [:maybe ::sm/timestamp]]
[:trial-end [:maybe ::sm/timestamp]]
[:trial-start [:maybe ::sm/timestamp]]
[:cancel-at [:maybe ::sm/timestamp]]
[:canceled-at [:maybe ::sm/timestamp]]
[:current-period-end [:maybe ::sm/timestamp]]
[:current-period-start [:maybe ::sm/timestamp]]
[:created-at schema:timestamp]
[:start-date [:maybe schema:timestamp]]
[:ended-at [:maybe schema:timestamp]]
[:trial-end [:maybe schema:timestamp]]
[:trial-start [:maybe schema:timestamp]]
[:cancel-at [:maybe schema:timestamp]]
[:canceled-at [:maybe schema:timestamp]]
[:current-period-end [:maybe schema:timestamp]]
[:current-period-start [:maybe schema:timestamp]]
[:cancel-at-period-end :boolean]
[:cancellation-details

View File

@@ -12,11 +12,11 @@
[app.common.data :as d]
[app.common.files.migrations :as fmg]
[app.common.files.validate :as cfv]
[app.common.time :as ct]
[app.db :as db]
[app.main :as main]
[app.rpc.commands.files :as files]
[app.rpc.commands.files-snapshot :as fsnap]
[app.util.time :as dt]))
[app.rpc.commands.files-snapshot :as fsnap]))
(def ^:dynamic *system* nil)
@@ -165,7 +165,7 @@
(when (string? label)
(fsnap/create-file-snapshot! system file
{:label label
:deleted-at (dt/in-future {:days 30})
:deleted-at (ct/in-future {:days 30})
:created-by :admin}))
(let [file' (update file' :revn inc)]

View File

@@ -19,6 +19,7 @@
[app.common.pprint :as p]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -38,7 +39,6 @@
[app.srepl.helpers :as h]
[app.util.blob :as blob]
[app.util.pointer-map :as pmap]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.java.io :as io]
[clojure.pprint :refer [print-table]]
@@ -476,7 +476,7 @@
:max-jobs max-jobs
:max-items max-items)
(let [tpoint (dt/tpoint)
(let [tpoint (ct/tpoint)
factory (px/thread-factory :virtual false :prefix "penpot/file-process/")
executor (px/cached-executor :factory factory)
sjobs (ps/create :permits max-jobs)
@@ -506,7 +506,7 @@
(Thread/sleep (int pause)))
(ps/release! sjobs)
(let [elapsed (dt/format-duration (tpoint))]
(let [elapsed (ct/format-duration (tpoint))]
(l/trc :hint "process:file:end"
:tid thread-id
:file-id (str file-id)
@@ -516,7 +516,7 @@
process-file*
(fn [idx file-id]
(ps/acquire! sjobs)
(px/run! executor (partial process-file file-id idx (dt/tpoint)))
(px/run! executor (partial process-file file-id idx (ct/tpoint)))
(inc idx))
process-files
@@ -542,7 +542,7 @@
(l/dbg :hint "process:error" :cause cause))
(finally
(let [elapsed (dt/format-duration (tpoint))]
(let [elapsed (ct/format-duration (tpoint))]
(l/dbg :hint "process:end"
:rollback rollback?
:elapsed elapsed))))))
@@ -556,7 +556,7 @@
"Mark a project for deletion"
[file-id]
(let [file-id (h/parse-uuid file-id)
tnow (dt/now)]
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-file"
@@ -618,7 +618,7 @@
::audit/props file
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-file!"}
::audit/tracked-at (dt/now)})
::audit/tracked-at (ct/now)})
(restore-file* system file-id))))))
@@ -626,7 +626,7 @@
"Mark a project for deletion"
[project-id]
(let [project-id (h/parse-uuid project-id)
tnow (dt/now)]
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-project"
@@ -673,7 +673,7 @@
::audit/props project
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}
::audit/tracked-at (dt/now)})
::audit/tracked-at (ct/now)})
(restore-project* system project-id))))))
@@ -681,7 +681,7 @@
"Mark a team for deletion"
[team-id]
(let [team-id (h/parse-uuid team-id)
tnow (dt/now)]
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-team"
@@ -733,7 +733,7 @@
::audit/props team
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-team!"}
::audit/tracked-at (dt/now)})
::audit/tracked-at (ct/now)})
(restore-team* system team-id))))))
@@ -741,7 +741,7 @@
"Mark a profile for deletion."
[profile-id]
(let [profile-id (h/parse-uuid profile-id)
tnow (dt/now)]
tnow (ct/now)]
(audit/insert! main/system
{::audit/name "delete-profile"
@@ -775,7 +775,7 @@
::audit/props (audit/profile->props profile)
::audit/context {:triggered-by "srepl"
:cause "explicit call to restore-profile!"}
::audit/tracked-at (dt/now)})
::audit/tracked-at (ct/now)})
(db/update! system :profile
{:deleted-at nil}
@@ -821,7 +821,7 @@
{:deleted deleted :total total})))]
(let [path (fs/path path)
deleted-at (dt/minus (dt/now) (cf/get-deletion-delay))]
deleted-at (ct/minus (ct/now) (cf/get-deletion-delay))]
(when-not (fs/exists? path)
(throw (ex-info "path does not exists" {:path path})))
@@ -905,7 +905,7 @@
(db/tx-run! main/system
(fn [{:keys [::db/conn] :as cfg}]
(db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])
(let [team (-> (assoc cfg ::bfc/timestamp (dt/now))
(let [team (-> (assoc cfg ::bfc/timestamp (ct/now))
(mgmt/duplicate-team :team-id team-id :name name))
rels (db/query conn :team-profile-rel {:team-id team-id})]

View File

@@ -12,13 +12,13 @@
[app.common.data.macros :as dm]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.storage.fs :as sfs]
[app.storage.impl :as impl]
[app.storage.s3 :as ss3]
[app.util.time :as dt]
[cuerdas.core :as str]
[datoteka.fs :as fs]
[integrant.core :as ig])
@@ -122,7 +122,7 @@
(dissoc :id))
touched-at (if touch
(or touched-at (dt/now))
(or touched-at (ct/now))
touched-at)
;; NOTE: for now we don't reuse the deleted objects, but in
@@ -224,7 +224,7 @@
(assert (valid-storage? storage))
(let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id)]
(-> (db/update! connectable :storage-object
{:touched-at (dt/now)}
{:touched-at (ct/now)}
{:id id})
(db/get-update-count)
(pos?))))
@@ -235,7 +235,7 @@
[storage object]
(assert (valid-storage? storage))
(when (or (nil? (:expired-at object))
(dt/is-after? (:expired-at object) (dt/now)))
(ct/is-after? (:expired-at object) (ct/now)))
(-> (impl/resolve-backend storage (:backend object))
(impl/get-object-data object))))
@@ -244,7 +244,7 @@
[storage object]
(assert (valid-storage? storage))
(when (or (nil? (:expired-at object))
(dt/is-after? (:expired-at object) (dt/now)))
(ct/is-after? (:expired-at object) (ct/now)))
(-> (impl/resolve-backend storage (:backend object))
(impl/get-object-bytes object))))
@@ -254,7 +254,7 @@
([storage object options]
(assert (valid-storage? storage))
(when (or (nil? (:expired-at object))
(dt/is-after? (:expired-at object) (dt/now)))
(ct/is-after? (:expired-at object) (ct/now)))
(-> (impl/resolve-backend storage (:backend object))
(impl/get-object-url object options)))))
@@ -266,7 +266,7 @@
(let [backend (impl/resolve-backend storage (:backend object))]
(when (and (= :fs (::type backend))
(or (nil? (:expired-at object))
(dt/is-after? (:expired-at object) (dt/now))))
(ct/is-after? (:expired-at object) (ct/now))))
(-> (impl/get-object-url backend object nil) file-url->path))))
(defn del-object!
@@ -274,7 +274,7 @@
(assert (valid-storage? storage))
(let [id (if (impl/object? object-or-id) (:id object-or-id) object-or-id)
res (db/update! connectable :storage-object
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id id})]
(pos? (db/get-update-count res))))

View File

@@ -15,10 +15,10 @@
(:require
[app.common.data :as d]
[app.common.logging :as l]
[app.common.time :as ct]
[app.db :as db]
[app.storage :as sto]
[app.storage.impl :as impl]
[app.util.time :as dt]
[integrant.core :as ig]))
(def ^:private sql:lock-sobjects
@@ -106,18 +106,18 @@
(defmethod ig/expand-key ::handler
[k v]
{k (assoc v ::min-age (dt/duration {:hours 2}))})
{k (assoc v ::min-age (ct/duration {:hours 2}))})
(defmethod ig/init-key ::handler
[_ {:keys [::min-age] :as cfg}]
(fn [{:keys [props] :as task}]
(let [min-age (dt/duration (or (:min-age props) min-age))]
(let [min-age (ct/duration (or (:min-age props) min-age))]
(db/tx-run! cfg (fn [cfg]
(let [cfg (assoc cfg ::min-age min-age)
total (clean-deleted! cfg)]
(l/inf :hint "task finished"
:min-age (dt/format-duration min-age)
:min-age (ct/format-duration min-age)
:total total)
{:deleted total}))))))

View File

@@ -12,11 +12,11 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.storage :as-alias sto]
[app.storage.impl :as impl]
[app.storage.tmp :as tmp]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[clojure.java.io :as io]
[datoteka.fs :as fs]
@@ -69,7 +69,7 @@
20000)
(def default-timeout
(dt/duration {:seconds 30}))
(ct/duration {:seconds 30}))
(declare put-object)
(declare get-object-bytes)
@@ -338,11 +338,11 @@
(p/fmap #(.asByteArray ^ResponseBytes %)))))
(def default-max-age
(dt/duration {:minutes 10}))
(ct/duration {:minutes 10}))
(defn- get-object-url
[{:keys [::presigner ::bucket ::prefix]} {:keys [id]} {:keys [max-age] :or {max-age default-max-age}}]
(assert (dt/duration? max-age) "expected valid duration instance")
(assert (ct/duration? max-age) "expected valid duration instance")
(let [gor (.. (GetObjectRequest/builder)
(bucket bucket)

View File

@@ -12,8 +12,8 @@
(:require
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.util.time :as dt]
[app.worker :as wrk]
[datoteka.fs :as fs]
[datoteka.io :as io]
@@ -38,7 +38,7 @@
(defmethod ig/expand-key ::cleaner
[k v]
{k (assoc v ::min-age (dt/duration "60m"))})
{k (assoc v ::min-age (ct/duration "60m"))})
(defmethod ig/init-key ::cleaner
[_ cfg]
@@ -52,13 +52,13 @@
(defn- io-loop
[{:keys [::min-age] :as cfg}]
(l/inf :hint "started tmp cleaner" :default-min-age (dt/format-duration min-age))
(l/inf :hint "started tmp cleaner" :default-min-age (ct/format-duration min-age))
(try
(loop []
(when-let [[path min-age'] (sp/take! queue)]
(let [min-age (or min-age' min-age)]
(l/dbg :hint "schedule tempfile deletion" :path path
:expires-at (dt/plus (dt/now) min-age))
:expires-at (ct/plus (ct/now) min-age))
(px/schedule! (inst-ms min-age) (partial remove-temp-file cfg path))
(recur))))
(catch InterruptedException _
@@ -87,7 +87,7 @@
path (fs/join default-tmp-dir (str prefix (uuid/next) suffix))
path (Files/createFile path attrs)]
(fs/delete-on-exit! path)
(sp/offer! queue [path (some-> min-age dt/duration)])
(sp/offer! queue [path (some-> min-age ct/duration)])
path))
(defn tempfile-from

View File

@@ -8,10 +8,10 @@
"A generic task for object deletion cascade handling"
(:require
[app.common.logging :as l]
[app.common.time :as ct]
[app.db :as db]
[app.rpc.commands.files :as files]
[app.rpc.commands.profile :as profile]
[app.util.time :as dt]
[integrant.core :as ig]))
(def ^:dynamic *team-deletion* false)
@@ -23,7 +23,7 @@
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}]
(when-let [file (db/get* conn :file {:id id} {::db/remove-deleted false})]
(l/trc :hint "marking for deletion" :rel "file" :id (str id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(db/update! conn :file
{:deleted-at deleted-at}
@@ -62,7 +62,7 @@
(defmethod delete-object :project
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}]
(l/trc :hint "marking for deletion" :rel "project" :id (str id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(db/update! conn :project
{:deleted-at deleted-at}
@@ -79,7 +79,7 @@
(defmethod delete-object :team
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}]
(l/trc :hint "marking for deletion" :rel "team" :id (str id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(db/update! conn :team
{:deleted-at deleted-at}
{:id id}
@@ -101,7 +101,7 @@
(defmethod delete-object :profile
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}]
(l/trc :hint "marking for deletion" :rel "profile" :id (str id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(db/update! conn :profile
{:deleted-at deleted-at}

View File

@@ -16,6 +16,7 @@
[app.common.files.validate :as cfv]
[app.common.logging :as l]
[app.common.thumbnails :as thc]
[app.common.time :as ct]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctt]
@@ -23,7 +24,6 @@
[app.db :as db]
[app.features.fdata :as feat.fdata]
[app.storage :as sto]
[app.util.time :as dt]
[app.worker :as wrk]
[integrant.core :as ig]))
@@ -282,7 +282,7 @@
(defmethod ig/init-key ::handler
[_ cfg]
(fn [{:keys [props] :as task}]
(let [min-age (dt/duration (or (:min-age props)
(let [min-age (ct/duration (or (:min-age props)
(cf/get-deletion-delay)))
file-id (get props :file-id)
cfg (-> cfg

View File

@@ -8,9 +8,9 @@
"A maintenance task that is responsible of properly scheduling the
file-gc task for all files that matches the eligibility threshold."
(:require
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.util.time :as dt]
[app.worker :as wrk]
[integrant.core :as ig]))
@@ -53,7 +53,7 @@
(defmethod ig/init-key ::handler
[_ cfg]
(fn [{:keys [props] :as task}]
(let [min-age (dt/duration (or (:min-age props) (::min-age cfg)))]
(let [min-age (ct/duration (or (:min-age props) (::min-age cfg)))]
(-> cfg
(assoc ::db/rollback (:rollback? props))
(assoc ::min-age min-age)

View File

@@ -9,9 +9,9 @@
of deleted or unreachable objects."
(:require
[app.common.logging :as l]
[app.common.time :as ct]
[app.db :as db]
[app.storage :as sto]
[app.util.time :as dt]
[integrant.core :as ig]))
(def ^:private sql:get-profiles
@@ -53,7 +53,7 @@
(l/trc :hint "permanently delete"
:rel "team"
:id (str id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; Mark as deleted the storage object
(some->> photo-id (sto/touch-object! storage))
@@ -82,7 +82,7 @@
:rel "team-font-variant"
:id (str id)
:team-id (str team-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; Mark as deleted the all related storage objects
(some->> (:woff1-file-id font) (sto/touch-object! storage))
@@ -114,7 +114,7 @@
:rel "project"
:id (str id)
:team-id (str team-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; And finally, permanently delete the project.
(db/delete! conn :project {:id id})
@@ -140,7 +140,7 @@
:rel "file"
:id (str id)
:project-id (str project-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(when (= "objects-storage" (:data-backend file))
(sto/touch-object! storage (:data-ref-id file)))
@@ -169,7 +169,7 @@
:rel "file-thumbnail"
:file-id (str file-id)
:revn revn
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; Mark as deleted the storage object
(some->> media-id (sto/touch-object! storage))
@@ -198,7 +198,7 @@
:rel "file-tagged-object-thumbnail"
:file-id (str file-id)
:object-id object-id
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; Mark as deleted the storage object
(some->> media-id (sto/touch-object! storage))
@@ -227,7 +227,7 @@
:rel "file-data-fragment"
:id (str id)
:file-id (str file-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(some->> data-ref-id (sto/touch-object! storage))
(db/delete! conn :file-data-fragment {:file-id file-id :id id})
@@ -253,7 +253,7 @@
:rel "file-media-object"
:id (str id)
:file-id (str file-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
;; Mark as deleted the all related storage objects
(some->> (:media-id fmo) (sto/touch-object! storage))
@@ -282,7 +282,7 @@
:rel "file-change"
:id (str id)
:file-id (str file-id)
:deleted-at (dt/format-instant deleted-at))
:deleted-at (ct/format-inst deleted-at))
(when (= "objects-storage" (:data-backend xlog))
(sto/touch-object! storage (:data-ref-id xlog)))
@@ -328,7 +328,7 @@
(defmethod ig/init-key ::handler
[_ cfg]
(fn [{:keys [props] :as task}]
(let [threshold (dt/duration (get props :deletion-threshold 0))
(let [threshold (ct/duration (get props :deletion-threshold 0))
cfg (assoc cfg ::deletion-threshold (db/interval threshold))]
(loop [procs (map deref deletion-proc-vars)
total 0]

View File

@@ -10,8 +10,8 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.time :as ct]
[app.common.transit :as t]
[app.util.time :as dt]
[buddy.sign.jwe :as jwe]))
(defn generate
@@ -22,7 +22,7 @@
(bytes? tokens-key))
(let [payload (-> claims
(assoc :iat (dt/now))
(assoc :iat (ct/now))
(d/without-nils)
(t/encode))]
(jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm})))
@@ -35,8 +35,8 @@
(defn verify
[sprops {:keys [token] :as params}]
(let [claims (decode sprops token)]
(when (and (dt/instant? (:exp claims))
(dt/is-before? (:exp claims) (dt/now)))
(when (and (ct/inst? (:exp claims))
(ct/is-before? (:exp claims) (ct/now)))
(ex/raise :type :validation
:code :invalid-token
:reason :token-expired

View File

@@ -9,7 +9,7 @@
(:refer-clojure :exclude [get])
(:require
[app.common.schema :as sm]
[app.util.time :as dt]
[app.common.time :as ct]
[promesa.exec :as px])
(:import
com.github.benmanes.caffeine.cache.AsyncCache
@@ -51,7 +51,7 @@
(let [cache (as-> (Caffeine/newBuilder) builder
(if (fn? on-remove) (.removalListener builder (create-listener on-remove)) builder)
(if executor (.executor builder ^Executor (px/resolve-executor executor)) builder)
(if keepalive (.expireAfterAccess builder ^Duration (dt/duration keepalive)) builder)
(if keepalive (.expireAfterAccess builder ^Duration (ct/duration keepalive)) builder)
(if (int? max-size) (.maximumSize builder (long max-size)) builder)
(.recordStats builder)
(.buildAsync builder))

View File

@@ -0,0 +1,138 @@
;; 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.util.cron
(:require
[app.common.exceptions :as ex])
(:import
java.time.Instant
java.util.Date
org.apache.logging.log4j.core.util.CronExpression))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cron Expression
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cron expressions are comprised of 6 required fields and one
;; optional field separated by white space. The fields respectively
;; are described as follows:
;;
;; Field Name Allowed Values Allowed Special Characters
;; Seconds 0-59 , - * /
;; Minutes 0-59 , - * /
;; Hours 0-23 , - * /
;; Day-of-month 1-31 , - * ? / L W
;; Month 0-11 or JAN-DEC , - * /
;; Day-of-Week 1-7 or SUN-SAT , - * ? / L #
;; Year (Optional) empty, 1970-2199 , - * /
;;
;; The '*' character is used to specify all values. For example, "*"
;; in the minute field means "every minute".
;;
;; The '?' character is allowed for the day-of-month and day-of-week
;; fields. It is used to specify 'no specific value'. This is useful
;; when you need to specify something in one of the two fields, but
;; not the other.
;;
;; The '-' character is used to specify ranges For example "10-12" in
;; the hour field means "the hours 10, 11 and 12".
;;
;; The ',' character is used to specify additional values. For
;; example "MON,WED,FRI" in the day-of-week field means "the days
;; Monday, Wednesday, and Friday".
;;
;; The '/' character is used to specify increments. For example "0/15"
;; in the seconds field means "the seconds 0, 15, 30, and
;; 45". And "5/15" in the seconds field means "the seconds 5, 20, 35,
;; and 50". Specifying '*' before the '/' is equivalent to specifying
;; 0 is the value to start with. Essentially, for each field in the
;; expression, there is a set of numbers that can be turned on or
;; off. For seconds and minutes, the numbers range from 0 to 59. For
;; hours 0 to 23, for days of the month 0 to 31, and for months 0 to
;; 11 (JAN to DEC). The "/" character simply helps you turn on
;; every "nth" value in the given set. Thus "7/6" in the month field
;; only turns on month "7", it does NOT mean every 6th month, please
;; note that subtlety.
;;
;; The 'L' character is allowed for the day-of-month and day-of-week
;; fields. This character is short-hand for "last", but it has
;; different meaning in each of the two fields. For example, the
;; value "L" in the day-of-month field means "the last day of the
;; month" - day 31 for January, day 28 for February on non-leap
;; years. If used in the day-of-week field by itself, it simply
;; means "7" or "SAT". But if used in the day-of-week field after
;; another value, it means "the last xxx day of the month" - for
;; example "6L" means "the last friday of the month". You can also
;; specify an offset from the last day of the month, such as "L-3"
;; which would mean the third-to-last day of the calendar month. When
;; using the 'L' option, it is important not to specify lists, or
;; ranges of values, as you'll get confusing/unexpected results.
;;
;; The 'W' character is allowed for the day-of-month field. This
;; character is used to specify the weekday (Monday-Friday) nearest
;; the given day. As an example, if you were to specify "15W" as the
;; value for the day-of-month field, the meaning is: "the nearest
;; weekday to the 15th of the month". So if the 15th is a Saturday,
;; the trigger will fire on Friday the 14th. If the 15th is a Sunday,
;; the trigger will fire on Monday the 16th. If the 15th is a Tuesday,
;; then it will fire on Tuesday the 15th. However if you specify "1W"
;; as the value for day-of-month, and the 1st is a Saturday, the
;; trigger will fire on Monday the 3rd, as it will not 'jump' over the
;; boundary of a month's days. The 'W' character can only be specified
;; when the day-of-month is a single day, not a range or list of days.
;;
;; The 'L' and 'W' characters can also be combined for the
;; day-of-month expression to yield 'LW', which translates to "last
;; weekday of the month".
;;
;; The '#' character is allowed for the day-of-week field. This
;; character is used to specify "the nth" XXX day of the month. For
;; example, the value of "6#3" in the day-of-week field means the
;; third Friday of the month (day 6 = Friday and "#3" = the 3rd one in
;; the month). Other examples: "2#1" = the first Monday of the month
;; and "4#5" = the fifth Wednesday of the month. Note that if you
;; specify "#5" and there is not 5 of the given day-of-week in the
;; month, then no firing will occur that month. If the '#' character
;; is used, there can only be one expression in the day-of-week
;; field ("3#1,6#3" is not valid, since there are two expressions).
;;
;; The legal characters and the names of months and days of the week
;; are not case sensitive.
(defn cron
"Creates an instance of CronExpression from string."
[s]
(try
(CronExpression. s)
(catch java.text.ParseException e
(ex/raise :type :parse
:code :invalid-cron-expression
:cause e
:context {:expr s}))))
(defn cron-expr?
[v]
(instance? CronExpression v))
(defn next-valid-instant-from
[^CronExpression cron ^Instant now]
(assert (cron-expr? cron))
(.toInstant (.getNextValidTimeAfter cron (Date/from now))))
(defn get-next
[cron tnow]
(let [nt (next-valid-instant-from cron tnow)]
(cons nt (lazy-seq (get-next cron nt)))))
(defmethod print-method CronExpression
[o w]
(print-dup o w))
(defmethod print-dup CronExpression
[mv ^java.io.Writer writer]
;; Do not delete this comment
;; (print-ctor o (fn [o w] (print-dup (.toString ^CronExpression o) w)) w)
(.write writer (str "#penpot/cron \"" (.toString ^CronExpression mv) "\"")))

View File

@@ -37,9 +37,9 @@
(:require
[app.common.fressian :as fres]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.util.time :as dt]
[clojure.core :as c]
[clojure.data.json :as json])
(:import
@@ -104,7 +104,7 @@
(clone [this]
(when-not loaded? (load! this))
(let [mdata (assoc mdata :created-at (dt/now))
(let [mdata (assoc mdata :created-at (ct/now))
id (uuid/next)
pmap (PointerMap. id
mdata
@@ -179,7 +179,7 @@
(let [odata' (assoc odata key val)]
(if (identical? odata odata')
this
(let [mdata (assoc mdata :created-at (dt/now))
(let [mdata (assoc mdata :created-at (ct/now))
id (if modified? id (uuid/next))
pmap (PointerMap. id
mdata
@@ -197,7 +197,7 @@
(let [odata' (dissoc odata key)]
(if (identical? odata odata')
this
(let [mdata (assoc mdata :created-at (dt/now))
(let [mdata (assoc mdata :created-at (ct/now))
id (if modified? id (uuid/next))
pmap (PointerMap. id
mdata
@@ -220,7 +220,7 @@
(defn create
([]
(let [id (uuid/next)
mdata (assoc *metadata* :created-at (dt/now))
mdata (assoc *metadata* :created-at (ct/now))
pmap (PointerMap. id mdata {} true true)]
(some-> *tracked* (swap! assoc id pmap))
pmap))
@@ -239,7 +239,7 @@
(do
(some-> *tracked* (swap! assoc (get-id data) data))
data)
(let [mdata (assoc (meta data) :created-at (dt/now))
(let [mdata (assoc (meta data) :created-at (ct/now))
id (uuid/next)
pmap (PointerMap. id
mdata

View File

@@ -1,399 +0,0 @@
;; 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.util.time
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.schema.openapi :as-alias oapi]
[app.common.time :as common-time]
[clojure.spec.alpha :as s]
[clojure.test.check.generators :as tgen]
[cuerdas.core :as str]
[fipp.ednize :as fez])
(:import
java.nio.file.attribute.FileTime
java.time.Duration
java.time.Instant
java.time.OffsetDateTime
java.time.ZoneId
java.time.ZonedDateTime
java.time.format.DateTimeFormatter
java.time.temporal.ChronoUnit
java.time.temporal.Temporal
java.time.temporal.TemporalAmount
java.time.temporal.TemporalUnit
java.util.Date
org.apache.logging.log4j.core.util.CronExpression))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Instant & Duration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn temporal-unit
[o]
(if (instance? TemporalUnit o)
o
(case o
:nanos ChronoUnit/NANOS
:millis ChronoUnit/MILLIS
:micros ChronoUnit/MICROS
:seconds ChronoUnit/SECONDS
:minutes ChronoUnit/MINUTES
:hours ChronoUnit/HOURS
:days ChronoUnit/DAYS)))
;; --- DURATION
(defn- obj->duration
[params]
(reduce-kv (fn [o k v]
(.plus ^Duration o ^long v ^TemporalUnit (temporal-unit k)))
(Duration/ofMillis 0)
params))
(defn duration?
[v]
(instance? Duration v))
(defn duration
[ms-or-obj]
(cond
(string? ms-or-obj)
(Duration/parse (str "PT" ms-or-obj))
(duration? ms-or-obj)
ms-or-obj
(integer? ms-or-obj)
(Duration/ofMillis ms-or-obj)
:else
(obj->duration ms-or-obj)))
(defn ->seconds
[d]
(-> d inst-ms (/ 1000) int))
(defn diff
[t1 t2]
(Duration/between t1 t2))
(defn truncate
[o unit]
(let [unit (temporal-unit unit)]
(cond
(instance? Instant o)
(.truncatedTo ^Instant o ^TemporalUnit unit)
(instance? Duration o)
(.truncatedTo ^Duration o ^TemporalUnit unit)
:else
(throw (IllegalArgumentException. "only instant and duration allowed")))))
(s/def ::duration
(s/conformer
(fn [v]
(cond
(duration? v) v
(string? v)
(try
(duration v)
(catch java.time.format.DateTimeParseException _e
::s/invalid))
:else
::s/invalid))
(fn [v]
(subs (str v) 2))))
(extend-protocol clojure.core/Inst
java.time.Duration
(inst-ms* [v] (.toMillis ^Duration v))
OffsetDateTime
(inst-ms* [v] (.toEpochMilli (.toInstant ^OffsetDateTime v)))
FileTime
(inst-ms* [v] (.toMillis ^FileTime v)))
(defmethod print-method Duration
[mv ^java.io.Writer writer]
(.write writer (str "#app/duration \"" (str/lower (subs (str mv) 2)) "\"")))
(defmethod print-dup Duration [o w]
(print-method o w))
(extend-protocol fez/IEdn
Duration
(-edn [o]
(tagged-literal 'app/duration (str o))))
(defn format-duration
[o]
(str/lower (subs (str o) 2)))
;; --- INSTANT
(defn instant?
[v]
(instance? Instant v))
(defn instant
([s]
(cond
(instant? s) s
(int? s) (Instant/ofEpochMilli s)
:else (Instant/parse s)))
([s fmt]
(case fmt
:rfc1123 (Instant/from (.parse DateTimeFormatter/RFC_1123_DATE_TIME ^String s))
:iso (Instant/from (.parse DateTimeFormatter/ISO_INSTANT ^String s))
:iso8601 (Instant/from (.parse DateTimeFormatter/ISO_INSTANT ^String s)))))
(defn is-after?
"Analgous to: da > db"
[da db]
(.isAfter ^Instant da ^Instant db))
(defn is-before?
[da db]
(.isBefore ^Instant da ^Instant db))
(defn plus
[d ta]
(let [^TemporalAmount ta (duration ta)]
(cond
(instance? Duration d)
(.plus ^Duration d ta)
(instance? Temporal d)
(.plus ^Temporal d ta)
:else
(throw (UnsupportedOperationException. "unsupported type")))))
(defn minus
[d ta]
(let [^TemporalAmount ta (duration ta)]
(cond
(instance? Duration d)
(.minus ^Duration d ta)
(instance? Temporal d)
(.minus ^Temporal d ta)
:else
(throw (UnsupportedOperationException. "unsupported type")))))
(dm/export common-time/now)
(defn in-future
[v]
(plus (now) v))
(defn in-past
[v]
(minus (now) v))
(defn instant->zoned-date-time
[v]
(ZonedDateTime/ofInstant v (ZoneId/of "UTC")))
(defn format-instant
([v] (.format DateTimeFormatter/ISO_INSTANT ^Instant v))
([v fmt]
(case fmt
:iso
(.format DateTimeFormatter/ISO_INSTANT ^Instant v)
:iso-local-time
(.format DateTimeFormatter/ISO_LOCAL_TIME
^ZonedDateTime (instant->zoned-date-time v))
:rfc1123
(.format DateTimeFormatter/RFC_1123_DATE_TIME
^ZonedDateTime (instant->zoned-date-time v)))))
(defmethod print-method Instant
[mv ^java.io.Writer writer]
(.write writer (str "#app/instant \"" (format-instant mv) "\"")))
(defmethod print-dup Instant [o w]
(print-method o w))
(extend-protocol fez/IEdn
Instant
(-edn [o] (tagged-literal 'app/instant (format-instant o))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cron Expression
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cron expressions are comprised of 6 required fields and one
;; optional field separated by white space. The fields respectively
;; are described as follows:
;;
;; Field Name Allowed Values Allowed Special Characters
;; Seconds 0-59 , - * /
;; Minutes 0-59 , - * /
;; Hours 0-23 , - * /
;; Day-of-month 1-31 , - * ? / L W
;; Month 0-11 or JAN-DEC , - * /
;; Day-of-Week 1-7 or SUN-SAT , - * ? / L #
;; Year (Optional) empty, 1970-2199 , - * /
;;
;; The '*' character is used to specify all values. For example, "*"
;; in the minute field means "every minute".
;;
;; The '?' character is allowed for the day-of-month and day-of-week
;; fields. It is used to specify 'no specific value'. This is useful
;; when you need to specify something in one of the two fields, but
;; not the other.
;;
;; The '-' character is used to specify ranges For example "10-12" in
;; the hour field means "the hours 10, 11 and 12".
;;
;; The ',' character is used to specify additional values. For
;; example "MON,WED,FRI" in the day-of-week field means "the days
;; Monday, Wednesday, and Friday".
;;
;; The '/' character is used to specify increments. For example "0/15"
;; in the seconds field means "the seconds 0, 15, 30, and
;; 45". And "5/15" in the seconds field means "the seconds 5, 20, 35,
;; and 50". Specifying '*' before the '/' is equivalent to specifying
;; 0 is the value to start with. Essentially, for each field in the
;; expression, there is a set of numbers that can be turned on or
;; off. For seconds and minutes, the numbers range from 0 to 59. For
;; hours 0 to 23, for days of the month 0 to 31, and for months 0 to
;; 11 (JAN to DEC). The "/" character simply helps you turn on
;; every "nth" value in the given set. Thus "7/6" in the month field
;; only turns on month "7", it does NOT mean every 6th month, please
;; note that subtlety.
;;
;; The 'L' character is allowed for the day-of-month and day-of-week
;; fields. This character is short-hand for "last", but it has
;; different meaning in each of the two fields. For example, the
;; value "L" in the day-of-month field means "the last day of the
;; month" - day 31 for January, day 28 for February on non-leap
;; years. If used in the day-of-week field by itself, it simply
;; means "7" or "SAT". But if used in the day-of-week field after
;; another value, it means "the last xxx day of the month" - for
;; example "6L" means "the last friday of the month". You can also
;; specify an offset from the last day of the month, such as "L-3"
;; which would mean the third-to-last day of the calendar month. When
;; using the 'L' option, it is important not to specify lists, or
;; ranges of values, as you'll get confusing/unexpected results.
;;
;; The 'W' character is allowed for the day-of-month field. This
;; character is used to specify the weekday (Monday-Friday) nearest
;; the given day. As an example, if you were to specify "15W" as the
;; value for the day-of-month field, the meaning is: "the nearest
;; weekday to the 15th of the month". So if the 15th is a Saturday,
;; the trigger will fire on Friday the 14th. If the 15th is a Sunday,
;; the trigger will fire on Monday the 16th. If the 15th is a Tuesday,
;; then it will fire on Tuesday the 15th. However if you specify "1W"
;; as the value for day-of-month, and the 1st is a Saturday, the
;; trigger will fire on Monday the 3rd, as it will not 'jump' over the
;; boundary of a month's days. The 'W' character can only be specified
;; when the day-of-month is a single day, not a range or list of days.
;;
;; The 'L' and 'W' characters can also be combined for the
;; day-of-month expression to yield 'LW', which translates to "last
;; weekday of the month".
;;
;; The '#' character is allowed for the day-of-week field. This
;; character is used to specify "the nth" XXX day of the month. For
;; example, the value of "6#3" in the day-of-week field means the
;; third Friday of the month (day 6 = Friday and "#3" = the 3rd one in
;; the month). Other examples: "2#1" = the first Monday of the month
;; and "4#5" = the fifth Wednesday of the month. Note that if you
;; specify "#5" and there is not 5 of the given day-of-week in the
;; month, then no firing will occur that month. If the '#' character
;; is used, there can only be one expression in the day-of-week
;; field ("3#1,6#3" is not valid, since there are two expressions).
;;
;; The legal characters and the names of months and days of the week
;; are not case sensitive.
(defn cron
"Creates an instance of CronExpression from string."
[s]
(try
(CronExpression. s)
(catch java.text.ParseException e
(ex/raise :type :parse
:code :invalid-cron-expression
:cause e
:context {:expr s}))))
(defn cron?
[v]
(instance? CronExpression v))
(defn next-valid-instant-from
[^CronExpression cron ^Instant now]
(s/assert cron? cron)
(.toInstant (.getNextValidTimeAfter cron (Date/from now))))
(defn get-next
[cron tnow]
(let [nt (next-valid-instant-from cron tnow)]
(cons nt (lazy-seq (get-next cron nt)))))
(defmethod print-method CronExpression
[mv ^java.io.Writer writer]
(.write writer (str "#app/cron \"" (.toString ^CronExpression mv) "\"")))
(defmethod print-dup CronExpression
[o w]
(print-ctor o (fn [o w] (print-dup (.toString ^CronExpression o) w)) w))
(extend-protocol fez/IEdn
CronExpression
(-edn [o] (pr-str o)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Measurement Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn tpoint
"Create a measurement checkpoint for time measurement of potentially
asynchronous flow."
[]
(let [p1 (System/nanoTime)]
#(duration {:nanos (- (System/nanoTime) p1)})))
(sm/register!
{:type ::instant
:pred instant?
:type-properties
{:error/message "should be an instant"
:title "instant"
:decode/string instant
:encode/string format-instant
:decode/json instant
:encode/json format-instant
:gen/gen (tgen/fmap (fn [i] (in-past i)) tgen/pos-int)
::oapi/type "string"
::oapi/format "iso"}})
(sm/register!
{:type ::duration
:pred duration?
:type-properties
{:error/message "should be a duration"
:gen/gen (tgen/fmap duration tgen/pos-int)
:title "duration"
:decode/string duration
:encode/string format-duration
:decode/json duration
:encode/json format-duration
::oapi/type "string"
::oapi/format "duration"}})

View File

@@ -9,10 +9,10 @@
(:require
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid]
[app.util.inet :as inet]
[app.util.time :as dt]
[promesa.exec :as px]
[promesa.exec.csp :as sp]
[promesa.util :as pu]
@@ -93,7 +93,7 @@
(assoc ::id id)
(assoc ::state state)
(assoc ::beats beats)
(assoc ::created-at (dt/now))
(assoc ::created-at (ct/now))
(assoc ::input-ch input-ch)
(assoc ::heartbeat-ch hbeat-ch)
(assoc ::output-ch output-ch)
@@ -107,7 +107,7 @@
(let [options (-> options
(assoc ::channel channel)
(on-connect))
timeout (dt/duration idle-timeout)]
timeout (ct/duration idle-timeout)]
(yws/set-idle-timeout! channel timeout)
(px/submit! :vthread (partial start-io-loop! options))))
@@ -128,7 +128,7 @@
(fn on-message [_channel message]
(when (string? message)
(sp/offer! input-ch message)
(swap! state assoc ::last-activity-at (dt/now))))
(swap! state assoc ::last-activity-at (ct/now))))
:on-pong
(fn on-pong [_channel data]

View File

@@ -10,11 +10,11 @@
[app.common.data :as d]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.metrics :as mtx]
[app.util.time :as dt]
[cuerdas.core :as str]
[integrant.core :as ig]))
@@ -31,7 +31,7 @@
[f metrics tname]
(let [labels (into-array String [tname])]
(fn [params]
(let [tp (dt/tpoint)]
(let [tp (ct/tpoint)]
(try
(f params)
(finally
@@ -95,7 +95,7 @@
[::task [:or ::sm/text :keyword]]
[::label {:optional true} ::sm/text]
[::delay {:optional true}
[:or ::sm/int ::dt/duration]]
[:or ::sm/int ::ct/duration]]
[::queue {:optional true} [:or ::sm/text :keyword]]
[::priority {:optional true} ::sm/int]
[::max-retries {:optional true} ::sm/int]
@@ -111,7 +111,7 @@
(check-options! options)
(let [duration (dt/duration delay)
(let [duration (ct/duration delay)
interval (db/interval duration)
props (db/tjson params)
id (uuid/next)
@@ -129,7 +129,7 @@
:queue queue
:label label
:dedupe (boolean dedupe)
:delay (dt/format-duration duration)
:delay (ct/format-duration duration)
:replace (or deleted 0))
(db/exec-one! conn [sql:insert-new-task id task props queue

View File

@@ -10,8 +10,9 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.db :as db]
[app.util.time :as dt]
[app.util.cron :as cron]
[app.worker :as wrk]
[app.worker.runner :refer [get-error-context]]
[cuerdas.core :as str]
@@ -49,7 +50,7 @@
[cfg {:keys [id cron] :as task}]
(px/thread
{:name (str "penpot/cron-task/" id)}
(let [tpoint (dt/tpoint)]
(let [tpoint (ct/tpoint)]
(try
(db/tx-run! cfg (fn [{:keys [::db/conn]}]
(db/exec-one! conn ["SET LOCAL statement_timeout=0;"])
@@ -57,20 +58,20 @@
(when (lock-scheduled-task! conn id)
(db/update! conn :scheduled-task
{:cron-expr (str cron)
:modified-at (dt/now)}
:modified-at (ct/now)}
{:id id}
{::db/return-keys false})
(l/dbg :hint "start" :id id)
((:fn task) task)
(let [elapsed (dt/format-duration (tpoint))]
(let [elapsed (ct/format-duration (tpoint))]
(l/dbg :hint "end" :id id :elapsed elapsed)))))
(catch InterruptedException _
(let [elapsed (dt/format-duration (tpoint))]
(let [elapsed (ct/format-duration (tpoint))]
(l/debug :hint "task interrupted" :id id :elapsed elapsed)))
(catch Throwable cause
(let [elapsed (dt/format-duration (tpoint))]
(let [elapsed (ct/format-duration (tpoint))]
(binding [l/*context* (get-error-context cause task)]
(l/err :hint "unhandled exception on running task"
:id id
@@ -82,10 +83,10 @@
(defn- ms-until-valid
[cron]
(assert (dt/cron? cron) "expected cron instance")
(let [now (dt/now)
next (dt/next-valid-instant-from cron now)]
(dt/diff now next)))
(assert (cron/cron-expr? cron) "expected cron instance")
(let [now (ct/now)
next (cron/next-valid-instant-from cron now)]
(ct/diff now next)))
(defn- schedule-cron-task
[{:keys [::running] :as cfg} {:keys [cron id] :as task}]
@@ -93,8 +94,8 @@
ft (px/schedule! ts (partial execute-cron-task cfg task))]
(l/dbg :hint "schedule" :id id
:ts (dt/format-duration ts)
:at (dt/format-instant (dt/in-future ts)))
:ts (ct/format-duration ts)
:at (ct/format-inst (ct/in-future ts)))
(swap! running #(into #{ft} (filter p/pending?) %))))
@@ -104,7 +105,7 @@
[:vector
[:maybe
[:map
[:cron [:fn dt/cron?]]
[:cron [:fn cron/cron-expr?]]
[:task :keyword]
[:props {:optional true} :map]
[:id {:optional true} :keyword]]]]]

View File

@@ -10,11 +10,11 @@
[app.common.data.macros :as dm]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t]
[app.db :as db]
[app.metrics :as mtx]
[app.redis :as rds]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[cuerdas.core :as str]
[integrant.core :as ig]
@@ -32,9 +32,9 @@
(defmethod ig/expand-key ::wrk/dispatcher
[k v]
{k (-> (d/without-nils v)
(assoc ::timeout (dt/duration "10s"))
(assoc ::timeout (ct/duration "10s"))
(assoc ::batch-size 100)
(assoc ::wait-duration (dt/duration "5s")))})
(assoc ::wait-duration (ct/duration "5s")))})
(defmethod ig/assert-key ::wrk/dispatcher
[_ cfg]

View File

@@ -10,8 +10,8 @@
[app.common.data :as d]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.metrics :as mtx]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[integrant.core :as ig]
[promesa.exec :as px])
@@ -55,7 +55,7 @@
(defmethod ig/expand-key ::wrk/monitor
[k v]
{k (-> (d/without-nils v)
(assoc ::interval (dt/duration "2s")))})
(assoc ::interval (ct/duration "2s")))})
(defmethod ig/init-key ::wrk/monitor
[_ {:keys [::wrk/executor ::mtx/metrics ::interval ::wrk/name]}]

View File

@@ -12,11 +12,11 @@
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t]
[app.db :as db]
[app.metrics :as mtx]
[app.redis :as rds]
[app.util.time :as dt]
[app.worker :as wrk]
[cuerdas.core :as str]
[integrant.core :as ig]
@@ -29,10 +29,10 @@
[:id ::sm/uuid]
[:queue :string]
[:name :string]
[:created-at ::sm/inst]
[:modified-at ::sm/inst]
[:scheduled-at {:optional true} ::sm/inst]
[:completed-at {:optional true} ::sm/inst]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]
[:scheduled-at {:optional true} ::ct/inst]
[:completed-at {:optional true} ::ct/inst]
[:error {:optional true} :string]
[:max-retries :int]
[:retry-num :int]
@@ -76,10 +76,10 @@
:queue queue
:runner-id id
:retry (:retry-num task))
(let [tpoint (dt/tpoint)
(let [tpoint (ct/tpoint)
task-fn (wrk/get-task registry (:name task))
result (when task-fn (task-fn task))
elapsed (dt/format-duration (tpoint))
elapsed (ct/format-duration (tpoint))
result (if (valid-task-result? result)
result
{:status "completed"})]
@@ -105,7 +105,7 @@
(:max-retries task))
(= ::retry (:type edata)))
(cond-> {:status "retry" :error cause}
(dt/duration? (:delay edata))
(ct/duration? (:delay edata))
(assoc :delay (:delay edata))
(= ::noop (:strategy edata))
@@ -156,13 +156,13 @@
(str error))
task (-> result meta ::task)
nretry (+ (:retry-num task) inc-by)
now (dt/now)
now (ct/now)
delay (->> (iterate #(* % 2) delay) (take nretry) (last))]
(db/update! pool :task
{:error explain
:status "retry"
:modified-at now
:scheduled-at (dt/plus now delay)
:scheduled-at (ct/plus now delay)
:retry-num nretry}
{:id (:id task)})
nil))
@@ -172,14 +172,14 @@
explain (ex-message error)]
(db/update! pool :task
{:error explain
:modified-at (dt/now)
:modified-at (ct/now)
:status "failed"}
{:id (:id task)})
nil))
(handle-task-completion [result]
(let [task (-> result meta ::task)
now (dt/now)]
now (ct/now)]
(db/update! pool :task
{:completed-at now
:modified-at now
@@ -255,7 +255,7 @@
(let [cfg (-> cfg
(assoc ::rds/rconn rconn)
(assoc ::queue (str/ffmt "%:%" tenant queue))
(assoc ::timeout (dt/duration "5s")))]
(assoc ::timeout (ct/duration "5s")))]
(loop []
(when (px/interrupted?)
(throw (InterruptedException. "interrupted")))

View File

@@ -1,3 +1,3 @@
{app/instant app.util.time/instant
app/cron app.util.time/cron
app/duration app.util.time/duration}
{penpot/inst app.common.time/inst
penpot/cron app.util.cron/cron
penpot/duration app.common.time/duration}

View File

@@ -20,7 +20,6 @@
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.storage.tmp :as tmp]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[cuerdas.core :as str]

View File

@@ -6,11 +6,11 @@
(ns backend-tests.bounce-handling-test
(:require
[app.common.time :as ct]
[app.db :as db]
[app.email :as email]
[app.http.awsns :as awsns]
[app.tokens :as tokens]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.pprint :refer [pprint]]
[clojure.test :as t]
@@ -250,7 +250,7 @@
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)]
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
@@ -268,8 +268,8 @@
:profile-complaint-threshold 2})}]
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)]
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (dt/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile) :created-at (ct/in-past {:days 8})})
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(th/create-complaint-for pool {:type :complaint :id (:id profile)})

View File

@@ -15,6 +15,7 @@
[app.common.pprint :as pp]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.time :as ct]
[app.common.transit :as tr]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -33,7 +34,6 @@
[app.rpc.helpers :as rph]
[app.util.blob :as blob]
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[app.worker.runner]
[clojure.java.io :as io]
@@ -263,7 +263,7 @@
(dm/with-open [conn (db/open system)]
(db/insert! conn :profile-complaint-report
{:profile-id id
:created-at (or created-at (dt/now))
:created-at (or created-at (ct/now))
:type (name type)
:content (db/tjson {})})))
@@ -273,7 +273,7 @@
(db/insert! conn :global-complaint-report
{:email email
:type (name type)
:created-at (or created-at (dt/now))
:created-at (or created-at (ct/now))
:content (db/tjson {})})))
(defn create-team-role*
@@ -305,7 +305,7 @@
([system {:keys [file-id changes session-id profile-id revn]
:or {session-id (uuid/next) revn 0}}]
(-> system
(assoc ::files.update/timestamp (dt/now))
(assoc ::files.update/timestamp (ct/now))
(db/tx-run! (fn [{:keys [::db/conn] :as system}]
(let [file (files.update/get-file conn file-id)]
(#'files.update/update-file* system
@@ -379,7 +379,7 @@
;; (app.common.pprint/pprint (:app.rpc/methods *system*))
(try-on! (method-fn (-> data
(dissoc ::type)
(assoc :app.rpc/request-at (dt/now)))))))
(assoc :app.rpc/request-at (ct/now)))))))
(defn run-task!
([name]
@@ -525,7 +525,7 @@
(defn sleep
[ms-or-duration]
(Thread/sleep (inst-ms (dt/duration ms-or-duration))))
(Thread/sleep (inst-ms (ct/duration ms-or-duration))))
(defn config-get-mock
[data]

View File

@@ -7,10 +7,10 @@
(ns backend-tests.rpc-audit-test
(:require
[app.common.pprint :as pp]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[yetti.request]))
@@ -46,7 +46,7 @@
:route "dashboard-files"}
:context {:engine "blink"}
:profile-id (:id prof)
:timestamp (dt/now)
:timestamp (ct/now)
:type "action"}]}
params (with-meta params
@@ -79,7 +79,7 @@
:route "dashboard-files"}
:context {:engine "blink"}
:profile-id uuid/zero
:timestamp (dt/now)
:timestamp (ct/now)
:type "action"}]}
params (with-meta params
{:app.http/request http-request})

View File

@@ -7,6 +7,7 @@
(ns backend-tests.rpc-comment-test
(:require
[app.common.geom.point :as gpt]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.http :as http]
@@ -14,7 +15,6 @@
[app.rpc.commands.comments :as comments]
[app.rpc.cond :as cond]
[app.rpc.quotes :as-alias quotes]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[datoteka.fs :as fs]
@@ -78,7 +78,7 @@
(let [{:keys [result] :as out} (th/command! data)]
(t/is (th/success? out))
(t/is (dt/instant? (:modified-at result))))
(t/is (ct/inst? (:modified-at result))))
(let [status' (th/db-get :comment-thread-status
{:thread-id (:id thread)

View File

@@ -17,7 +17,6 @@
[app.http :as http]
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[cuerdas.core :as str]))

View File

@@ -9,6 +9,7 @@
[app.common.features :as cfeat]
[app.common.pprint :as pp]
[app.common.thumbnails :as thc]
[app.common.time :as ct]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -18,7 +19,6 @@
[app.rpc :as-alias rpc]
[app.rpc.commands.files :as files]
[app.storage :as sto]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[cuerdas.core :as str]))
@@ -135,7 +135,7 @@
(t/is (nil? (:users result))))))
(th/db-update! :file
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id file-id})
(t/testing "query single file after delete and wait"
@@ -1844,7 +1844,7 @@
(th/run-task! :delete-object
{:object :file
:deleted-at (dt/now)
:deleted-at (ct/now)
:id (:id file-1)})
;; Check that file media object references are marked all for deletion

View File

@@ -16,7 +16,6 @@
[app.rpc.commands.auth :as cauth]
[app.storage :as sto]
[app.tokens :as tokens]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.java.io :as io]
[clojure.test :as t]

View File

@@ -6,11 +6,11 @@
(ns backend-tests.rpc-media-test
(:require
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[datoteka.fs :as fs]))
@@ -257,7 +257,7 @@
:is-shared false})
_ (th/db-update! :file
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id (:id file)})
mfile {:filename "sample.jpg"

View File

@@ -6,6 +6,7 @@
(ns backend-tests.rpc-profile-test
(:require
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -14,7 +15,6 @@
[app.rpc :as-alias rpc]
[app.rpc.commands.profile :as profile]
[app.tokens :as tokens]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.java.io :as io]
[clojure.test :as t]
@@ -158,7 +158,7 @@
(let [row (th/db-get :team
{:id (:default-team-id prof)}
{::db/remove-deleted false})]
(t/is (dt/instant? (:deleted-at row))))
(t/is (ct/inst? (:deleted-at row))))
;; execute permanent deletion task
(let [result (th/run-task! :objects-gc {:min-age 0})]
@@ -212,7 +212,7 @@
;; (th/print-result! out)
(let [team (th/db-get :team {:id (:id team1)} {::db/remove-deleted false})]
(t/is (dt/instant? (:deleted-at team)))))
(t/is (ct/inst? (:deleted-at team)))))
;; Request profile to be deleted
(let [params {::th/type :delete-profile
@@ -517,7 +517,7 @@
(let [sprops (:app.setup/props th/*system*)
itoken (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "48h")
:exp (ct/in-future "48h")
:role :editor
:team-id uuid/zero
:member-email "user@example.com"})
@@ -546,7 +546,7 @@
(let [sprops (:app.setup/props th/*system*)
itoken (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "48h")
:exp (ct/in-future "48h")
:role :editor
:team-id uuid/zero
:member-email "user2@example.com"})
@@ -568,7 +568,7 @@
(let [sprops (:app.setup/props th/*system*)
itoken (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "48h")
:exp (ct/in-future "48h")
:role :editor
:team-id uuid/zero
:member-email "user@example.com"})
@@ -589,7 +589,7 @@
(let [sprops (:app.setup/props th/*system*)
itoken (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "48h")
:exp (ct/in-future "48h")
:role :editor
:team-id uuid/zero
:member-email "user2@example.com"})
@@ -611,7 +611,7 @@
(let [sprops (:app.setup/props th/*system*)
itoken (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "48h")
:exp (ct/in-future "48h")
:role :editor
:team-id uuid/zero
:member-email "user2@example.com"})

View File

@@ -11,7 +11,6 @@
[app.db :as db]
[app.http :as http]
[app.rpc :as-alias rpc]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]))

View File

@@ -7,6 +7,7 @@
(ns backend-tests.rpc-team-test
(:require
[app.common.logging :as l]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@@ -14,7 +15,6 @@
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.tokens :as tokens]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[datoteka.fs :as fs]
@@ -163,7 +163,7 @@
;; Proceed to delete the requester user
(th/db-update! :profile
{:deleted-at (dt/in-past "1h")}
{:deleted-at (ct/in-past "1h")}
{:id (:id requester)})
;; Create a new profile with the same email
@@ -271,7 +271,7 @@
(let [token (tokens/generate sprops
{:iss :team-invitation
:exp (dt/in-future "1h")
:exp (ct/in-future "1h")
:profile-id (:id profile1)
:role :editor
:team-id (:id team)
@@ -283,7 +283,7 @@
{:team-id (:id team)
:email-to (:email profile2)
:role "editor"
:valid-until (dt/in-future "48h")})
:valid-until (ct/in-future "48h")})
(let [data {::th/type :verify-token :token token}
out (th/command! data)]
@@ -328,7 +328,7 @@
{:team-id (:id team)
:email-to (:email profile3)
:role "editor"
:valid-until (dt/in-future "48h")})
:valid-until (ct/in-future "48h")})
(let [data {::th/type :verify-token
::rpc/profile-id (:id profile1)
@@ -381,14 +381,14 @@
{:team-id (:team-id data)
:email-to "test1@mail.com"
:role "editor"
:valid-until (dt/in-future "48h")})
:valid-until (ct/in-future "48h")})
;; insert an entry on the database with an expired invitation
(db/insert! th/*pool* :team-invitation
{:team-id (:team-id data)
:email-to "test2@mail.com"
:role "editor"
:valid-until (dt/in-past "48h")})
:valid-until (ct/in-past "48h")})
(let [out (th/command! data)]
(t/is (th/success? out))
@@ -415,7 +415,7 @@
{:team-id (:team-id data)
:email-to "test1@mail.com"
:role "editor"
:valid-until (dt/in-future "48h")})
:valid-until (ct/in-future "48h")})
(let [out (th/command! data)
;; retrieve the value from the database and check its content
@@ -438,7 +438,7 @@
{:team-id (:team-id data)
:email-to "test1@mail.com"
:role "editor"
:valid-until (dt/in-future "48h")})
:valid-until (ct/in-future "48h")})
(let [out (th/command! data)
;; retrieve the value from the database and check its content
@@ -582,7 +582,7 @@
(let [rows (th/db-exec! ["select * from team where id = ?" (:id team)])]
(t/is (= 1 (count rows)))
(t/is (dt/instant? (:deleted-at (first rows)))))
(t/is (ct/inst? (:deleted-at (first rows)))))
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 5 (:processed result))))))
@@ -626,7 +626,7 @@
(th/reset-mock! mock)
(th/db-update! :team-access-request
{:valid-until (dt/in-past "1h")}
{:valid-until (ct/in-past "1h")}
{:team-id (:id team)
:requester-id (:id requester)})

View File

@@ -7,11 +7,11 @@
(ns backend-tests.storage-test
(:require
[app.common.exceptions :as ex]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.storage :as sto]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t]
[cuerdas.core :as str]
@@ -53,12 +53,12 @@
(configure-storage-backend))
content (sto/content "content")
object (sto/put-object! storage {::sto/content content
::sto/expired-at (dt/in-future {:seconds 1})
::sto/expired-at (ct/in-future {:seconds 1})
:content-type "text/plain"})]
(t/is (sto/object? object))
(t/is (dt/instant? (:expired-at object)))
(t/is (dt/is-after? (:expired-at object) (dt/now)))
(t/is (ct/inst? (:expired-at object)))
(t/is (ct/is-after? (:expired-at object) (ct/now)))
(t/is (= object (sto/get-object storage (:id object))))
(th/sleep 1000)
@@ -73,7 +73,7 @@
content (sto/content "content")
object (sto/put-object! storage {::sto/content content
:content-type "text/plain"
:expired-at (dt/in-future {:seconds 1})})]
:expired-at (ct/in-future {:seconds 1})})]
(t/is (sto/object? object))
(t/is (true? (sto/del-object! storage object)))
@@ -95,13 +95,13 @@
content3 (sto/content "content3")
object1 (sto/put-object! storage {::sto/content content1
::sto/expired-at (dt/now)
::sto/expired-at (ct/now)
:content-type "text/plain"})
object2 (sto/put-object! storage {::sto/content content2
::sto/expired-at (dt/in-past {:hours 2})
::sto/expired-at (ct/in-past {:hours 2})
:content-type "text/plain"})
object3 (sto/put-object! storage {::sto/content content3
::sto/expired-at (dt/in-past {:hours 1})
::sto/expired-at (ct/in-past {:hours 1})
:content-type "text/plain"})]
@@ -154,7 +154,7 @@
(t/is (= (:media-id result-1) (:media-id result-2)))
(th/db-update! :file-media-object
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id (:id result-1)})
;; run the objects gc task for permanent deletion
@@ -239,7 +239,7 @@
result-2 (:result out2)]
(th/db-update! :team-font-variant
{:deleted-at (dt/now)}
{:deleted-at (ct/now)}
{:id (:id result-2)})
;; run the objects gc task for permanent deletion

View File

@@ -7,7 +7,6 @@
(ns backend-tests.tasks-telemetry-test
(:require
[app.db :as db]
[app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.pprint :refer [pprint]]
[clojure.test :as t]

View File

@@ -10,15 +10,15 @@
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"dependencies": {
"luxon": "^3.6.1"
},
"devDependencies": {
"concurrently": "^9.1.2",
"nodemon": "^3.1.10",
"source-map-support": "^0.5.21",
"ws": "^8.18.2"
},
"dependencies": {
"date-fns": "^4.1.0"
},
"scripts": {
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"fmt:clj": "cljfmt fix --parallel=true src/ test/",

View File

@@ -14,7 +14,6 @@
[app.common.schema.generators :as sg]
[app.common.schema.openapi :as-alias oapi]
[app.common.schema.registry :as sr]
[app.common.time :as tm]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[clojure.core :as c]
@@ -850,38 +849,38 @@
{:title "contains any"
:description "contains predicate"}}))})
(register!
{:type ::inst
:pred inst?
:type-properties
{:title "inst"
:description "Satisfies Inst protocol"
:error/message "should be an instant"
:gen/gen (->> (sg/small-int :min 0 :max 100000)
(sg/fmap (fn [v] (tm/parse-instant v))))
;; (register!
;; {:type ::inst
;; :pred tm/instant?
;; :type-properties
;; {:title "inst"
;; :description "Satisfies Inst protocol"
;; :error/message "should be an instant"
;; :gen/gen (->> (sg/small-int :min 0 :max 100000)
;; (sg/fmap (fn [v] (tm/parse-inst v))))
:decode/string tm/parse-instant
:encode/string tm/format-instant
:decode/json tm/parse-instant
:encode/json tm/format-instant
::oapi/type "string"
::oapi/format "iso"}})
;; :decode/string tm/parse-inst
;; :encode/string tm/format-inst
;; :decode/json tm/parse-inst
;; :encode/json tm/format-inst
;; ::oapi/type "string"
;; ::oapi/format "iso"}})
(register!
{:type ::timestamp
:pred inst?
:type-properties
{:title "inst"
:description "Satisfies Inst protocol"
:error/message "should be an instant"
:gen/gen (->> (sg/small-int)
(sg/fmap (fn [v] (tm/parse-instant v))))
:decode/string tm/parse-instant
:encode/string inst-ms
:decode/json tm/parse-instant
:encode/json inst-ms
::oapi/type "string"
::oapi/format "number"}})
;; (register!
;; {:type ::timestamp
;; :pred tm/instant?
;; :type-properties
;; {:title "inst"
;; :description "Satisfies Inst protocol, the same as ::inst but encodes to epoch"
;; :error/message "should be an instant"
;; :gen/gen (->> (sg/small-int)
;; (sg/fmap (fn [v] (tm/parse-inst v))))
;; :decode/string tm/parse-inst
;; :encode/string inst-ms
;; :decode/json tm/parse-inst
;; :encode/json inst-ms
;; ::oapi/type "string"
;; ::oapi/format "number"}})
(register!
{:type ::fn

View File

@@ -4,28 +4,152 @@
;;
;; Copyright (c) KALEIDOS INC
#_{:clj-kondo/ignore [:unused-namespace]}
(ns app.common.time
"Minimal cross-platoform date time api for specific use cases on types
definition and other common code."
#?(:cljs
(:require
["luxon" :as lxn])
:clj
(:refer-clojure :exclude [inst?])
(:require
#?@(:cljs [["date-fns/format$default" :as dfn-format]
["date-fns/formatISO$default" :as dfn-format-iso]
["date-fns/setDefaultOptions$default" :as dfn-set-default-options]
["date-fns/differenceInMilliseconds$default" :as dfn-diff]
["date-fns/formatDistanceToNowStrict$default" :as dfn-distance-to-now]
["date-fns/add$default" :as dfn-add]
["date-fns/sub$default" :as dfn-sub]
["date-fns/parseISO$default" :as dfn-parse-iso]
["date-fns/locale/ar-SA$default" :as dfn-ar]
["date-fns/locale/ca$default" :as dfn-ca]
["date-fns/locale/cs$default" :as dfn-cs]
["date-fns/locale/de$default" :as dfn-de]
["date-fns/locale/el$default" :as dfn-el]
["date-fns/locale/en-US$default" :as df-en-us]
["date-fns/locale/es$default" :as dfn-es]
["date-fns/locale/eu$default" :as dfn-eu]
["date-fns/locale/fa-IR$default" :as dfn-fa-ir]
["date-fns/locale/fr$default" :as dfn-fr]
["date-fns/locale/gl$default" :as dfn-gl]
["date-fns/locale/he$default" :as dfn-he]
["date-fns/locale/hr$default" :as dfn-hr]
["date-fns/locale/id$default" :as dfn-id]
["date-fns/locale/it$default" :as dfn-it]
["date-fns/locale/ja$default" :as dfn-ja]
["date-fns/locale/ko$default" :as dfn-ko]
["date-fns/locale/lv$default" :as dfn-lv]
["date-fns/locale/nb$default" :as dfn-nb]
["date-fns/locale/nl$default" :as dfn-nl]
["date-fns/locale/pl$default" :as dfn-pl]
["date-fns/locale/pt$default" :as dfn-pt]
["date-fns/locale/pt-BR$default" :as dfn-pt-br]
["date-fns/locale/ro$default" :as dfn-ro]
["date-fns/locale/ru$default" :as dfn-ru]
["date-fns/locale/tr$default" :as dfn-tr]
["date-fns/locale/uk$default" :as dfn-uk]
["date-fns/locale/zh-CN$default" :as dfn-zh-cn]])
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.schema.openapi :as-alias oapi]
[cuerdas.core :as str])
#?(:clj
(:import
java.time.format.DateTimeFormatter
java.time.Duration
java.time.Instant
java.time.Duration)))
#?(:cljs
(def DateTime lxn/DateTime))
#?(:cljs
(def Duration lxn/Duration))
java.time.OffsetDateTime
java.time.ZoneId
java.time.ZonedDateTime
java.time.format.DateTimeFormatter
java.time.temporal.ChronoUnit
java.time.temporal.Temporal
java.time.temporal.TemporalAmount
java.time.temporal.TemporalUnit)))
(defn now
[]
#?(:clj (Instant/now)
:cljs (.local ^js DateTime)))
:cljs (new js/Date)))
;; --- DURATION
(defn- resolve-temporal-unit
[o]
(case o
(:nanos :nano)
#?(:clj ChronoUnit/NANOS
:cljs (throw (js/Error. "not supported nanos")))
(:micros :microsecond :micro)
#?(:clj ChronoUnit/MICROS
:cljs (throw (js/Error. "not supported nanos")))
(:millis :millisecond :milli)
#?(:clj ChronoUnit/MILLIS
:cljs "millisecond")
(:seconds :second)
#?(:clj ChronoUnit/SECONDS
:cljs "second")
(:minutes :minute)
#?(:clj ChronoUnit/MINUTES
:cljs "minute")
(:hours :hour)
#?(:clj ChronoUnit/HOURS
:cljs "hour")
(:days :day)
#?(:clj ChronoUnit/DAYS
:cljs "day")))
(defn temporal-unit
[o]
#?(:clj (if (instance? TemporalUnit o) o (resolve-temporal-unit o))
:cljs (resolve-temporal-unit o)))
#?(:clj
(defn- obj->duration
[params]
(reduce-kv (fn [o k v]
(.plus ^Duration o ^long v ^TemporalUnit (temporal-unit k)))
(Duration/ofMillis 0)
params)))
#?(:clj
(defn duration?
[o]
(instance? Duration o)))
(defn duration
[ms-or-obj]
#?(:clj
(cond
(string? ms-or-obj)
(Duration/parse (str "PT" ms-or-obj))
(duration? ms-or-obj)
ms-or-obj
(integer? ms-or-obj)
(Duration/ofMillis ms-or-obj)
:else
(obj->duration ms-or-obj))
:cljs
(clj->js ms-or-obj)))
#?(:clj
(defn parse-duration
[s]
(duration s)))
#?(:clj
(defn format-duration
[o]
(str/lower (subs (str o) 2))))
;; --- INSTNANT & DATETIME
(defn is-after?
"Analgous to: da > db"
@@ -44,65 +168,261 @@
(zero? result) false
:else false)))
(defn instant?
(defn inst?
[o]
#?(:clj (instance? Instant o)
:cljs (instance? DateTime o)))
:cljs (instance? js/Date o)))
(defn parse-instant
(defn seconds
[d]
(-> d inst-ms (/ 1000) int))
(defn format-inst
([v] (format-inst v :iso))
([v fmt]
(case fmt
(:iso :iso8601)
#?(:clj (.format DateTimeFormatter/ISO_INSTANT ^Instant v)
:cljs (dfn-format-iso v))
:iso-date
#?(:clj (.format DateTimeFormatter/ISO_LOCAL_DATE
^ZonedDateTime (ZonedDateTime/ofInstant v (ZoneId/of "UTC")))
:cljs (dfn-format-iso v #js {:representation "date"}))
(:rfc1123 :http)
#?(:clj (.format DateTimeFormatter/RFC_1123_DATE_TIME
^ZonedDateTime (ZonedDateTime/ofInstant v (ZoneId/of "UTC")))
:cljs (dfn-format v "EEE, dd LLL yyyy HH:mm:ss 'GMT'"))
#?@(:cljs [:time-24-simple
(dfn-format v "HH:mm")
;; DEPRECATED
:date-full
(dfn-format v "PPP")
:localized-date
(dfn-format v "PPP")
:localized-time
(dfn-format v "p")
:localized-date-time
(dfn-format v "PPPp")
(if (string? fmt)
(dfn-format v fmt)
(throw (js/Error. "unpexted format")))]))))
#?(:cljs
(def locales
#js {:ar dfn-ar
:ca dfn-ca
:de dfn-de
:el dfn-el
:en df-en-us
:en_us df-en-us
:es dfn-es
:es_es dfn-es
:fa dfn-fa-ir
:fa_ir dfn-fa-ir
:fr dfn-fr
:he dfn-he
:pt dfn-pt
:pt_pt dfn-pt
:pt_br dfn-pt-br
:ro dfn-ro
:ru dfn-ru
:tr dfn-tr
:zh-cn dfn-zh-cn
:nl dfn-nl
:eu dfn-eu
:gl dfn-gl
:hr dfn-hr
:it dfn-it
:nb dfn-nb
:nb_no dfn-nb
:pl dfn-pl
:id dfn-id
:uk dfn-uk
:cs dfn-cs
:lv dfn-lv
:ko dfn-ko
:ja dfn-ja
:ja_jp dfn-ja}))
#?(:cljs
(defn timeago
[v]
(when v
(dfn-distance-to-now v #js {:includeSeconds true
:addSuffix true}))))
(defn inst
[s]
(cond
(instant? s)
(inst? s)
s
(int? s)
#?(:clj (Instant/ofEpochMilli s)
:cljs (.fromMillis ^js DateTime s #js {:zone "local" :setZone false}))
#?(:clj (Instant/ofEpochMilli s)
:cljs (new js/Date s))
(string? s)
#?(:clj (Instant/parse s)
:cljs (.fromISO ^js DateTime s))))
#?(:clj (Instant/from (.parse DateTimeFormatter/ISO_DATE_TIME ^String s))
:cljs (dfn-parse-iso s))
(defn format-instant
:else
(throw (ex-info "invalid parameters" {}))))
#?(:clj
(defn truncate
[o unit]
(let [unit (temporal-unit unit)]
(cond
(inst? o)
(.truncatedTo ^Instant o ^TemporalUnit unit)
(instance? Duration o)
(.truncatedTo ^Duration o ^TemporalUnit unit)
:else
(throw (IllegalArgumentException. "only instant and duration allowed"))))))
(defn plus
[d ta]
(let [ta (duration ta)]
(cond
#?@(:clj [(duration? d) (.plus ^Duration d ^TemporalAmount ta)])
#?(:cljs (inst? d)
:clj (instance? Temporal d))
#?(:cljs (dfn-add d ta)
:clj (.plus ^Temporal d ^Duration ta))
:else
(throw #?(:clj (UnsupportedOperationException. "unsupported type")
:cljs (js/Error. "unsupported type"))))))
(defn minus
[d ta]
(let [^TemporalAmount ta (duration ta)]
(cond
#?@(:clj [(duration? d) (.minus ^Duration d ^TemporalAmount ta)])
#?(:cljs (inst? d) :clj (instance? Temporal d))
#?(:cljs (dfn-sub d ta)
:clj (.minus ^Temporal d ^Duration ta))
:else
(throw #?(:clj (UnsupportedOperationException. "unsupported type")
:cljs (js/Error. "unsupported type"))))))
(defn in-future
[v]
#?(:clj (.format DateTimeFormatter/ISO_INSTANT ^Instant v)
:cljs (.toISO ^js v)))
(plus (now) v))
;; To check for valid date time we can just use the core inst? function
(defn in-past
[v]
(minus (now) v))
#?(:clj
(defn diff
[t1 t2]
(Duration/between t1 t2)))
#?(:cljs
(extend-protocol IComparable
DateTime
(-compare [it other]
(if ^boolean (.equals it other)
0
(if (< (inst-ms it) (inst-ms other)) -1 1)))
Duration
(-compare [it other]
(if ^boolean (.equals it other)
0
(if (< (inst-ms it) (inst-ms other)) -1 1)))))
(defn diff-ms
[t1 t2]
(dfn-diff t2 t1)))
#?(:cljs
(extend-type DateTime
cljs.core/IEquiv
(-equiv [o other]
(and (instance? DateTime other)
(== (.valueOf o) (.valueOf other))))))
(defn set-default-locale!
[locale]
(when-let [locale (unchecked-get locales locale)]
(dfn-set-default-options #js {:locale locale}))))
;; --- HELPERS
#?(:clj
(defn tpoint
"Create a measurement checkpoint for time measurement of potentially
asynchronous flow."
[]
(let [p1 (System/nanoTime)]
#(duration {:nanos (- (System/nanoTime) p1)}))))
#?(:cljs
(extend-protocol cljs.core/Inst
DateTime
(inst-ms* [inst] (.toMillis ^js inst))
(defn tpoint-ms
"Create a measurement checkpoint for time measurement of potentially
asynchronous flow."
[]
(let [p1 (.now js/performance)]
#(- (.now js/performance) p1))))
Duration
(inst-ms* [inst] (.toMillis ^js inst)))
;; --- EXTENSIONS
:clj
#?(:clj
(extend-protocol clojure.core/Inst
Duration
(inst-ms* [v] (.toMillis ^Duration v))
OffsetDateTime
(inst-ms* [v] (.toEpochMilli (.toInstant ^OffsetDateTime v)))
Instant
(inst-ms* [v] (.toEpochMilli ^Instant v))))
#?(:clj
(defmethod print-method Duration
[o w]
(print-dup o w)))
#?(:clj
(defmethod print-dup Duration
[mv ^java.io.Writer writer]
(.write writer (str "#penpot/duration \"" (str/lower (subs (str mv) 2)) "\""))))
#?(:clj
(defmethod print-method Instant
[o w]
(print-dup o w)))
#?(:clj
(defmethod print-dup Instant
[mv ^java.io.Writer writer]
(.write writer (str "#penpot/inst \"" (format-inst mv) "\""))))
(def schema:inst
(sm/register!
{:type ::inst
:pred inst?
:type-properties
{:error/message "should be an instant"
:title "instant"
:decode/string inst
:encode/string format-inst
:decode/json inst
:encode/json format-inst
:gen/gen (->> (sg/small-int :min 0)
(sg/fmap (fn [i] (in-past i))))
::oapi/type "string"
::oapi/format "iso"}}))
#?(:clj
(def schema:duration
(sm/register!
{:type ::duration
:pred duration?
:type-properties
{:error/message "should be a duration"
:gen/gen (->> (sg/small-int :min 0)
(sg/fmap duration))
:title "duration"
:decode/string parse-duration
:encode/string format-duration
:decode/json parse-duration
:encode/json format-duration
::oapi/type "string"
::oapi/format "duration"}})))

View File

@@ -7,7 +7,6 @@
(ns app.common.transit
(:require
#?(:clj [datoteka.fs :as fs])
#?(:cljs ["luxon" :as lxn])
[app.common.data :as d]
[app.common.uri :as uri]
[cognitect.transit :as t]
@@ -130,20 +129,18 @@
:wfn vec
:rfn #(into lks/empty-linked-set %)}
{:id "duration"
:class #?(:clj Duration :cljs lxn/Duration)
:rfn (fn [v]
#?(:clj (Duration/ofMillis v)
:cljs (.fromMillis ^js lxn/Duration v)))
:wfn inst-ms}
#?(:clj
{:id "duration"
:class Duration
:rfn (fn [v] (Duration/ofMillis v))
:wfn inst-ms})
{:id "m"
:class #?(:clj Instant :cljs lxn/DateTime)
:class #?(:clj Instant :cljs js/Date)
:rfn (fn [v]
#?(:clj (-> (Long/parseLong v)
(Instant/ofEpochMilli))
:cljs (let [ms (js/parseInt v 10)]
(.fromMillis ^js lxn/DateTime ms))))
#?(:clj (-> (Long/parseLong v)
(Instant/ofEpochMilli))
:cljs (new js/Date (js/parseInt v 10))))
:wfn (comp str inst-ms)}
{:id "penpot/pointer"
@@ -204,12 +201,30 @@
(with-open [input (ByteArrayInputStream. ^bytes data)]
(t/read (reader input opts))))))
#?(:cljs
(defn- is-date-like?
[obj]
(and ^boolean (some? obj)
^boolean (fn? (.-getTime obj))
^boolean (some? (.getTime obj)))))
#_:clj-kondo/ignore
(def ^:private date-write-handler
(t/write-handler (constantly "m")
(comp str inst-ms)))
(defn encode-str
([data] (encode-str data nil))
([data opts]
#?(:cljs
(let [type (:type opts :json)
params {:handlers @write-handler-map}
params {:handlers @write-handler-map
;; NOTE: this is necessary because the plugin
;; secure context alters the js/Date constructor
:handlerForForeign (fn [x _]
(if (is-date-like? x)
date-write-handler
nil))}
params (if (:with-meta opts)
(assoc params :transform t/write-meta)
params)

View File

@@ -14,6 +14,7 @@
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.schema.openapi :as-alias oapi]
[app.common.time :as ct]
[app.common.types.plugins :as ctpg]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -136,7 +137,7 @@
[:name ::sm/text]
[:path {:optional true} :string]
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:plugin-data {:optional true} ::ctpg/plugin-data]])
(def schema:library-color

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.time :as-alias ct]
[app.common.types.page :as ctp]
[app.common.types.plugins :as ctpg]
[app.common.types.variant :as ctv]
@@ -25,7 +26,7 @@
[:id ::sm/uuid]
[:name :string]
[:path {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:objects {:gen/max 10 :optional true} ctp/schema:objects]
[:main-instance-id ::sm/uuid]
[:main-instance-page ::sm/uuid]

View File

@@ -12,6 +12,7 @@
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.time :as-alias ct]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.pages-list :as ctpl]
@@ -39,7 +40,7 @@
[::sm/one-of valid-container-types]]
[:name :string]
[:path {:optional true} [:maybe :string]]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:objects {:optional true}
[:map-of {:gen/max 10} ::sm/uuid :map]]
[:plugin-data {:optional true} schema:plugin-data]]))

View File

@@ -16,7 +16,7 @@
[app.common.geom.shapes.tree-seq :as gsts]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as dt]
[app.common.time :as ct]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
@@ -34,7 +34,6 @@
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTANTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -49,8 +48,8 @@
"A schema that represents the file media object"
[:map {:title "FileMedia"}
[:id ::sm/uuid]
[:created-at {:optional true} ::sm/inst]
[:deleted-at {:optional true} ::sm/inst]
[:created-at {:optional true} ::ct/inst]
[:deleted-at {:optional true} ::ct/inst]
[:name :string]
[:width ::sm/safe-int]
[:height ::sm/safe-int]
@@ -96,9 +95,9 @@
[:name :string]
[:revn :int]
[:vern {:optional true} :int]
[:created-at ::sm/inst]
[:modified-at ::sm/inst]
[:deleted-at {:optional true} ::sm/inst]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]
[:deleted-at {:optional true} ::ct/inst]
[:project-id {:optional true} ::sm/uuid]
[:team-id {:optional true} ::sm/uuid]
[:is-shared {:optional true} ::sm/boolean]
@@ -164,7 +163,7 @@
:or {create-page true with-data true}}]
(let [id (or id (uuid/next))
created-at (or created-at (dt/now))
created-at (or created-at (ct/now))
modified-at (or modified-at created-at)
features (d/nilv features #{})

View File

@@ -13,7 +13,7 @@
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.time :as dt]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.types.token :as cto]
[app.common.uuid :as uuid]
@@ -98,7 +98,7 @@
[:type [::sm/one-of cto/token-types]]
[:value ::sm/any]
[:description {:optional true} :string]
[:modified-at {:optional true} ::sm/inst]])
[:modified-at {:optional true} ::ct/inst]])
(declare make-token)
@@ -118,7 +118,7 @@
[& {:as attrs}]
(-> attrs
(update :id #(or % (uuid/next)))
(update :modified-at #(or % (dt/now)))
(update :modified-at #(or % (ct/now)))
(update :description d/nilv "")
(check-token-attrs)
(map->Token)))
@@ -208,14 +208,14 @@
(TokenSet. id
new-name
description
(dt/now)
(ct/now)
tokens))
(set-description [_ new-description]
(TokenSet. id
name
(d/nilv new-description "")
(dt/now)
(ct/now)
tokens))
ITokenSet
@@ -231,17 +231,17 @@
(TokenSet. id
name
description
(dt/now)
(ct/now)
(assoc tokens (:name token) token))))
(update-token [this id f]
(if-let [token (token-by-id this id)]
(let [token' (-> (make-token (f token))
(assoc :modified-at (dt/now)))]
(assoc :modified-at (ct/now)))]
(TokenSet. id
name
description
(dt/now)
(ct/now)
(if (= (:name token) (:name token'))
(assoc tokens (:name token') token')
(-> tokens
@@ -254,7 +254,7 @@
(TokenSet. id
name
description
(dt/now)
(ct/now)
(dissoc tokens (:name token)))))
(get-token [this id]
@@ -279,7 +279,7 @@
[:id ::sm/uuid]
[:name :string]
[:description {:optional true} :string]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:tokens {:optional true
:gen/gen (->> (sg/map-of (sg/generator ::sm/text)
(sg/generator schema:token))
@@ -316,7 +316,7 @@
[& {:as attrs}]
(let [attrs (-> attrs
(update :id #(or % (uuid/next)))
(update :modified-at #(or % (dt/now)))
(update :modified-at #(or % (ct/now)))
(update :tokens #(into (d/ordered-map) %))
(update :description d/nilv "")
(check-token-set-attrs))]
@@ -552,7 +552,7 @@
description
is-source
external-id
(dt/now)
(ct/now)
set-names))
(enable-set [this set-name]
@@ -580,7 +580,7 @@
description
is-source
external-id
(dt/now)
(ct/now)
(conj (disj sets prev-set-name) set-name))
this))
@@ -606,7 +606,7 @@
[:description {:optional true} :string]
[:is-source {:optional true} :boolean]
[:external-id {:optional true} :string]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:sets {:optional true} [:set {:gen/max 5} :string]]])
(def schema:token-theme
@@ -640,7 +640,7 @@
(update :description d/nilv "")
(update :is-source d/nilv false)
(update :external-id #(or % (str new-id)))
(update :modified-at #(or % (dt/now)))
(update :modified-at #(or % (ct/now)))
(update :sets set)
(check-token-theme-attrs)
(map->TokenTheme))))
@@ -1070,7 +1070,7 @@ Will return a value that matches this schema:
(let [theme (dm/get-in themes [group name])]
(if theme
(let [theme' (-> (make-token-theme (f theme))
(assoc :modified-at (dt/now)))
(assoc :modified-at (ct/now)))
group' (:group theme')
name' (:name theme')
same-group? (= group group')
@@ -1490,7 +1490,7 @@ Will return a value that matches this schema:
:is-source (get theme "is-source")
:external-id (get theme "id")
:modified-at (some-> (get theme "modified-at")
(dt/parse-instant))
(ct/inst))
:sets (into #{}
(comp (map key)
xf-normalize-set-name

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.time :as-alias ct]
[app.common.types.plugins :as ctpg]
[app.common.types.text :as txt]
[app.common.uuid :as uuid]))
@@ -31,7 +32,7 @@
[:line-height :string]
[:letter-spacing :string]
[:text-transform :string]
[:modified-at {:optional true} ::sm/inst]
[:modified-at {:optional true} ::ct/inst]
[:path {:optional true} [:maybe :string]]
[:plugin-data {:optional true} ::ctpg/plugin-data]]))

View File

@@ -10,7 +10,7 @@
[clojure.test :as t]))
(t/deftest compare-time
(let [dta (dt/parse-instant 10000)
dtb (dt/parse-instant 20000)]
(let [dta (dt/inst 10000)
dtb (dt/inst 20000)]
(t/is (false? (dt/is-after? dta dtb)))
(t/is (true? (dt/is-before? dta dtb)))))

View File

@@ -12,7 +12,7 @@
#?(:clj [clojure.datafy :refer [datafy]])
[app.common.data :as d]
[app.common.test-helpers.ids-map :as thi]
[app.common.time :as dt]
[app.common.time :as ct]
[app.common.transit :as tr]
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid]
@@ -20,14 +20,14 @@
(defn setup-virtual-time
[next]
(let [current (volatile! (inst-ms (dt/now)))]
(with-redefs [dt/now #(dt/parse-instant (vswap! current inc))]
(let [current (volatile! (inst-ms (ct/now)))]
(with-redefs [ct/now #(ct/inst (vswap! current inc))]
(next))))
(t/use-fixtures :once setup-virtual-time)
(t/deftest make-token
(let [now (dt/now)
(let [now (ct/now)
token1 (ctob/make-token :id (thi/new-id! :token1)
:name "test-token-1"
:type :boolean
@@ -71,7 +71,7 @@
(t/is (= #{"foo" "bar"} (ctob/find-token-value-references "{foo}} + {bar}")))))
(t/deftest make-token-set
(let [now (dt/now)
(let [now (ct/now)
token-set1 (ctob/make-token-set :name "test-token-set-1")
token-set2 (ctob/make-token-set :name "test-token-set-2"
:description "test description"
@@ -196,7 +196,7 @@
(t/is (= (get-in expected ["baz" "boo" :name]) "baz.boo"))))
(t/deftest make-token-theme
(let [now (dt/now)
(let [now (ct/now)
token-theme1 (ctob/make-token-theme :name "test-token-theme-1")
token-theme2 (ctob/make-token-theme :name "test-token-theme-2"
:group "group-1"
@@ -266,7 +266,7 @@
(t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (ctob/get-name token-set') "test-token-set"))
(t/is (= (ctob/get-description token-set') "some description"))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest rename-token-set
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -282,7 +282,7 @@
(t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (ctob/get-name token-set') "updated-name"))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest rename-token-set-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -403,7 +403,7 @@
(t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (count (ctob/get-tokens-map token-set')) 1))
(t/is (= (:name token') "test-token"))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest update-token
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -445,8 +445,8 @@
(t/is (= (:name token') "test-token-1"))
(t/is (= (:description token') "some description"))
(t/is (= (:value token') false))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (ct/is-after? (:modified-at token') (:modified-at token)))))
(t/deftest rename-token
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -479,8 +479,8 @@
(t/is (= (:name token') "updated-name"))
(t/is (= (:description token') ""))
(t/is (= (:value token') true))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (ct/is-after? (:modified-at token') (:modified-at token)))))
(t/deftest delete-token
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -502,7 +502,7 @@
(t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (count (ctob/get-tokens-map token-set')) 0))
(t/is (nil? token'))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest get-ordered-sets
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -788,7 +788,7 @@
(t/is (= (ctob/theme-count tokens-lib') 2))
(t/is (= (:name token-theme') "test-token-theme"))
(t/is (= (:description token-theme') "some description"))
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest rename-token-theme
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -805,7 +805,7 @@
(t/is (= (ctob/theme-count tokens-lib') 2))
(t/is (= (:name token-theme') "updated-name"))
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest delete-token-theme
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -834,7 +834,7 @@
token-theme (ctob/get-theme tokens-lib "" "test-token-theme")
token-theme' (ctob/get-theme tokens-lib' "" "test-token-theme")]
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest transit-serialization
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -952,8 +952,8 @@
(t/is (= (:name token') "group1.test-token-2"))
(t/is (= (:description token') "some description"))
(t/is (= (:value token') false))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (ct/is-after? (:modified-at token') (:modified-at token)))))
(t/deftest update-token-in-sets-rename
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -989,8 +989,8 @@
(t/is (= (:name token') "group1.updated-name"))
(t/is (= (:description token') ""))
(t/is (= (:value token') true))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (:ctob/get-modified-at token-set)))
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (:ctob/get-modified-at token-set)))
(t/is (ct/is-after? (:modified-at token') (:modified-at token)))))
(t/deftest move-token-of-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1027,8 +1027,8 @@
(t/is (= (:name token') "group2.updated-name"))
(t/is (= (:description token') ""))
(t/is (= (:value token') true))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (dt/is-after? (:modified-at token') (:modified-at token)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))
(t/is (ct/is-after? (:modified-at token') (:modified-at token)))))
(t/deftest delete-token-in-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1053,7 +1053,7 @@
(t/is (= (ctob/set-count tokens-lib') 1))
(t/is (= (count (ctob/get-tokens-map token-set')) 1))
(t/is (nil? token'))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest update-token-set-in-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1079,7 +1079,7 @@
(t/is (= (d/index-of (keys group1') "S-token-set-2") 0))
(t/is (= (ctob/get-name token-set') "group1/token-set-2"))
(t/is (= (ctob/get-description token-set') "some description"))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest rename-token-set-in-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1105,7 +1105,7 @@
(t/is (= (d/index-of (keys group1') "S-updated-name") 0))
(t/is (= (ctob/get-name token-set') "group1/updated-name"))
(t/is (= (ctob/get-description token-set') ""))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest move-token-set-of-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1133,7 +1133,7 @@
(t/is (nil? (get group1' "S-updated-name")))
(t/is (= (ctob/get-name token-set') "group2/updated-name"))
(t/is (= (ctob/get-description token-set') ""))
(t/is (dt/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/is (ct/is-after? (ctob/get-modified-at token-set') (ctob/get-modified-at token-set)))))
(t/deftest delete-token-set-in-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1174,7 +1174,7 @@
(t/is (= (:name token-theme') "token-theme-2"))
(t/is (= (:group token-theme') "group1"))
(t/is (= (:description token-theme') "some description"))
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest get-token-theme-groups
(let [token-lib (-> (ctob/make-tokens-lib)
@@ -1211,7 +1211,7 @@
(t/is (= (:name token-theme') "updated-name"))
(t/is (= (:group token-theme') "group1"))
(t/is (= (:description token-theme') ""))
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest move-token-theme-of-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1241,7 +1241,7 @@
(t/is (= (:name token-theme') "updated-name"))
(t/is (= (:group token-theme') "group2"))
(t/is (= (:description token-theme') ""))
(t/is (dt/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/is (ct/is-after? (:modified-at token-theme') (:modified-at token-theme)))))
(t/deftest delete-token-theme-in-group
(let [tokens-lib (-> (ctob/make-tokens-lib)
@@ -1357,7 +1357,7 @@
#?(:clj
(t/deftest export-dtcg-json
(let [now (dt/now)
(let [now (ct/now)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "core"
:tokens {"colors.red.600"
@@ -1408,7 +1408,7 @@
#?(:clj
(t/deftest export-parse-dtcg-json
(with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")
(with-redefs [ct/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")
uuid/next (constantly uuid/zero)]
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "core"
@@ -1477,7 +1477,7 @@
#?(:clj
(t/deftest export-dtcg-json-with-active-theme-and-set
(let [now (dt/now)
(let [now (ct/now)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "core"
:tokens {"colors.red.600"
@@ -1530,7 +1530,7 @@
#?(:clj
(t/deftest export-dtcg-multi-file
(let [now (dt/now)
(let [now (ct/now)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "some/set"
:tokens {"colors.red.600"

View File

@@ -248,7 +248,7 @@ __metadata:
resolution: "common@workspace:."
dependencies:
concurrently: "npm:^9.1.2"
luxon: "npm:^3.6.1"
date-fns: "npm:^4.1.0"
nodemon: "npm:^3.1.10"
source-map-support: "npm:^0.5.21"
ws: "npm:^8.18.2"
@@ -291,6 +291,13 @@ __metadata:
languageName: node
linkType: hard
"date-fns@npm:^4.1.0":
version: 4.1.0
resolution: "date-fns@npm:4.1.0"
checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
version: 4.4.1
resolution: "debug@npm:4.4.1"
@@ -620,13 +627,6 @@ __metadata:
languageName: node
linkType: hard
"luxon@npm:^3.6.1":
version: 3.6.1
resolution: "luxon@npm:3.6.1"
checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5
languageName: node
linkType: hard
"make-fetch-happen@npm:^14.0.3":
version: 14.0.3
resolution: "make-fetch-happen@npm:14.0.3"

View File

@@ -114,7 +114,6 @@
"js-beautify": "^1.15.4",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"luxon": "^3.6.1",
"opentype.js": "^1.3.4",
"postcss-modules": "^6.0.1",
"randomcolor": "^0.6.2",

View File

@@ -10,11 +10,11 @@
[app.common.data.macros :as dm]
[app.common.files.changes :as cpc]
[app.common.logging :as log]
[app.common.time :as ct]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.helpers :as dsh]
[app.main.worker :as mw]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
@@ -118,7 +118,7 @@
source (d/nilv source :local)
local? (= source :local)
commit {:id commit-id
:created-at (dt/now)
:created-at (ct/now)
:source source
:origin (ptk/type origin)
:features features

View File

@@ -10,6 +10,7 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
[app.main.data.event :as ev]
@@ -35,8 +36,8 @@
[:seqn :int]
[:content :string]
[:participants ::sm/set-of-uuid]
[:created-at ::sm/inst]
[:modified-at ::sm/inst]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]
[:position ::gpt/point]
[:count-unread-comments {:optional true} :int]
[:count-comments {:optional true} :int]])
@@ -49,8 +50,8 @@
[:owner-id ::sm/uuid]
[:owner-fullname {:optional true} ::sm/text]
[:owner-email {:optional true} ::sm/email]
[:created-at ::sm/inst]
[:modified-at ::sm/inst]
[:created-at ::ct/inst]
[:modified-at ::ct/inst]
[:content :string]])
(def check-comment-thread!

Some files were not shown because too many files have changed in this diff Show More