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 [])
|
||||
|
||||
(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}}
|
||||
'nft-token-viewer {:value component :properties {:token :string}}}
|
||||
:queries {'get-collectible-token {:value :get-collectible-token}}
|
||||
:hooks {:commands {:properties {:scope #{:personal-chats :public-chats}
|
||||
:queries {'get-collectible-token {:value :get-collectible-token}
|
||||
'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
|
||||
:preview :view
|
||||
:parameters [{:id :keyword
|
||||
:type {:one-of #{:text :phone :password :number}}
|
||||
:placeholder :string
|
||||
:suggestions? :view}]}}}}})
|
||||
:suggestions? :view}]
|
||||
:on-send? :event
|
||||
:on-receive? :event}}}}})
|
||||
|
||||
(defn ^:export parse [m]
|
||||
(reader/parse ctx (:data m)))
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
(defn resolve-binding-value [v]
|
||||
(cond
|
||||
(fn? v) @(v) ;; TODO better abstract query
|
||||
(vector? v) @(re-frame/subscribe v) ;; TODO better abstract query
|
||||
(not (list? v)) v))
|
||||
|
||||
(defn resolve-binding-key [k v]
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
::unknown-component-property
|
||||
::unknown-query
|
||||
::unknown-event
|
||||
::invalid-component-property-type
|
||||
::invalid-view
|
||||
::invalid-property-map
|
||||
::invalid-block
|
||||
|
|
|
@ -29,17 +29,23 @@
|
|||
(or (get ext (symbol ns (name s)))
|
||||
(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
|
||||
"Resolve a reference defined by a hook
|
||||
|
||||
```clojure
|
||||
(= {:data \"view\"} (resolve {'views/id \"view\"} :view ['id]))
|
||||
(= {:data \"view\"} (resolve {} {'views/id \"view\"} :view ['id]))
|
||||
```"
|
||||
[ctx ext type value]
|
||||
(if-let [s (reference->symbol value)]
|
||||
(if-let [ns (get type->ns type)]
|
||||
(if-let [o (resolve-symbol ctx ext type ns s)]
|
||||
{:data o}
|
||||
{:errors [(errors/error ::errors/unknown-reference {:value s})]})
|
||||
{:errors [(errors/error ::errors/unknown-reference-type {:value type})]})
|
||||
{:errors [(errors/error ::errors/invalid-reference {:value value})]}))
|
||||
(if (valid-reference? value)
|
||||
(let [s (reference->symbol value)]
|
||||
(if-let [ns (get type->ns type)]
|
||||
(if-let [o (resolve-symbol ctx ext type ns s)]
|
||||
{:data o}
|
||||
{:errors [(errors/error ::errors/unknown-reference {:value s :type type})]})
|
||||
{: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.reference :as reference]))
|
||||
|
||||
(def reference-types #{:view :event :query})
|
||||
|
||||
(defmulti resolve
|
||||
"Resolve a value based on a type.
|
||||
Returns a map of either:
|
||||
* data with the resolved data
|
||||
* errors encapsulating all errors generated during resolution"
|
||||
(fn [ctx ext type value]
|
||||
(cond
|
||||
(keyword? type) type
|
||||
(:one-of type) :one-of
|
||||
(set? type) :subset
|
||||
(map? type) :assoc
|
||||
(vector? type) :sequence)))
|
||||
(if (symbol? value)
|
||||
:symbol
|
||||
(cond
|
||||
(keyword? type) type
|
||||
(:one-of type) :one-of
|
||||
(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]
|
||||
(errors/error ::errors/invalid-type-value {:type type :value value}))
|
||||
|
@ -73,21 +81,26 @@
|
|||
{} type)
|
||||
{: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)]
|
||||
(merge (when data {:data (f data)})
|
||||
(when errors
|
||||
{:errors (apply conj [(errors/error error name)] errors)}))))
|
||||
(merge (when data (reference-with-arguments ctx ext data key name arguments))
|
||||
(when errors
|
||||
{:errors (apply conj [(errors/error error name)] errors)}))))
|
||||
|
||||
(defmethod resolve :event [ctx ext type [_ properties :as value]]
|
||||
(resolve-reference ctx ext type value
|
||||
(fn [data] #(re-frame/dispatch (if properties [data properties] [data])))
|
||||
::errors/unknown-event))
|
||||
(defmethod resolve :event [ctx ext type [name arguments :as value]]
|
||||
(resolve-reference ctx ext type value :events ::errors/unknown-event))
|
||||
|
||||
(defmethod resolve :query [ctx ext type [name properties :as value]]
|
||||
(resolve-reference ctx ext type value
|
||||
(fn [data] #(re-frame/subscribe (if properties [data properties] [data])))
|
||||
::errors/unknown-query))
|
||||
(defmethod resolve :query [ctx ext type [name arguments :as value]]
|
||||
(resolve-reference ctx ext type value :queries ::errors/unknown-query))
|
||||
|
||||
(defmethod resolve :default [_ _ type value]
|
||||
{:errors [(errors/error ::errors/invalid-type (merge {:type type} (when value {:value value})))]})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns pluto.reader.views
|
||||
(:require [clojure.spec.alpha :as spec]
|
||||
[re-frame.core :as re-frame]
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.destructuring :as destructuring]
|
||||
[pluto.reader.errors :as errors]
|
||||
|
@ -43,7 +44,9 @@
|
|||
(fn? o) o
|
||||
(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]
|
||||
{:data value})
|
||||
|
@ -51,12 +54,23 @@
|
|||
(defmethod resolve-default-component-properties :default [_ value]
|
||||
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]
|
||||
(or (resolve-default-component-properties k v)
|
||||
(if-let [type (get-in ctx [:capacities :components component :properties k])]
|
||||
;; 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})]})))
|
||||
(resolve-custom-component-properties ctx ext component k v)))
|
||||
|
||||
(defn- resolve-property [ctx ext component k v]
|
||||
(if (component? component)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
(is (= {:data [blocks/let-block {:env '{a {:b 1} b 1}} 'b]}
|
||||
(blocks/parse {} {} '(let [{a :a} {:a {:b 1}} {b :b} a] b))))
|
||||
(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]]}
|
||||
(blocks/parse {} {} (list 'let ['s "Hello"] ['test {} 's]))))
|
||||
|
|
|
@ -17,12 +17,22 @@
|
|||
(is (= 'test (reference/reference->symbol ['test])))
|
||||
(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
|
||||
(is (= {:errors [{::errors/type ::errors/unknown-reference
|
||||
::errors/value {:value 'id}}]}
|
||||
::errors/value {:value 'id :type :view}}]}
|
||||
(reference/resolve {} {} :view ['id])))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-reference
|
||||
::errors/value {:value ""}}]}
|
||||
::errors/value {:value "" :type :view}}]}
|
||||
(reference/resolve {} {'views/id "view"} :view "")))
|
||||
(is (= {:errors [{::errors/type ::errors/unknown-reference-type
|
||||
::errors/value {:value :unknown}}]}
|
||||
|
|
|
@ -106,8 +106,12 @@
|
|||
(is (= {:errors [{::errors/type ::errors/unknown-event
|
||||
::errors/value 'event}
|
||||
{::errors/type ::errors/unknown-reference
|
||||
::errors/value {:value 'event}}]}
|
||||
::errors/value {:value 'event :type :event}}]}
|
||||
(types/resolve {} {} :event ['event])))
|
||||
(let [{:keys [data errors]} (types/resolve {:capacities {:events {'event {:value :event}}}} {} :event ['event])]
|
||||
(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
|
||||
(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,
|
||||
::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]))))
|
||||
|
||||
(deftest invalid-view-element-spec-errors
|
||||
|
|
Loading…
Reference in New Issue