Skip to content

Commit

Permalink
1 change the options of the export-commands from map to collection of…
Browse files Browse the repository at this point in the history
… collection

2 enable dymanmice expand option
  • Loading branch information
zhuangxm committed Dec 9, 2011
1 parent a9f0974 commit 4af882e
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 87 deletions.
115 changes: 55 additions & 60 deletions src/clj_rpc/context.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
(:require [ring.middleware.cookies :as cookies]
[clj-rpc.user-data :as data]))


;;the options availbe to context
(def context-options [:require-context :params-check :params-inject])

(defn wrap-client-ip
"let :remote-addr represents the real client ip even though
the client connects through a proxy server"
Expand Down Expand Up @@ -37,7 +33,10 @@

(defn add-context
"add context to specific command
options is a map include several keys
options is a collection include several keys
like [ [:requre-context true] [:params-checks ...] [... ...] ]
the meaning of option:
:require-context (true or false default false)
whether this command must have context
:params-checks
Expand All @@ -53,8 +52,7 @@
if client invoke (fun param1 param2) then the acutally invoke will be like
(fun client-ip server-name param1 param2)"
[command options]
{:pre [ (every? #(% (set context-options)) (keys options))]}
(merge command options))
(assoc command :options options))

(defn add-context-to-command-map
"add context options to all the command in the command-map
Expand All @@ -66,69 +64,66 @@
"return true if method-reqeust error
else false"
[method-request]
(boolean (or (:code method-request)
(:message method-request))))
(boolean (:error method-request)))

(defmulti pre-handle-method-request
"pre handle method request
return new method-request if successful
else return error like {:code error-code :message error-message}"
(fn [option-key option request method-request]
(if (not (error-method-request? method-request))
option-key
:default)))
(defmulti render-method-request
"adjust method request by option
return new method-request or nil (not any change)"
(fn [option-key option-value request method-request]
option-key))

;;pre handle :require-context option
;;return method-request if the command don't need authrization or
;;the context include the authorization information
;;else return {:code :unauthorized}
(defmethod pre-handle-method-request :require-context
[_ option request method-request]
(let [context (get request :context)]
(if (and option
(not (seq context)))
{:code :unauthorized}
method-request)))
;;render method-request :require-context option
(defmethod render-method-request :require-context
[_ option-value request method-request]
(when (and option-value
(not (seq (get request :context) )))
(assoc method-request :error {:code :unauthorized})))

;;pre handle :params-check
;;return method-request if the params of the command satisfy
;;the requirement of the command provided specific context
;;else return {:code invalid-params :message error-message}
(defmethod pre-handle-method-request :params-check
[_ option request method-request]
(let [context (get request :context)
params (:params method-request)
;;render method-request :params-check option
;;return method-request with params error or nil
(defmethod render-method-request :params-check
[_ option-value request method-request]
(let [{context :context} request
{params :params} method-request
fn-check
(fn [[k v]]
(if (not= (nth params k) (get-in context v))
{:code :invalid-params
:message (str "the " k "th param must be " (get-in context v))}))]
(or (->> option
(map fn-check )
(filter identity)
first)
method-request)))
:message (str "the " k "th param must be "
(get-in context v))}))]
(when-let [error (->> option-value
(map fn-check )
(filter identity)
first)]
(assoc method-request :error
error))))

;;pre handle :params-inject
;;render method-request :params-inject option
;;inject the params from request needed by the command
;;into the actualparams
;;return the new method-request
(defmethod pre-handle-method-request :params-inject
[_ option request method-request]
(if option
;;into the actual params
;;return the new method-request or nil
(defmethod render-method-request :params-inject
[_ option-value request method-request]
(when option-value
(update-in method-request [:params]
#(concat (map (partial get-in request) option) %))
method-request))
#(concat (map (partial get-in request) option-value) %))))

;;default return origin method-request
(defmethod pre-handle-method-request :default
[_ option request method-request]
method-request)
;;default throw RuntimeException
(defmethod render-method-request :default
[option-key option-value request method-request]
(throw (RuntimeException. (str "invalid command option , method-requet: "
method-request " option: "

This comment has been minimized.

Copy link
@robertluo

robertluo Dec 12, 2011

Collaborator

"Unknown command option: " option-key ", method-request: " method-request

option-key
" => " option-value) )) )

(defn check-context
"handle options of the command by order
and return new method-request or error message"
(defn adjust-method-request
"return new method-request (possible with error message)"
[cmd request method-request]
(reduce (fn [m-r k] (pre-handle-method-request k (get cmd k) request m-r))
method-request
context-options))
(loop [options (:options cmd)
m-r method-request]
(let [[option-key option-value] (first options)]
(if (or (nil? option-key) (error-method-request? m-r))
m-r
(recur (rest options)
(or (render-method-request option-key option-value request m-r)
m-r))))))

This comment has been minimized.

Copy link
@robertluo

robertluo Dec 12, 2011

Collaborator

when will render-method-request return nil and this recur with m-r? Suggest replace it will simple (render-method-request ...) call, and it is render-method-request's responsibility to return a simple m-r.

10 changes: 6 additions & 4 deletions src/clj_rpc/rpc.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@
"execute a function f with params
return rpc response
id : used for generate the response"
[f params id]
[f {:keys [params id error]}]
(try
(if f
(mk-response (apply f params) id)
(mk-error :method-not-found id))
(if error
(mk-error (:code error) id (:message error))
(if f
(mk-response (apply f params) id)
(mk-error :method-not-found id)))
(catch ArityException e (mk-error :invalid-params id (.getMessage e)) )
(catch Exception e (mk-error :internal-error id (.getMessage e)))))
15 changes: 6 additions & 9 deletions src/clj_rpc/server.clj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
(defn export-commands
"export all functions (fn-names is null or empty)
or specify functions in the namespace ns
invoker of this method must notice the order of the options
options: is a map include several keys
:require-context (true or false default false)
whether this command must have context
Expand All @@ -52,7 +52,7 @@
(export-commands \"clojure.core\" nil)
(export-commands 'clojure.core ['+])
(export-commands 'clojure.core [\"+\"]
{:require-context true :params-check {0 [:id]}})"
[ [:require-context true] [:params-check {0 [:id]}] ])"
[ns fn-names & [options]]
(let [ns (symbol ns)]
(require ns)
Expand All @@ -68,14 +68,11 @@
return the execute result"
[command-map request method-request]
(logging/debug "execute-command == " method-request)
(let [id (:id method-request)
cmd (command-map (:method method-request))
(let [cmd (command-map (:method method-request))
f (and cmd (command/.func cmd))
new-method-request (context/check-context cmd request method-request)]
(if (context/error-method-request? new-method-request)
(rpc/mk-error (:code new-method-request) id
(:message new-method-request))
(rpc/execute-method f (:params new-method-request) id))))
new-method-request (context/adjust-method-request
cmd request method-request)]
(rpc/execute-method f new-method-request)))

(defn help-commands
"return the command list"
Expand Down
26 changes: 14 additions & 12 deletions test/clj_rpc/test/context.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,39 @@
(deftest check-add-context-to-command-map
(let [command-map {"str" (command/mk-command "str" #'str)
"concat" (command/mk-command "concat" #'concat)}
options {:require-context true :params-check {0 1}}
options [ [:require-context true] [:params-check {0 1}]]
new-map (add-context-to-command-map command-map options)
vs (vals new-map)]
(is (= (get-in (first vs) [:params-check]) {0 1}))
(is (= (get-in (second vs) [:params-check]) {0 1}))))
(is (= (get (first vs) :options) options))
(is (= (get (second vs) :options) options))))

(facts
"check whether or not the command and params statifies context requirement"
(let [cmd (-> (command/mk-command "concat" #'concat)
(add-context {:require-context true
:params-check {0 [:p1] 1 [:p2]}}))
(add-context [ [:require-context true]
[:params-check {0 [:p1] 1 [:p2]}]]))
cmd-not-require (command/mk-command "str" #'str)
method-request {:method "method" :params [ [1 2] [3 4] [5 6]]}]
;;stastify authorized and params requirement
(check-context cmd {:context {:p1 [1 2] :p2 [3 4]}}
(adjust-method-request cmd {:context {:p1 [1 2] :p2 [3 4]}}
method-request) => method-request

;;do not statify authorization requirement
(check-context cmd nil method-request) => (contains {:code :unauthorized})
(check-context cmd {} method-request) => (contains {:code :unauthorized})
(adjust-method-request cmd nil method-request) => (contains {:error {:code :unauthorized}})
(adjust-method-request cmd {} method-request) => (contains {:error {:code :unauthorized}})

;;do not statify params check requirement
(check-context cmd {:context {:p1 [1 2] }} method-request) => (contains {:code :invalid-params})
(get-in (adjust-method-request cmd {:context {:p1 [1 2] }} method-request)
[:error :code]) => :invalid-params

;;do not require context check
(check-context cmd-not-require nil {:method "method" :params ["str1" "str2"]})
(adjust-method-request cmd-not-require nil {:method "method" :params ["str1" "str2"]})
=> {:method "method" :params ["str1" "str2"]}))

;.;. The work itself praises the master. -- CPE Bach
(facts "test inject parasm"
(let [cmd (-> (command/mk-command "str" #'str)
(add-context {:params-inject [ [:remote-addr] [:inject]]})) ]
(check-context cmd {:remote-addr "192.168.1.1" :inject "inject-param2"}
{:method "method" :params ["p1" "p2"]})
(adjust-method-request cmd {:remote-addr "192.168.1.1" :inject "inject-param2"}
{:method "method" :params ["p1" "p2"]})
=> {:method "method" :params ["192.168.1.1" "inject-param2" "p1" "p2"]}))
5 changes: 3 additions & 2 deletions test/clj_rpc/test/integrate.clj
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
(server/export-commands 'clojure.core nil)
(server/export-commands "clj-rpc.command" ['mk-command "get-commands"])
(server/export-commands "clj-rpc.test.integrate" ['fn-with-context-check]
{:require-context true :params-check {0 [:username]} })
[ [:require-context true]
[:params-check {0 [:username]}]] )
(server/export-commands 'clojure.core ['str]
{:params-inject [ [:remote-addr]]})
[ [:params-inject [ [:remote-addr]]]])
(server/export-commands 'clj-rpc.test.integrate
['fn-save-data 'fn-get-data 'fn-delete-data]))

Expand Down

1 comment on commit 4af882e

@robertluo
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good!

Please sign in to comment.