Kaynağa Gözat

Add rules linter for unbound vars and invalid queries

Linter caught one unbound var which wasn't an error but nonetheless good
to cleanup
Gabriel Horner 3 yıl önce
ebeveyn
işleme
6200ec70d3

+ 3 - 0
.github/workflows/build.yml

@@ -113,6 +113,9 @@ jobs:
       - name: Lint invalid translation entries
         run: bb lang:invalid-translations
 
+      - name: Lint datalog rules
+        run: scripts/lint_rules.clj
+
   e2e-test:
     runs-on: ubuntu-latest
 

+ 52 - 0
scripts/lint_rules.clj

@@ -0,0 +1,52 @@
+#!/usr/bin/env bb
+
+(require '[babashka.deps :as deps])
+(deps/add-deps '{:deps {me.tagaholic/dlint {:mvn/version "0.1.0"}
+                        io.lambdaforge/datalog-parser {:mvn/version "0.1.11"}}
+                 :paths ["src/main"]})
+
+(ns lint-rules
+  "Lint datalog rules for parse-ability and unbound variables"
+  (:require [datalog.parser.impl :as parser-impl]
+            [dlint.core :as dlint]
+            [frontend.db.rules :as rules]))
+
+(defn- lint-unbound-rule [rule]
+  (->> (dlint/lint [rule])
+       (keep
+        (fn [[k v]]
+          (when (seq v)
+            {:success false :name k :rule rule :unbound-vars v})))))
+
+(defn- lint-rule [rule]
+  (try (parser-impl/parse-rule rule)
+    {:success true :rule rule}
+    (catch Exception e
+      {:success false :rule rule :error (.getMessage e)})))
+
+(defn- collect-logseq-rules
+  "Collects logseq rules and prepares them for linting"
+  []
+  (into rules/rules
+        (-> rules/query-dsl-rules
+            ;; TODO: Update linter to handle false positive on ?str-val
+            (dissoc :property)
+            vals)))
+
+(defn -main [rules]
+  (let [invalid-unbound-rules (->> rules
+                                   (mapcat lint-unbound-rule)
+                                   (remove :success))
+        invalid-rules (->> rules
+                           (map lint-rule)
+                           (remove :success))
+        lint-results (concat invalid-unbound-rules invalid-rules)]
+    (if (seq lint-results)
+      (do
+        (println (count lint-results) "rules failed to lint:")
+        (println lint-results)
+        (System/exit 1))
+      (println (count rules) "datalog rules linted fine!"))))
+
+(when (= *file* (System/getProperty "babashka.file"))
+  (-main (collect-logseq-rules)))

+ 4 - 4
src/main/frontend/db/rules.cljs → src/main/frontend/db/rules.cljc

@@ -1,4 +1,4 @@
-(ns frontend.db.rules)
+(ns ^:bb-compatible frontend.db.rules)
 
 (def rules
   ;; rule "parent" is optimized for child node -> parent node nesting queries
@@ -31,9 +31,9 @@
     ;; Should optimize for query the decendents of a block
     ;; Quote:
     ;; My theory is that your rules are not written in a way that Datalog can optimize for this read pattern - probably resulting in a traversal of all the entities. I suggest to rewrite them as follows:
-    ;; [[(ubersymbol ?c ?p) 
+    ;; [[(ubersymbol ?c ?p)
     ;;   (?c :ml/parent ?p)]
-    ;;  [(ubersymbol ?c ?p) 
+    ;;  [(ubersymbol ?c ?p)
     ;;   ;; we bind a child of the ancestor, instead of a parent of the descendant
     ;;   (?c1 :ml/parent ?p)
     ;;   (ubersymbol ?c ?c1)]]
@@ -93,7 +93,7 @@
 
    :all-page-tags
    '[(all-page-tags ?p)
-     [?e :block/tags ?p]]
+     [_ :block/tags ?p]]
 
    :between
    '[(between ?b ?start ?end)