validate_client_db.cljs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. (ns validate-client-db
  2. "Script that validates the datascript db of a DB graph
  3. NOTE: This script is also used in CI to confirm our db's schema is up to date"
  4. (:require ["os" :as os]
  5. ["path" :as node-path]
  6. [babashka.cli :as cli]
  7. [cljs.pprint :as pprint]
  8. [clojure.string :as string]
  9. [datascript.core :as d]
  10. [logseq.db.frontend.malli-schema :as db-malli-schema]
  11. [logseq.db.frontend.validate :as db-validate]
  12. [logseq.db.sqlite.cli :as sqlite-cli]
  13. [malli.core :as m]
  14. [malli.error :as me]
  15. [nbb.core :as nbb]))
  16. (defn validate-client-db
  17. "Validate datascript db as a vec of entity maps"
  18. [db ent-maps* {:keys [verbose group-errors humanize closed-maps]}]
  19. (let [ent-maps (db-malli-schema/update-properties-in-ents db ent-maps*)
  20. explainer (db-validate/get-schema-explainer closed-maps)]
  21. (if-let [explanation (binding [db-malli-schema/*db-for-validate-fns* db]
  22. (->> (map (fn [e] (dissoc e :db/id)) ent-maps) explainer not-empty))]
  23. (do
  24. (if group-errors
  25. (let [ent-errors (->> (db-validate/group-errors-by-entity db ent-maps (:errors explanation))
  26. (map #(assoc %
  27. :dispatch-key
  28. (->> (dissoc (:entity %) :db/id) (db-malli-schema/entity-dispatch-key db)))))]
  29. (println "Found" (count ent-errors) "entities in errors:")
  30. (cond
  31. verbose
  32. (pprint/pprint ent-errors)
  33. humanize
  34. (pprint/pprint (map #(-> (dissoc % :errors-by-type)
  35. (update :errors (fn [errs] (me/humanize {:errors errs}))))
  36. ent-errors))
  37. :else
  38. (pprint/pprint (map :entity ent-errors))))
  39. (let [errors (:errors explanation)]
  40. (println "Found" (count errors) "errors:")
  41. (cond
  42. verbose
  43. (pprint/pprint
  44. (map #(assoc %
  45. :entity (get ent-maps (-> % :in first))
  46. :schema (m/form (:schema %)))
  47. errors))
  48. humanize
  49. (pprint/pprint (me/humanize {:errors errors}))
  50. :else
  51. (pprint/pprint errors))))
  52. (js/process.exit 1))
  53. (println "Valid!"))))
  54. (def spec
  55. "Options spec"
  56. {:help {:alias :h
  57. :desc "Print help"}
  58. :humanize {:alias :H
  59. :default true
  60. :desc "Humanize errors as an alternative to -v"}
  61. :verbose {:alias :v
  62. :desc "Print more info"}
  63. :closed-maps {:alias :c
  64. :default true
  65. :desc "Validate maps marked with closed as :closed"}
  66. :group-errors {:alias :g
  67. :default true
  68. :desc "Groups errors by their entity id"}})
  69. (defn- validate-graph [graph-dir options]
  70. (let [[dir db-name] (if (string/includes? graph-dir "/")
  71. (let [graph-dir'
  72. (node-path/join (or js/process.env.ORIGINAL_PWD ".") graph-dir)]
  73. ((juxt node-path/dirname node-path/basename) graph-dir'))
  74. [(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
  75. conn (try (sqlite-cli/open-db! dir db-name)
  76. (catch :default e
  77. (println "Error: For graph" (str (pr-str graph-dir) ":") (str e))
  78. (js/process.exit 1)))
  79. datoms (d/datoms @conn :eavt)
  80. ent-maps (db-malli-schema/datoms->entities datoms)]
  81. (println "Read graph" (str db-name " with counts: "
  82. (pr-str (assoc (db-validate/graph-counts @conn ent-maps)
  83. :datoms (count datoms)))))
  84. (validate-client-db @conn ent-maps options)))
  85. (defn -main [argv]
  86. (let [{:keys [args opts]} (cli/parse-args argv {:spec spec})
  87. _ (when (or (empty? args) (:help opts))
  88. (println (str "Usage: $0 GRAPH-NAME [& ADDITIONAL-GRAPHS] [OPTIONS]\nOptions:\n"
  89. (cli/format-opts {:spec spec})))
  90. (js/process.exit 1))]
  91. (doseq [graph-dir args]
  92. (validate-graph graph-dir opts))))
  93. (when (= nbb/*file* (:file (meta #'-main)))
  94. (-main *command-line-args*))