🐛 Accept an empty suffix on export asset items

The export form sends `""` when the user types no suffix, and the
legacy `/api/export` surface took a plain string there. The job schema
asked for `::sm/text` (not blank) instead, so every suffix-less export
died with `:params-validation` before reaching the worker. The suffix
is a free-form filename fragment the worker concatenates as-is, so the
schema takes any string now, empty included.

AI-assisted-by: glm-5.3-flash
This commit is contained in:
Andrey Antukh 2026-10-07 13:13:17 +00:00
parent e5a533daea
commit b456ca6fe0
2 changed files with 24 additions and 2 deletions

View File

@ -40,7 +40,10 @@
[:object-id ::sm/uuid]
[:type [::sm/one-of #{:png :jpeg :webp :svg :pdf}]]
[:name ::sm/text]
[:suffix {:optional true} ::sm/text]
;; a plain string on purpose, empty included: the export form sends
;; `""` when the user types no suffix, and the legacy `/api/export`
;; surface took it that way; the worker concatenates it as-is
[:suffix {:optional true} :string]
[:scale ::sm/number]
[:share-id {:optional true} ::sm/uuid]])

View File

@ -251,10 +251,29 @@
[(export-item file-id :type :psd)])]
(t/is (not (th/success? out)))
(t/is (= :data-validation (th/ex-code (:error out))))
;; refused by the RPC wrapper before the command runs (the same
;; `:params-validation` a real caller sees over HTTP)
(t/is (= :params-validation (th/ex-code (:error out))))
(t/testing "and nothing was created"
(t/is (zero? (count-jobs))))))
(t/deftest create-export-assets-job-accepts-an-empty-suffix
;; the export form sends `""` when the user types no suffix (it is the
;; normal case, not an edge): the legacy `/api/export` surface took a
;; plain string, so the job must take it too
(let [profile (th/create-profile* 1)
file-id (first (:file-ids (import-fixture! profile)))
item (assoc (export-item file-id) :suffix "")
out (create-export-assets-job (:id profile) [item])]
(t/is (th/success? out))
(t/testing "and the empty suffix is frozen as-is for the worker"
(let [job (jobs/get-job th/*system* (:id (:result out)))
job-def (jobs/get-job-def (::jobs/defs th/*system*) :export-assets)
params (jobs/decode-params job-def (:params job))]
(t/is (= "" (:suffix (first (:exports params)))))))))
(t/deftest create-export-assets-job-accepts-a-file-nameable-through-a-share
;; the viewer that renders a file through a public share exports the
;; same way every share fetch goes: the share id names the file, the