Skip to content

Commit

Permalink
Add history queries to CLJ SDK & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cap10morgan committed Apr 20, 2023
1 parent b19e945 commit caffde5
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 19 deletions.
1 change: 0 additions & 1 deletion src/fluree/db/query/fql/syntax.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
(json-ld/compact expanded kw-context)
s))}})))


(def registry
(merge
(m/predicate-schemas)
Expand Down
41 changes: 28 additions & 13 deletions src/fluree/db/query/history.cljc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns fluree.db.query.history
(:require
[clojure.core.async :as async]
[fluree.db.query.fql.syntax :as fql]
[malli.core :as m]
[fluree.json-ld :as json-ld]
[fluree.db.constants :as const]
Expand Down Expand Up @@ -60,7 +61,7 @@
pos-int?
[:re datatype/iso8601-datetime-re]]
::history-query [:and
[:map
[:map {:decode/fluree fql/decode-query}
["history" {:optional true}
[:orn
[:subject ::iri]
Expand Down Expand Up @@ -96,9 +97,23 @@
[:fn {:error/message "Must supply either a \"history\" or \"commit-details\" key."}
(fn [{:strs [history commit-details]}]
(or history commit-details))]]
::history-query-result [:fn history-query-result?]
::results-map [:map-of :string [:or
:string
[:sequential [:ref ::results-map]]
:any]]
::history-query-result [:and
::results-map
[:fn history-query-result?]]
::history-query-results [:sequential ::history-query-result]}))

(def coerce-query
(m/coercer ::history-query v/fluree-transformer {:registry registry}))

(defn results-encoder
[context]
(m/encoder ::history-query-results {:registry registry}
(fql/keyword-compact-iri-transformer context)))

(def history-query-validator
(m/validator ::history-query {:registry registry}))

Expand Down Expand Up @@ -134,16 +149,16 @@
[db cache context compact fuel error-ch s-flakes]
(async/go
(try*
(let [json-chan (json-ld-resp/flakes->res db cache context compact fuel 1000000
{:wildcard? true, :depth 0}
0 s-flakes)]
(-> (<? json-chan)
;; add the id in case the iri flake isn't present in s-flakes
(assoc "id" (-> s-flakes first flake/s (->> (dbproto/-iri db)) <?
(json-ld/compact compact)))))
(catch* e
(log/error e "Error transforming s-flakes.")
(async/>! error-ch e)))))
(let [json-chan (json-ld-resp/flakes->res db cache context compact fuel 1000000
{:wildcard? true, :depth 0}
0 s-flakes)]
(-> (<? json-chan)
;; add the id in case the iri flake isn't present in s-flakes
(assoc "id" (-> s-flakes first flake/s (->> (dbproto/-iri db)) <?
(json-ld/compact compact)))))
(catch* e
(log/error e "Error transforming s-flakes.")
(async/>! error-ch e)))))

(defn t-flakes->json-ld
"Build a collection of subject maps out of a set of flakes with the same t.
Expand All @@ -156,7 +171,7 @@
(vals)
(async/to-chan!))

s-out-ch (async/chan)]
s-out-ch (async/chan)]
(async/pipeline-async 2
s-out-ch
(fn [assert-flakes ch]
Expand Down
16 changes: 13 additions & 3 deletions src/fluree/sdk/clojure.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[fluree.db.util.validation :as v]
[fluree.db.query.fql.syntax :as fql]
[fluree.db.query.fql.parse :as fqp]
[fluree.db.query.history :as fqh]
[fluree.json-ld :as json-ld])
(:refer-clojure :exclude [load]))

Expand Down Expand Up @@ -106,6 +107,15 @@
(log/debug->>val "pre-encoded multi query results:")
encode-results))))

(defn history
[ledger query]
(api/history ledger query)))
(defn history
[ledger query]
(let [latest-db (db ledger)
context (json-ld/parse-context (fqp/parse-context query latest-db))
encode-results (fqh/results-encoder context)]
(future
(->> query
fqh/coerce-query
(api/history ledger)
deref
(log/debug->>val "pre-encoded history query results:")
encode-results))))
10 changes: 9 additions & 1 deletion test/fluree/db/test_utils.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
(:require [fluree.db.did :as did]
[fluree.db.json-ld.api :as fluree]
[fluree.db.util.core :as util :refer [try* catch*]]
#?@(:cljs [[clojure.core.async :refer [go go-loop]]
#?@(:clj [[fluree.sdk.clojure :as fluree-clj]]
:cljs [[clojure.core.async :refer [go go-loop]]
[clojure.core.async.interop :refer [<p!]]])))

(def default-context
Expand Down Expand Up @@ -116,6 +117,13 @@
(let [staged @(fluree/stage (fluree/db ledger) data)]
(fluree/commit! ledger staged commit-opts))))

#?(:clj
(defn transact-clj
([ledger data] (transact-clj ledger data {}))
([ledger data commit-opts]
(let [staged @(fluree-clj/stage (fluree-clj/db ledger) data)]
(fluree-clj/commit! ledger staged commit-opts)))))

(defn retry-promise-wrapped
"Retries a fn that when deref'd might return a Throwable. Intended for
retrying promise-wrapped API fns. Do not deref the return value, this will
Expand Down
192 changes: 191 additions & 1 deletion test/fluree/sdk/clojure_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@
{:f/property [:* {:f/allow [:* {:f/targetRole ["_id"]}]}]}]}
:where [[?s :rdf/type :f/Policy]]})))))))))

(deftest ^:integration multi-query-test
(deftest multi-query-test
(let [conn (test-utils/create-conn
{:context (merge test-utils/default-context
{"ex" "http://example.org/ns/"})})
Expand All @@ -455,3 +455,193 @@
:schema/name "Brian"}]}
subject)
"returns all results in a map keyed by alias.")))))

(deftest history-query-test
(let [ts-primeval (util/current-time-iso)

conn (test-utils/create-conn)
ledger @(fluree/create conn "historytest"
{:defaults
{:context
["" {:ex "http://example.org/ns/"}]}})

db1 @(test-utils/transact-clj ledger [{:id :ex/dan
:ex/x "foo-1"
:ex/y "bar-1"}
{:id :ex/cat
:ex/x "foo-1"
:ex/y "bar-1"}
{:id :ex/dog
:ex/x "foo-1"
:ex/y "bar-1"}])
db2 @(test-utils/transact-clj ledger {:id :ex/dan
:ex/x "foo-2"
:ex/y "bar-2"})
ts2 (-> db2 :commit :time)
db3 @(test-utils/transact-clj ledger {:id :ex/dan
:ex/x "foo-3"
:ex/y "bar-3"})

ts3 (-> db3 :commit :time)
db4 @(test-utils/transact-clj ledger [{:id :ex/cat
:ex/x "foo-cat"
:ex/y "bar-cat"}
{:id :ex/dog
:ex/x "foo-dog"
:ex/y "bar-dog"}])
db5 @(test-utils/transact-clj ledger {:id :ex/dan
:ex/x "foo-cat"
:ex/y "bar-cat"})]
(testing "subject history"
(is (= [{:f/t 1
:f/assert [{:id :ex/dan :ex/x "foo-1" :ex/y "bar-1"}]
:f/retract []}
{:f/t 2
:f/assert [{:id :ex/dan :ex/x "foo-2" :ex/y "bar-2"}]
:f/retract [{:id :ex/dan :ex/x "foo-1" :ex/y "bar-1"}]}
{:f/t 3
:f/assert [{:id :ex/dan :ex/x "foo-3" :ex/y "bar-3"}]
:f/retract [{:id :ex/dan :ex/x "foo-2" :ex/y "bar-2"}]}
{:f/t 5
:f/assert [{:id :ex/dan :ex/x "foo-cat" :ex/y "bar-cat"}]
:f/retract [{:id :ex/dan :ex/x "foo-3" :ex/y "bar-3"}]}]
@(fluree/history ledger {:history :ex/dan, :t {:from 1}}))))
(testing "one-tuple flake history"
(is (= [{:f/t 1
:f/assert [{:id :ex/dan :ex/x "foo-1" :ex/y "bar-1"}]
:f/retract []}
{:f/t 2
:f/assert [{:ex/x "foo-2" :ex/y "bar-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :ex/y "bar-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :ex/y "bar-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :ex/y "bar-2" :id :ex/dan}]}
{:f/t 5
:f/assert [{:ex/x "foo-cat" :ex/y "bar-cat" :id :ex/dan}]
:f/retract [{:ex/x "foo-3" :ex/y "bar-3" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan] :t {:from 1}}))))
(testing "two-tuple flake history"
(is (= [{:f/t 1 :f/assert [{:ex/x "foo-1" :id :ex/dan}] :f/retract []}
{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}
{:f/t 5
:f/assert [{:ex/x "foo-cat" :id :ex/dan}]
:f/retract [{:ex/x "foo-3" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:from 1}})))

(is (= [{:f/t 1 :f/assert [{:ex/x "foo-1" :id :ex/dog}
{:ex/x "foo-1" :id :ex/cat}
{:ex/x "foo-1" :id :ex/dan}]
:f/retract []}
{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}
{:f/t 4
:f/assert [{:ex/x "foo-dog" :id :ex/dog}
{:ex/x "foo-cat" :id :ex/cat}]
:f/retract [{:ex/x "foo-1" :id :ex/dog}
{:ex/x "foo-1" :id :ex/cat}]}
{:f/t 5
:f/assert [{:ex/x "foo-cat" :id :ex/dan}]
:f/retract [{:ex/x "foo-3" :id :ex/dan}]}]
@(fluree/history ledger {:history [nil :ex/x] :t {:from 1}}))))
(testing "three-tuple flake history"
(is (= [{:f/t 4 :f/assert [{:ex/x "foo-cat" :id :ex/cat}] :f/retract []}
{:f/t 5 :f/assert [{:ex/x "foo-cat" :id :ex/dan}] :f/retract []}]
@(fluree/history ledger {:history [nil :ex/x "foo-cat"] :t {:from 1}})))
(is (= [{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract []}
{:f/t 3
:f/assert []
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}]
@(fluree/history ledger {:history [nil :ex/x "foo-2"] :t {:from 1}})))
(is (= [{:f/t 5 :f/assert [{:ex/x "foo-cat" :id :ex/dan}] :f/retract []}]
@(fluree/history ledger {:history [:ex/dan :ex/x "foo-cat"] :t {:from 1}}))))

(testing "at-t"
(let [expected [{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}]]
(is (= expected
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:from 3 :to 3}})))
(is (= expected
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:at 3}})))))
(testing "from-t"
(is (= [{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}
{:f/t 5
:f/assert [{:ex/x "foo-cat" :id :ex/dan}]
:f/retract [{:ex/x "foo-3" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:from 3}}))))
(testing "to-t"
(is (= [{:f/t 1
:f/assert [{:ex/x "foo-1" :id :ex/dan}]
:f/retract []}
{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:to 3}}))))
(testing "t-range"
(is (= [{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}
{:f/t 4
:f/assert [{:ex/x "foo-dog" :id :ex/dog} {:ex/x "foo-cat" :id :ex/cat}]
:f/retract [{:ex/x "foo-1" :id :ex/dog} {:ex/x "foo-1" :id :ex/cat}]}]
@(fluree/history ledger {:history [nil :ex/x] :t {:from 2 :to 4}}))))
(testing "datetime-t"
(is (= [{:f/t 2
:f/assert [{:ex/x "foo-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :id :ex/dan}]}
{:f/t 3
:f/assert [{:ex/x "foo-3" :id :ex/dan}]
:f/retract [{:ex/x "foo-2" :id :ex/dan}]}]
@(fluree/history ledger {:history [nil :ex/x] :t {:from ts2 :to ts3}}))
"does not include t 1 4 or 5")
(is (= [{:f/t 5
:f/assert [{:ex/x "foo-cat" :id :ex/dan}]
:f/retract [{:ex/x "foo-3" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan :ex/x] :t {:from (util/current-time-iso)}}))
"timestamp translates to first t before ts")

(is (= (str "There is no data as of " ts-primeval)
(-> @(fluree/history ledger {:history [:ex/dan :ex/x] :t {:from ts-primeval}})
(Throwable->map)
:cause))))

(testing "invalid query"
(is (thrown? Exception @(fluree/history ledger {:history []}))))

(testing "small cache"
(let [conn (test-utils/create-conn)
ledger @(fluree/create conn "historycachetest"
{:defaults
{:context
["" {:ex "http://example.org/ns/"}]}})

db1 @(test-utils/transact-clj ledger [{:id :ex/dan
:ex/x "foo-1"
:ex/y "bar-1"}])
db2 @(test-utils/transact-clj ledger {:id :ex/dan
:ex/x "foo-2"
:ex/y "bar-2"})]
(testing "no t-range cache collision"
(is (= [{:f/t 2
:f/assert [{:ex/x "foo-2" :ex/y "bar-2" :id :ex/dan}]
:f/retract [{:ex/x "foo-1" :ex/y "bar-1" :id :ex/dan}]}]
@(fluree/history ledger {:history [:ex/dan] :t {:from 2}}))))))))

0 comments on commit caffde5

Please sign in to comment.