validate_db.cljs 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. (ns validate-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 [babashka.cli :as cli]
  5. [cljs.pprint :as pprint]
  6. [datascript.core :as d]
  7. [logseq.db.common.sqlite-cli :as sqlite-cli]
  8. [logseq.db.frontend.malli-schema :as db-malli-schema]
  9. [logseq.db.frontend.validate :as db-validate]
  10. [malli.core :as m]
  11. [malli.error :as me]
  12. [nbb.core :as nbb]))
  13. (defn validate-db*
  14. "Validate datascript db as a vec of entity maps"
  15. [db ent-maps* {:keys [verbose group-errors humanize closed-maps]}]
  16. (let [ent-maps (db-malli-schema/update-properties-in-ents db ent-maps*)
  17. explainer (db-validate/get-schema-explainer closed-maps)]
  18. (if-let [explanation (binding [db-malli-schema/*db-for-validate-fns* db]
  19. (->> (map (fn [e] (dissoc e :db/id)) ent-maps) explainer not-empty))]
  20. (do
  21. (if group-errors
  22. (let [ent-errors (db-validate/group-errors-by-entity db ent-maps (:errors explanation))]
  23. (println "Found" (count ent-errors) "entities in errors:")
  24. (cond
  25. verbose
  26. (pprint/pprint ent-errors)
  27. humanize
  28. (pprint/pprint (map #(update % :errors (fn [errs]
  29. (->> (me/humanize {:errors errs})
  30. vals
  31. (apply merge-with into))))
  32. ent-errors))
  33. :else
  34. (pprint/pprint (map :entity ent-errors))))
  35. (let [errors (:errors explanation)]
  36. (println "Found" (count errors) "errors:")
  37. (cond
  38. verbose
  39. (pprint/pprint
  40. (map #(assoc %
  41. :entity (get ent-maps (-> % :in first))
  42. :schema (m/form (:schema %)))
  43. errors))
  44. humanize
  45. (pprint/pprint (me/humanize {:errors errors}))
  46. :else
  47. (pprint/pprint errors))))
  48. (js/process.exit 1))
  49. (println "Valid!"))))
  50. (def spec
  51. "Options spec"
  52. {:help {:alias :h
  53. :desc "Print help"}
  54. :humanize {:alias :H
  55. :default true
  56. :desc "Humanize errors as an alternative to -v"}
  57. :verbose {:alias :v
  58. :desc "Print more info"}
  59. :closed-maps {:alias :c
  60. :default true
  61. :desc "Validate maps marked with closed as :closed"}
  62. :group-errors {:alias :g
  63. :default true
  64. :desc "Groups errors by their entity id"}})
  65. (defn validate-db [db db-name options]
  66. (let [datoms (d/datoms db :eavt)
  67. ent-maps (db-malli-schema/datoms->entities datoms)]
  68. (println "Read graph" (str db-name " with counts: "
  69. (pr-str (assoc (db-validate/graph-counts db ent-maps)
  70. :datoms (count datoms)))))
  71. (validate-db* db ent-maps options)))
  72. (defn- validate-graph [graph-dir options]
  73. (let [open-db-args (sqlite-cli/->open-db-args graph-dir)
  74. db-name (if (= 1 (count open-db-args)) (first open-db-args) (second open-db-args))
  75. conn (try (apply sqlite-cli/open-db! open-db-args)
  76. (catch :default e
  77. (println "Error: For graph" (str (pr-str graph-dir) ":") (str e))
  78. (js/process.exit 1)))]
  79. (validate-db @conn db-name options)))
  80. (defn -main [argv]
  81. (let [{:keys [args opts]} (cli/parse-args argv {:spec spec})
  82. _ (when (or (empty? args) (:help opts))
  83. (println (str "Usage: $0 GRAPH-NAME [& ADDITIONAL-GRAPHS] [OPTIONS]\nOptions:\n"
  84. (cli/format-opts {:spec spec})))
  85. (js/process.exit 1))]
  86. (doseq [graph-dir args]
  87. (validate-graph graph-dir opts))))
  88. (when (= nbb/*file* (nbb/invoked-file))
  89. (-main *command-line-args*))