Consuming WSDL in Clojure - clojure

I need to consume a WSDL web service and the Java client-side code I've seen so far looks bloated and complicated. I was wondering whether a cleaner solution might exist in Clojure so that I may perhaps implement that part in Clojure and expose a simpler API to the Java code.

cd your_project_dir/src
wsimport -p some.import.ns http://.../service?wsdl
It would create ./some.import.ns/*.class. So you can just use them in your clojure project
(ns your.ns ...
(:import [some.import.ns some_WS_Service ...]))
(let [port (-> (some_WS_Service.)
.getSome_WS_ServicePort]
(... (.someMethod port) ...))

Check out paos: https://github.com/xapix-io/paos
Lightweight and easy-to-use library to build SOAP clients from WSDL files.
(require '[clj-http.client :as client])
(require '[paos.service :as service])
(require '[paos.wsdl :as wsdl])
(defn parse-response [{:keys [status body] :as response} body-parser fail-parser]
(assoc response
:body
(case status
200 (body-parser body)
500 (fail-parser body))))
(let [soap-service (wsdl/parse "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl")
srv (get-in soap-service ["BLZServiceSOAP11Binding" :operations "getBank"])
soap-url (get-in soap-service ["BLZServiceSOAP11Binding" :url])
soap-headers (service/soap-headers srv)
content-type (service/content-type srv)
mapping (service/request-mapping srv)
context (assoc-in mapping ["Envelope" "Body" "getBank" "blz" :__value] "28350000")
body (service/wrap-body srv context)
resp-parser (partial service/parse-response srv)
fault-parser (partial service/parse-fault srv)]
(-> soap-url
(client/post {:content-type content-type
:body body
:headers (merge {} soap-headers)
:do-not-throw true})
(parse-response resp-parser fault-parser)))

Related

Endpoint unit testing with Lacinia-Pedestal in Clojure

I have been using Pedestal for RESTful API servers and its endpoint unit testing. This approach is to set the server up and test it on endpoint level. The so-called "endpoint unit testing" is well documented in below page.
http://pedestal.io/reference/unit-testing#_testing_your_service_with_response_for
This time, however, I am using Lacinia-Pedestal for GraphQL (not RESTful, in other words) and am wondering if I can apply the same endpoint testing logic. In Lacinia-Pedestal repository (https://github.com/walmartlabs/lacinia-pedestal), I could not find a relevant instruction. In fact it doesn't mention about unit testing at all.
If anyone has experience with this approach, can you please share?
Thanks!
-- edit
I am adding my testing code here.
resources/main-schema.edn:
{:queries
{:hello
{:type String}}}
core.clj
(ns lacinia-pedestal-lein-clj.core
(:require [clojure.edn :as edn]
[clojure.java.io :as io]
[com.walmartlabs.lacinia.pedestal2 :as p2]
[com.walmartlabs.lacinia.schema :as schema]
[com.walmartlabs.lacinia.util :as util]
[io.pedestal.http :as http]))
(defn resolve-hello
[_ _ _]
"hello, darren")
(defn hello-schema
[]
(-> (io/resource "main-schema.edn")
slurp
edn/read-string
(util/inject-resolvers {:queries/hello resolve-hello})
schema/compile))
(def service
(-> (hello-schema)
(p2/default-service nil)
http/create-server
http/start))
and Pedestal (not Lacinia-Pedestal) says that the server instance can be set up and tested by the following code snippet:
(ns lacinia-pedestal-lein-clj.core-test
(:require [lacinia-pedestal-lein-clj.core :as core]
[clojure.test :as t]
[io.pedestal.http :as http]
[io.pedestal.test :as ptest]
[com.walmartlabs.lacinia.pedestal2 :as p2]
[com.walmartlabs.lacinia.util :as util]))
(def service
(:io.pedestal.http/service-fn
(io.pedestal.http/create-servlet service-map)))
(is (= "Hello!" (:body (response-for service :get "/hello"))))
But, I believe this way works for RESTful but not for GraphQL because GraphQL needs to set schema (.edn file) and resolvers for a server.
So, I tried to tweak this.
(ns lacinia-pedestal-lein-clj.core-test
(:require [lacinia-pedestal-lein-clj.core :as core]
[clojure.test :as t]
[io.pedestal.http :as http]
[io.pedestal.test :as ptest]
[com.walmartlabs.lacinia.pedestal2 :as p2]
[com.walmartlabs.lacinia.util :as util]))
(defonce service
(-> (core/hello-schema)
(p2/default-service nil)
http/create-server
http/start))
(t/is (= "Hello!"
(:body
(util/response-for service
:get "/hello")))) ;; NOT WORKING
But it does not work this way because response-for expects interceptor-service-fn type.
So, as far as I know, the real question is how to use response-for function with GraphQL server instance.
in fact, i found a solution from Lacinia documentation.
https://lacinia.readthedocs.io/en/latest/tutorial/testing-1.html

How to apply ring-anti-forgery on specific reitit routes?

I keep getting "Invalid anti-forgery token" when wrapping specific routes created with metosin/reitit reitit.ring/ring-router. I've also tried reitit's middleware registry, but it didn't work too. Although I could just wrap the entire handler with wrap-session and wrap-anti-forgery, that defeats the reitit's advantage on allowing route-specific middleware.
(ns t.core
(:require [immutant.web :as web]
[reitit.ring :as ring]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.session :refer [wrap-session]]
[ring.util.anti-forgery :refer [anti-forgery-field]]
[ring.util.response :as res]))
(defn render-index [_req]
(res/response (str "<form action='/sign-in' method='post'>"
(anti-forgery-field)
"<button>Sign In</button></form>")))
(defn sign-in [{:keys [params session]}]
(println "params: " params
"session:" session)
(res/redirect "/index.html"))
(defn wrap-af [handler]
(-> handler
wrap-anti-forgery
wrap-session
wrap-keyword-params
wrap-params))
(def app
(ring/ring-handler
(ring/router [["/index.html" {:get render-index
:middleware [[wrap-content-type]
[wrap-af]]}]
["/sign-in" {:post sign-in
:middleware [wrap-af]}]])))
(defn -main [& args]
(web/run app {:host "localhost" :port 7777}))
It turns out that metosin/reitit creates one session store for each route (refer to issue 205 for more information); in other words, ring-anti-forgery is not working because reitit does not use the same session store for each route.
As of the time of this answer, the maintainer suggests the following (copied from the issue for ease of reference within Stack Overflow):
mount the wrap-session outside of the router so there is only one instance of the mw for the whole app. There is :middleware option in ring-handler for this:
(require '[reitit.ring :as ring])
(require '[ring.middleware.session :as session])
(defn handler [{session :session}]
(let [counter (inc (:counter session 0))]
{:status 200
:body {:counter counter}
:session {:counter counter}}))
(def app
(ring/ring-handler
(ring/router
["/api"
["/ping" handler]
["/pong" handler]])
(ring/create-default-handler)
;; the middleware on ring-handler runs before routing
{:middleware [session/wrap-session]}))
create a single session store and use it within the routing table (all instances of the session middleware will share the single store).
(require '[ring.middleware.session.memory :as memory])
;; single instance
(def store (memory/memory-store))
;; inside, with shared store
(def app
(ring/ring-handler
(ring/router
["/api"
{:middleware [[session/wrap-session {:store store}]]}
["/ping" handler]
["/pong" handler]])))
Not shown in this answer is the third option that the maintainer calls for PR.

Clojure, Compojure-api: Access Request headers

I am trying to implement request end point authentication. For that I want to access accessToken value from request headers.
My GET request end Point is
CURL Command
curl -X GET \
'http://localhost:3000/hello?id=10' \
-H 'accesskey: 23423sfsdfsdfsfg' \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-H 'postman-token: f69b34e6-4888-ec31-5fbc-b734e176571b' \
-d '{
"artwork": {id" : 1}
}'
HTTP Command
GET /hello?id=10 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
accessKey: 23423sfsdfsdfsfg
Cache-Control: no-cache
Postman-Token: b974719d-5e1d-4d68-e910-e9ca50562b2f
My Code for GET Method Implementation
(defapi app
(GET ["/hello/:id", :id #"[0-9]+" ] [id]
(log/info "Function begins from here")
(def artworkData (logic/artwork-id (->> id (re-find #"\d+") Long/parseLong)))
(def data (if (not-empty artworkData)
{:data artworkData :status 200}
{:data [] :status 201}))
(ok data)))
I want to fetch accessKey: 23423sfsdfsdfsfg from request header.
Is there any way to get the value and use in my GET Method?
I am using POSTMAN to test all API end points.
Compojure has custom destructuring syntax (i.e., different from Clojure proper) for the parameters. You can bind the whole request map using keyword :as
(defapi app
(GET ["/hello/:id", :id #"[0-9]+" ] [id :as request]
If you want only request headers, the following should work
(defapi app
(GET ["/hello/:id", :id #"[0-9]+" ] [id :as {:headers headers}]
Note that this still allows you to bind path parameter id.
The Compojure Sweet API functions like [compojure.api.sweet :refer [defroutes GET PUT context]] let us bind the whole request or bind select headers. In the snippet below [:as request] makes the whole request available to me.
(GET
"/download/:id"
[:as request]
:header-params [{x-http-request-id :- X-Http-Request-Id nil}]
:path-params [id :- (describe String "The encoded id of the image")]
:summary "Download the image bytes"
:description "This endpoint responds 307 - Temporary Redirect to a cacheable presigned S3 URL for the actual bytes."
(let [http-response (->> request
walk/keywordize-keys
util/extract-base-url
(transform/generate-resource-url (util/decode-key id))
status/temporary-redirect)
expire-time (-> 3 hours from-now coerce/to-date ring-time/format-date)]
(log/infof "x-http-request-id is %s" x-http-request-id)
(response/header http-response "Expires" expire-time)))
The vector beginning :header-params [{x-http-request-id :- X-Http-Request-Id nil}] makes the value of the "X-HTTP-REQUEST-ID" header in the request available to my function directly as x-http-request-id.
The squiglies thing {...} makes the presence of x-http-request-id header optional in the request.
The :- X-Http-Request-Id nil stuff gives it a Schema which is defined somewhere else like (s/defschema X-Http-Request-Id (rss/describe String "Request ID for tracing calls")).
Once you've got those kids bound to names you just work with the names. The compojure folks don't do a great job at documenting everything you can do there. Poke around their examples and you'll find stuff like this.
I have figured out solution to the issue. Please check solution here.
(ns clojure-dauble-business-api.core
(:require [compojure.api.sweet :refer :all]
[ring.util.http-response :refer :all]
[clojure-dauble-business-api.logic :as logic]
[clojure.tools.logging :as log]
[clojure-dauble-business-api.domain.artwork]
[cheshire.core :as json])
(:import [clojure_dauble_business_api.domain.artwork Artwork]))
(defapi app
(GET ["/hello/:id", :id #"[0-9]+"] [id :as request]
(log/info "Function begins from here" request)
(def jsonString (json/generate-string (get-in request [:headers])))
(log/info "Create - Access Key is " (get-in (json/parse-string jsonString true) [:accesskey]))
(def artworkData (logic/artwork-id (->> id (re-find #"\d+") Long/parseLong)))
(def data (if (not-empty artworkData)
{:data artworkData :status 200}
{:data [] :status 201})))
I don't think it is smart way.
Can you anybody look into my solution and tell me Is there another way to get accesskey?

How to a json-rpc call from Clojure?

I've got a local server running on port 8545 which listen to JSON-RPC requests. I can call it using curl like this:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xf54c19d9ef3873bfd1f7a622d02d86249a328f06", "latest"],"id":1}' http://localhost:8545
What would be the equivalent call from Clojure? Do I need to add some external libraries to the project.clj?
I think you should try http-kit.
Also you will need some library for json (data.json or cheshire)
So add to your project.clj following dependencies:
[http-kit "2.1.18"]
[org.clojure/data.json "0.2.6"]
And try this
(ns your-ns
(:require [org.httpkit.client :as http]
[clojure.data.json :as json]))
(let [url "http://localhost:8545"
body (json/write-str
{:jsonrpc "2.0"
:method "eth_getBalance"
:params ["0xf54c19d9ef3873bfd1f7a622d02d86249a328f06" "latest"]
:id 1})
options {:body body}
result #(http/post url options)]
(prn result))
I had a similar use case so I created a small Clojure library for making JSON-RPC calls. With this, you can do,
(ns example.core
(:require [json-rpc.core :as rpc]))
(with-open [channel (rpc/open "http://localhost:8545")]
(rpc/send! channel "eth_blockNumber" ["latest"]))
;; => {:result "0x14eca", :id "6fd9a7a8-c774-4b76-a61e-6802ae64e212"}
, and the boilerplate will be handled for you.

Is there any support for RPC in ClojureScript?

I have a ClojureScript application and I want to make RPC calls to the server which would look like normal function core.async calls on the client side.
In order to do this for the moment I wrote the code below based on cljx. In the RPC definitions section I would have to add all the server-side functions which I want to expose as RPC to the client side.
Note: the send function is taken from here: https://dimagog.github.io/blog/clojure/clojurescript/2013/07/12/making-http-requests-from-clojurescript-with-core.async/
Is there a way to do this nicer without the boilerplate code?
Thinking about how to improve it the only idea that I have is to write a leiningen plugin which generates server side and client side code needed for RPC i.e. the part that I do at this moment using cljx. Is there a better way?
(ns myapp.shared.rpc
(:require
#+cljs [myapp.tools :refer [send log]]
#+cljs [cljs.reader :as reader]
#+clj [clojure.tools.logging :as log]
#+clj [noir.response :refer [edn]]
#+clj [myapp.rpc :as rpc]
))
#+cljs (defn rpc-client [function params]
#+cljs (log "RPC call: (" function params ")")
#+cljs (send "POST" "/api"
#+cljs (str "rpc=" (pr-str {:fun function :params params}))
#+cljs (fn [x]
#+cljs (log "RPC response:'" x "'")
#+cljs (:response (reader/read-string x)))))
#+clj (defmulti rpc-impl #(:fun %))
#+clj (defn rpc-server [{rpc :rpc}]
#+clj (log/info "RPC call received:" rpc)
#+clj (let [response (-> rpc read-string rpc-impl)]
#+clj (log/info "RPC response sent: '" response "'")
#+clj (edn {:response response})))
;;;;; RPC definitions
#+cljs (defn demo [ & p] (rpc-client :demo p))
#+clj (defmethod rpc-impl :demo [{p :params}] (apply rpc/demo p))
Here are three libraries I've seen that handle RPC. I don't have significant experience with any of them, so take my comments with a large grain of salt.
Castra. The most recently updated, and has a nice readme.
Fetch. Looks simple, sweet, and probably sufficient.
Shoreleave. I used this successfully a while back. It worked fine, but has not been updated in a few years.
Clojure has edn data exchange format, you can use CQRS as data exchange method