diff --git a/backend/src/app/graph/project/document.clj b/backend/src/app/graph/project/document.clj index b7310cb6f7..d859c64594 100644 --- a/backend/src/app/graph/project/document.clj +++ b/backend/src/app/graph/project/document.clj @@ -29,9 +29,14 @@ uuid/zero) (defn- document-attrs + "The Document node's attrs: the file row, minus its data blob. + + `:options` is lifted out of the blob before it goes: it is file-level + configuration a consumer wants without opening `:data`." [file data] (-> file (assoc :id (or (:id data) (:id file))) + (cond-> (:options data) (assoc :options (:options data))) (dissoc :data))) (defn- page-attrs diff --git a/backend/src/app/graph/schema/contract.clj b/backend/src/app/graph/schema/contract.clj index 7d2c74aebd..798f4ae460 100644 --- a/backend/src/app/graph/schema/contract.clj +++ b/backend/src/app/graph/schema/contract.clj @@ -107,7 +107,14 @@ ;; derivable from `x`/`y`/`width`/`height`, and a fixed-size array is a ;; tensor row a consumer reads without parsing. "selrect" "DOUBLE[4]" - "svg_viewbox" "DOUBLE[4]" + + ;; The SVG provenance attributes are typed `:map` in the shape schema on + ;; purpose — legacy files hold them as plain maps rather than as `::grc/rect` + ;; and `::gmt/matrix` records, and a tighter *schema* would reject those files + ;; (`app.common.types.shape/schema:shape-generic-attrs`). A tighter *column* + ;; is free: `app.graph.schema.values/coerce` reads either form. + "svg_viewbox" "DOUBLE[4]" + "svg_transform" "DOUBLE[6]" ;; `:fills` is an `:or` — the packed `app.common.types.fills` value or a ;; plain vector of fill maps — so the schema alone cannot say it is a diff --git a/backend/src/app/graph/schema/nodes.clj b/backend/src/app/graph/schema/nodes.clj index b80cb1ae99..2a7ff0c4ae 100644 --- a/backend/src/app/graph/schema/nodes.clj +++ b/backend/src/app/graph/schema/nodes.clj @@ -18,6 +18,7 @@ (:require [app.common.exceptions :as ex] [app.common.schema :as sm] + [app.common.time :as ct] [app.common.types.component :as ctk] [app.common.types.file :as ctf] [app.common.types.page :as ctp] @@ -33,7 +34,24 @@ ;; beadpot/graph/schemas.py drop_fields (def ^:private document-projection {:source ctf/schema:file - :drop [:data]}) + :drop [:data] + ;; Attributes a file map carries that `ctf/schema:file` does not declare. + ;; + ;; They belong here rather than in that schema, even though the graph wants + ;; them, because `schema:file` is on the *write* path too: + ;; `app.binfile.common/update-file!` derives its UPDATE columns from a file + ;; map's keys, so declaring `:backend` there made it try to write a `backend` + ;; column, which the `file` table does not have — it is synthesized on read. + ;; A projection `:extra` is local to the graph and cannot reach a write. + ;; + ;; `:options` is lifted out of `:data` before the blob is dropped + ;; (`app.graph.project.document/document-attrs`); the rest come off the file + ;; map as `get-file` returns it. + :extra [:map + [:options {:optional true} [:maybe :map]] + [:backend {:optional true} [:maybe :string]] + [:comment-thread-seqn {:optional true} [:maybe :int]] + [:ignore-sync-until {:optional true} [:maybe ::ct/inst]]]}) (def ^:private page-projection {:source ctp/schema:page