mirror of https://github.com/status-im/pluto.git
Validate queries/events arguments
This commit is contained in:
parent
214adccbdb
commit
4de10d1d4b
|
@ -12,17 +12,35 @@
|
||||||
(defn component [])
|
(defn component [])
|
||||||
|
|
||||||
(def ctx
|
(def ctx
|
||||||
{:capacities {:components {'text {:value component} 'view {:value component} 'token-selector {:value component} 'asset-selector {:value component}
|
{:capacities {:components {'button {:value component :properties {:on-click :event}}
|
||||||
|
'text-input {:value component}
|
||||||
|
'text {:value component} 'view {:value component} 'token-selector {:value component} 'asset-selector {:value component}
|
||||||
'transaction-status {:value component :properties {:outgoing :string :tx-hash :string}}
|
'transaction-status {:value component :properties {:outgoing :string :tx-hash :string}}
|
||||||
'nft-token-viewer {:value component :properties {:token :string}}}
|
'nft-token-viewer {:value component :properties {:token :string}}}
|
||||||
:queries {'get-collectible-token {:value :get-collectible-token}}
|
:queries {'get-collectible-token {:value :get-collectible-token}
|
||||||
:hooks {:commands {:properties {:scope #{:personal-chats :public-chats}
|
'store/get {:value :store/get}}
|
||||||
|
:events {'alert
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :alert}
|
||||||
|
'log
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :log}
|
||||||
|
'store/put
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :store/put}
|
||||||
|
'http/get
|
||||||
|
{:permissions [:read]
|
||||||
|
:value :http/get}}
|
||||||
|
:hooks {:commands {:properties {:description? :string
|
||||||
|
:scope #{:personal-chats :public-chats}
|
||||||
:short-preview :view
|
:short-preview :view
|
||||||
:preview :view
|
:preview :view
|
||||||
:parameters [{:id :keyword
|
:parameters [{:id :keyword
|
||||||
:type {:one-of #{:text :phone :password :number}}
|
:type {:one-of #{:text :phone :password :number}}
|
||||||
:placeholder :string
|
:placeholder :string
|
||||||
:suggestions? :view}]}}}}})
|
:suggestions? :view}]
|
||||||
|
:on-send? :event
|
||||||
|
:on-receive? :event}}}}})
|
||||||
|
|
||||||
(defn ^:export parse [m]
|
(defn ^:export parse [m]
|
||||||
(reader/parse ctx (:data m)))
|
(reader/parse ctx (:data m)))
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
(defn resolve-binding-value [v]
|
(defn resolve-binding-value [v]
|
||||||
(cond
|
(cond
|
||||||
(fn? v) @(v) ;; TODO better abstract query
|
(vector? v) @(re-frame/subscribe v) ;; TODO better abstract query
|
||||||
(not (list? v)) v))
|
(not (list? v)) v))
|
||||||
|
|
||||||
(defn resolve-binding-key [k v]
|
(defn resolve-binding-key [k v]
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
::unknown-component-property
|
::unknown-component-property
|
||||||
::unknown-query
|
::unknown-query
|
||||||
::unknown-event
|
::unknown-event
|
||||||
|
::invalid-component-property-type
|
||||||
::invalid-view
|
::invalid-view
|
||||||
::invalid-property-map
|
::invalid-property-map
|
||||||
::invalid-block
|
::invalid-block
|
||||||
|
|
|
@ -29,17 +29,23 @@
|
||||||
(or (get ext (symbol ns (name s)))
|
(or (get ext (symbol ns (name s)))
|
||||||
(get-in ctx [:capacities (get type->capacity type) s :value])))
|
(get-in ctx [:capacities (get type->capacity type) s :value])))
|
||||||
|
|
||||||
|
(defn valid-reference? [[name arguments :as value]]
|
||||||
|
(and (symbol? name)
|
||||||
|
(>= 2 (count value))
|
||||||
|
(or (nil? arguments) (map? arguments) (symbol? arguments))))
|
||||||
|
|
||||||
(defn resolve
|
(defn resolve
|
||||||
"Resolve a reference defined by a hook
|
"Resolve a reference defined by a hook
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(= {:data \"view\"} (resolve {'views/id \"view\"} :view ['id]))
|
(= {:data \"view\"} (resolve {} {'views/id \"view\"} :view ['id]))
|
||||||
```"
|
```"
|
||||||
[ctx ext type value]
|
[ctx ext type value]
|
||||||
(if-let [s (reference->symbol value)]
|
(if (valid-reference? value)
|
||||||
(if-let [ns (get type->ns type)]
|
(let [s (reference->symbol value)]
|
||||||
(if-let [o (resolve-symbol ctx ext type ns s)]
|
(if-let [ns (get type->ns type)]
|
||||||
{:data o}
|
(if-let [o (resolve-symbol ctx ext type ns s)]
|
||||||
{:errors [(errors/error ::errors/unknown-reference {:value s})]})
|
{:data o}
|
||||||
{:errors [(errors/error ::errors/unknown-reference-type {:value type})]})
|
{:errors [(errors/error ::errors/unknown-reference {:value s :type type})]})
|
||||||
{:errors [(errors/error ::errors/invalid-reference {:value value})]}))
|
{:errors [(errors/error ::errors/unknown-reference-type {:value type})]}))
|
||||||
|
{:errors [(errors/error ::errors/invalid-reference {:type type :value value})]}))
|
||||||
|
|
|
@ -8,18 +8,26 @@
|
||||||
[pluto.reader.errors :as errors]
|
[pluto.reader.errors :as errors]
|
||||||
[pluto.reader.reference :as reference]))
|
[pluto.reader.reference :as reference]))
|
||||||
|
|
||||||
|
(def reference-types #{:view :event :query})
|
||||||
|
|
||||||
(defmulti resolve
|
(defmulti resolve
|
||||||
"Resolve a value based on a type.
|
"Resolve a value based on a type.
|
||||||
Returns a map of either:
|
Returns a map of either:
|
||||||
* data with the resolved data
|
* data with the resolved data
|
||||||
* errors encapsulating all errors generated during resolution"
|
* errors encapsulating all errors generated during resolution"
|
||||||
(fn [ctx ext type value]
|
(fn [ctx ext type value]
|
||||||
(cond
|
(if (symbol? value)
|
||||||
(keyword? type) type
|
:symbol
|
||||||
(:one-of type) :one-of
|
(cond
|
||||||
(set? type) :subset
|
(keyword? type) type
|
||||||
(map? type) :assoc
|
(:one-of type) :one-of
|
||||||
(vector? type) :sequence)))
|
(set? type) :subset
|
||||||
|
(map? type) :assoc
|
||||||
|
(vector? type) :sequence))))
|
||||||
|
|
||||||
|
(defmethod resolve :symbol [_ _ _ value]
|
||||||
|
;; TODO properly validate symbols based on inferred type
|
||||||
|
{:data value})
|
||||||
|
|
||||||
(defn invalid-type-value [type value]
|
(defn invalid-type-value [type value]
|
||||||
(errors/error ::errors/invalid-type-value {:type type :value value}))
|
(errors/error ::errors/invalid-type-value {:type type :value value}))
|
||||||
|
@ -73,21 +81,26 @@
|
||||||
{} type)
|
{} type)
|
||||||
{:errors [(errors/error ::errors/invalid-assoc-type {:type type :value value})]}))
|
{:errors [(errors/error ::errors/invalid-assoc-type {:type type :value value})]}))
|
||||||
|
|
||||||
(defn- resolve-reference [ctx ext type [name _ :as value] f error]
|
(defn- resolve-arguments [ctx ext key data arguments]
|
||||||
|
(resolve ctx ext (get-in ctx [:capacities key data :arguments]) arguments))
|
||||||
|
|
||||||
|
(defn- reference-with-arguments [ctx ext ref key name arguments]
|
||||||
|
(if arguments
|
||||||
|
(let [{:keys [data errors]} (resolve-arguments ctx ext key name arguments)]
|
||||||
|
(errors/merge-errors {:data [ref data]} errors))
|
||||||
|
{:data [ref]}))
|
||||||
|
|
||||||
|
(defn resolve-reference [ctx ext type [name arguments :as value] key error]
|
||||||
(let [{:keys [data errors]} (reference/resolve ctx ext type value)]
|
(let [{:keys [data errors]} (reference/resolve ctx ext type value)]
|
||||||
(merge (when data {:data (f data)})
|
(merge (when data (reference-with-arguments ctx ext data key name arguments))
|
||||||
(when errors
|
(when errors
|
||||||
{:errors (apply conj [(errors/error error name)] errors)}))))
|
{:errors (apply conj [(errors/error error name)] errors)}))))
|
||||||
|
|
||||||
(defmethod resolve :event [ctx ext type [_ properties :as value]]
|
(defmethod resolve :event [ctx ext type [name arguments :as value]]
|
||||||
(resolve-reference ctx ext type value
|
(resolve-reference ctx ext type value :events ::errors/unknown-event))
|
||||||
(fn [data] #(re-frame/dispatch (if properties [data properties] [data])))
|
|
||||||
::errors/unknown-event))
|
|
||||||
|
|
||||||
(defmethod resolve :query [ctx ext type [name properties :as value]]
|
(defmethod resolve :query [ctx ext type [name arguments :as value]]
|
||||||
(resolve-reference ctx ext type value
|
(resolve-reference ctx ext type value :queries ::errors/unknown-query))
|
||||||
(fn [data] #(re-frame/subscribe (if properties [data properties] [data])))
|
|
||||||
::errors/unknown-query))
|
|
||||||
|
|
||||||
(defmethod resolve :default [_ _ type value]
|
(defmethod resolve :default [_ _ type value]
|
||||||
{:errors [(errors/error ::errors/invalid-type (merge {:type type} (when value {:value value})))]})
|
{:errors [(errors/error ::errors/invalid-type (merge {:type type} (when value {:value value})))]})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns pluto.reader.views
|
(ns pluto.reader.views
|
||||||
(:require [clojure.spec.alpha :as spec]
|
(:require [clojure.spec.alpha :as spec]
|
||||||
|
[re-frame.core :as re-frame]
|
||||||
[pluto.reader.blocks :as blocks]
|
[pluto.reader.blocks :as blocks]
|
||||||
[pluto.reader.destructuring :as destructuring]
|
[pluto.reader.destructuring :as destructuring]
|
||||||
[pluto.reader.errors :as errors]
|
[pluto.reader.errors :as errors]
|
||||||
|
@ -43,7 +44,9 @@
|
||||||
(fn? o) o
|
(fn? o) o
|
||||||
(symbol? o) (get-in ctx [:capacities :components o :value])))
|
(symbol? o) (get-in ctx [:capacities :components o :value])))
|
||||||
|
|
||||||
(defmulti resolve-default-component-properties (fn [property value] property))
|
(defmulti resolve-default-component-properties
|
||||||
|
"Resolve default properties available for all components."
|
||||||
|
(fn [property value] property))
|
||||||
|
|
||||||
(defmethod resolve-default-component-properties :style [_ value]
|
(defmethod resolve-default-component-properties :style [_ value]
|
||||||
{:data value})
|
{:data value})
|
||||||
|
@ -51,12 +54,23 @@
|
||||||
(defmethod resolve-default-component-properties :default [_ value]
|
(defmethod resolve-default-component-properties :default [_ value]
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(defn resolve-existing-component-property-type [ctx ext type v]
|
||||||
|
(let [{:keys [data errors] :as t} (types/resolve ctx ext type v)]
|
||||||
|
(if (= :event type)
|
||||||
|
(errors/merge-errors {:data #(re-frame/dispatch data)} errors)
|
||||||
|
t)))
|
||||||
|
|
||||||
|
(defn resolve-custom-component-properties [ctx ext component k v]
|
||||||
|
(if-let [type (get-in ctx [:capacities :components component :properties k])]
|
||||||
|
(if-not (and (types/reference-types type) (not= :event type))
|
||||||
|
;; TODO Infer symbol types and fail if type does not match
|
||||||
|
(if (symbol? v) v (resolve-existing-component-property-type ctx ext type v))
|
||||||
|
{:errors [(errors/error ::errors/invalid-component-property-type {:component component :property k :type type})]})
|
||||||
|
{:errors [(errors/error ::errors/unknown-component-property {:component component :property k})]}))
|
||||||
|
|
||||||
(defn- resolve-component-property [ctx ext component k v]
|
(defn- resolve-component-property [ctx ext component k v]
|
||||||
(or (resolve-default-component-properties k v)
|
(or (resolve-default-component-properties k v)
|
||||||
(if-let [type (get-in ctx [:capacities :components component :properties k])]
|
(resolve-custom-component-properties ctx ext component k v)))
|
||||||
;; TODO Infer symbol types and fail if type does not match
|
|
||||||
(if (symbol? v) v (types/resolve ctx ext type v))
|
|
||||||
{:errors [(errors/error ::errors/unknown-component-property {:component component :property k})]})))
|
|
||||||
|
|
||||||
(defn- resolve-property [ctx ext component k v]
|
(defn- resolve-property [ctx ext component k v]
|
||||||
(if (component? component)
|
(if (component? component)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
(is (= {:data [blocks/let-block {:env '{a {:b 1} b 1}} 'b]}
|
(is (= {:data [blocks/let-block {:env '{a {:b 1} b 1}} 'b]}
|
||||||
(blocks/parse {} {} '(let [{a :a} {:a {:b 1}} {b :b} a] b))))
|
(blocks/parse {} {} '(let [{a :a} {:a {:b 1}} {b :b} a] b))))
|
||||||
(is (empty?
|
(is (empty?
|
||||||
(:errors (blocks/parse {:capacities {:queries {'aa {:value :a}}}} {} '(let [x 1 {a :a} [aa {:x x}]] a)))))
|
(:errors (blocks/parse {:capacities {:queries {'aa {:value :a :arguments {:x :string}}}}} {} '(let [x 1 {a :a} [aa {:x x}]] a)))))
|
||||||
|
|
||||||
(is (= {:data [blocks/let-block {:env {'s "Hello"}} ['test {} 's]]}
|
(is (= {:data [blocks/let-block {:env {'s "Hello"}} ['test {} 's]]}
|
||||||
(blocks/parse {} {} (list 'let ['s "Hello"] ['test {} 's]))))
|
(blocks/parse {} {} (list 'let ['s "Hello"] ['test {} 's]))))
|
||||||
|
|
|
@ -17,12 +17,22 @@
|
||||||
(is (= 'test (reference/reference->symbol ['test])))
|
(is (= 'test (reference/reference->symbol ['test])))
|
||||||
(is (= 'views/id (reference/reference->symbol ['views/id {}]))))
|
(is (= 'views/id (reference/reference->symbol ['views/id {}]))))
|
||||||
|
|
||||||
|
(deftest valid-reference?
|
||||||
|
(is (false? (reference/valid-reference? "")))
|
||||||
|
(is (true? (reference/valid-reference? ['test])))
|
||||||
|
(is (true? (reference/valid-reference? ['views/id {}])))
|
||||||
|
(is (false? (reference/valid-reference? ['views/id {} {}])))
|
||||||
|
(is (false? (reference/valid-reference? ['views/id 1 {}])))
|
||||||
|
(is (false? (reference/valid-reference? ["id" {}])))
|
||||||
|
(is (true? (reference/valid-reference? ['views/id {}])))
|
||||||
|
(is (true? (reference/valid-reference? ['views/id 'arg]))))
|
||||||
|
|
||||||
(deftest resolve
|
(deftest resolve
|
||||||
(is (= {:errors [{::errors/type ::errors/unknown-reference
|
(is (= {:errors [{::errors/type ::errors/unknown-reference
|
||||||
::errors/value {:value 'id}}]}
|
::errors/value {:value 'id :type :view}}]}
|
||||||
(reference/resolve {} {} :view ['id])))
|
(reference/resolve {} {} :view ['id])))
|
||||||
(is (= {:errors [{::errors/type ::errors/invalid-reference
|
(is (= {:errors [{::errors/type ::errors/invalid-reference
|
||||||
::errors/value {:value ""}}]}
|
::errors/value {:value "" :type :view}}]}
|
||||||
(reference/resolve {} {'views/id "view"} :view "")))
|
(reference/resolve {} {'views/id "view"} :view "")))
|
||||||
(is (= {:errors [{::errors/type ::errors/unknown-reference-type
|
(is (= {:errors [{::errors/type ::errors/unknown-reference-type
|
||||||
::errors/value {:value :unknown}}]}
|
::errors/value {:value :unknown}}]}
|
||||||
|
|
|
@ -106,8 +106,12 @@
|
||||||
(is (= {:errors [{::errors/type ::errors/unknown-event
|
(is (= {:errors [{::errors/type ::errors/unknown-event
|
||||||
::errors/value 'event}
|
::errors/value 'event}
|
||||||
{::errors/type ::errors/unknown-reference
|
{::errors/type ::errors/unknown-reference
|
||||||
::errors/value {:value 'event}}]}
|
::errors/value {:value 'event :type :event}}]}
|
||||||
(types/resolve {} {} :event ['event])))
|
(types/resolve {} {} :event ['event])))
|
||||||
(let [{:keys [data errors]} (types/resolve {:capacities {:events {'event {:value :event}}}} {} :event ['event])]
|
(let [{:keys [data errors]} (types/resolve {:capacities {:events {'event {:value :event}}}} {} :event ['event])]
|
||||||
(is (not errors))
|
(is (not errors))
|
||||||
(is data)))
|
(is data))
|
||||||
|
(let [{:keys [data errors]} (types/resolve {:capacities {:events {'event {:value :event :arguments {:on-finished? :event}}}}}
|
||||||
|
{} :event ['event {:on-finished ['event]}])]
|
||||||
|
(is (not errors))
|
||||||
|
(is (= [:event {:on-finished [:event]}] data))))
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
(deftest resolve
|
(deftest resolve
|
||||||
(is (= [:text "Hello"] ((:data (types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/main])) {})))
|
(is (= [:text "Hello"] ((:data (types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/main])) {})))
|
||||||
(is (= {:errors [{::errors/type ::errors/unknown-reference,
|
(is (= {:errors [{::errors/type ::errors/unknown-reference,
|
||||||
::errors/value {:value 'views/unknown}}]}
|
::errors/value {:value 'views/unknown :type :view}}]}
|
||||||
(types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/unknown]))))
|
(types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/unknown]))))
|
||||||
|
|
||||||
(deftest invalid-view-element-spec-errors
|
(deftest invalid-view-element-spec-errors
|
||||||
|
|
Loading…
Reference in New Issue