clojure java interop and interfaces - clojure

I am having trouble using a Java interface from Clojure.
I have the folowing class: public class OpenAccess
which has a method: static Connection connect(String url)
where Connection is a interface: public interface Connection
In Java I would do this to setup a connection:
Connection conn = OpenAccess.connect(url);
I tried the following from Clojure but it doesn't work:
(defn connection [url]
(let [oa (access.OpenAccess.)
connection (reify access.Connection
.....
(.connect oa connection)))
it errors with "IllegalArgumentException No matching method found: connect for class access.OpenAccess"
I can't figure out how to properly execute Java interfaces from Clojure.

Looks like a static call:
(defn connection [url]
(OpenAccess/connect url))
And you would use it like this if you needed to type-hint it:
(let [^Connection conn (connection "http://foo")]
// use your conn
)
You don't need the ^Connection but it will tell the compiler the type of the method invocations on conn which will avoid reflection.

Related

Compojure Ring Middleware How To Use Value Outside of Middleware

I have a simple route and middleware setup with compojure/swagger that is utilizing a ring middleware.
(POST "/worlds" [request]
:body-params [name :- String]
:header-params [token :- String]
:middleware [wrap-api-auth]
:summary "Creates a new world with 'name'"
(ok (do-something-with-user)
(defn wrap-api-auth [handler]
(fn [request]
(let
[token (get (:headers request) "token")
user (db/get-user-by-token token)]
(if user
(handler request) ; pass to wrapped handler
(unauthorized {:error "unauthorized"})))))
This defines a simple route with some basic auth. The token passed in the header param is used to make a database query, which either returns a user and continues, or returns false and fails.
What I'm trying to accomplish is to pass the returned user back out so that I can use it later. I haven't had any luck, as I don't really know where I would try to add it to that I could access it later. I've tried to assoc it with the request but it doesn't appear that I can access it later. The ideal situation is I'm able to pass it to the do-something-with-user function.
Using assoc to add some data to the request should totally work.
You can find an example with some code that is very close to what I have in production at https://gist.github.com/ska2342/4567b02531ff611db6a1208ebd4316e6#file-gh-validation-clj-L124
In essence, that middleware calls
(handler (assoc request
:validation {:valid true
:validation valid?}))
So for your case, the following should just work:
(handler (assoc request
:user user))
If I understood correctly, the destructuring syntax you use is from compojure-api. According to the example at https://github.com/metosin/compojure-api/wiki/Middleware I'd say that the middleware set via the :middleware key behaves just as expected and you should be able to extract the :user from the request that ultimately ends up in your route.
So, just pass the request on to the do-something-with-user function:
(POST "/worlds" request
:body-params [name :- String]
:header-params [token :- String]
:middleware [wrap-api-auth]
:summary "Creates a new world with 'name'"
(ok (do-something-with-user request))
It should contain the :user you assoced into it in your middleware function. Note the missing brackets around request like mentioned in the comments to this answer.

How to get the URL query params in Pedestal?

How do I get the URL params into the request map in Pedestal? I am assuming that this needs the use of an interceptor? However the Pedestal documentation (or severe lack thereof) does not make this at all clear. Thanks.
Query parameters are parsed automatically by Pedestal, and the resulting map is placed in the request map under the :query-params key.
As a simple example, start with the pedestal-service template and use the following definitions:
(defn home-page
[request]
(ring-resp/response (format "Hello with params: %s" (:query-params request))))
(defroutes routes
[[["/" {:get home-page}]]])
Now if you browse to http://localhost:8080/?param=true&other=1234, you should see Hello world with paramters: {:param "true", :other "1234"}.

How to extend this code example to also include basic auth information?

I am very new to Clojure and was playing around with the following example from HTTP-Kit to do async get requests and then process all the results together when they are ready. In my case I also have to provide basic auth information, therefore the example given here (http://www.http-kit.org/client.html#combined) is not working and I havent found a nice solution how to pass in the additional basic auth information.
(let [urls ["http://server.com/api/1" "http://server.com/api/2"
"http://server.com/api/3"]
;; send the request concurrently (asynchronously)
futures (doall (map http/get urls))]
(doseq [resp futures]
;; wait for server response synchronously
(println (-> #resp :opt :url) " status: " (:status #resp))
)
A single call with basic auth looks like this:
(def options {:basic-auth ["myuser" "mypwd"]})
(http/get url options)
It was easy to do sync calls one after another. With the async variant I do not know how to pass in the options in the right way. The code example uses map to call http/get with all the elements of urls. I need a way to pass in two arguments which are then applied to the http/get call. But as I just started learning Clojure I do not find the right way... I looked at apply / map / anonymous function...
You need to pass your auth options to each http/get invocation in your map expression. For example:
(map (fn [url] (http/get url options))
urls)
Or shorter version:
(map #(http/get % options) url)
Full example:
(let [urls ["http://server.com/api/1" "http://server.com/api/2" "http://server.com/api/3"]
options {:basic-auth ["myuser" "mypwd"]}
;; send the request concurrently (asynchronously)
futures (doall (map #(http/get % options) urls))]
(doseq [resp futures]
;; wait for server response synchronously
(println (-> #resp :opt :url) " status: " (:status #resp)))

Clojure - pass component to functions not executed during start or stop

In the README of Stuart Sierra's component, there is a function add-user that is given as an example, but not seen anywhere else :
(defn add-user [database username favorite-color]
(execute-insert (:connection database)
"INSERT INTO users (username, favorite_color)"
username favorite-color))
I imagine it could be executed (for instance) on a web server route. I have no trouble imagining that username and favorite-colour would be parameters to that route, and thus readily available when calling add-user.
I guess that having this would make the database component of the web-server (for example) component.
However I am having some trouble figuring where the database component instance parameter of add-user should come from exactly.
I feel that directly accessing the system (ie. doing (:database my-system-ns/system))) to retrieve it would defeat part of the purpose of using components in the first place.
For instance if I am using pedestal, I may have my pedestal component (who has access to the database component) set up this key :
::bootstrap/routes #(deref #'my-routes-ns/routes)
And this would be something like that :
;; in my-routes-ns
(defroutes routes [[[ "/add-user" {:post add-user-handler} ]]])
;; same function again for clarity
(defn add-user [database username favorite-color]
(execute-insert (:connection database)
"INSERT INTO users (username, favorite_color)"
username favorite-color))
;; my route handler
(defn add-user-handler [request]
;; How do I get access to the database component from here ?
(add-user database "user" "red"))
How do I get access to my database component in this example ?
In a typical application, you might have a web-server component that depends (see component/using) on your database component, and a collection of public functions associated with the database component that its consumers could call to query the database.
The web-server component will then be responsible for setting up your request handler and starting a listener (like Jetty). This will involve taking the database component and injecting it into your handler, perhaps by partial application (if your handler looks like (defn handler [database request] …), say), so that it can call add-user on the actual database component.
Note that depending on the design of your app, your setup may not match the above exactly – for example,web-server could only use the database component through one or more layers of intermediate components.
FYI, the README of component suggest create a closure over one or more component,
(defn app-routes
"Returns the web handler function as a closure over the
application component."
[app-component]
;; Instead of static 'defroutes':
(web-framework/routes
(GET "/" request (home-page app-component request))
(POST "/foo" request (foo-page app-component request))
(not-found "Not Found")))
(defrecord WebServer [http-server app-component]
component/Lifecycle
(start [this]
(assoc this :http-server
(web-framework/start-http-server
(app-routes app-component))))
(stop [this]
(stop-http-server http-server)
this))
(defn web-server
"Returns a new instance of the web server component which
creates its handler dynamically."
[]
(component/using (map->WebServer {})
[:app-component]))

How to convert korma select results to json for a rest service (compojure)?

I am using compojure, cheshire and korma (and postgre db) for creating a rest service.
I've created a table with two string fields (name and description) with such structure:
(defentity posts
(pk :id)
(table :posts)
(entity-fields :name :description))
I can insert records into this table but when I try to exec
(defn get-all-posts []
(select posts))
and return the results from the server
defroutes app-routes
(GET "/" [] (get-start))
(context "/posts" []
(GET "/" [] (get-all-posts))
...
I receive such an error:
java.lang.IllegalArgumentException
No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: clojure.lang.PersistentVector
As I see I need to convert posts collection to json. How to do it?
Ring responses can be either a map or a string. If they are a map then they use a few keys such as :status and :body to define the response and set cookies etc. You may want to explicitly convert your response from a Clojure sequence (edn) to JSON by wrapping the call to (get-all-posts) in generate-string (since you are using Cheshire) :
{:status 200
:content-type "application/json; charset=UTF-8"
:body (cheshire/generate-string (get-all-posts))}
And while you are at it it can't hurt to specify the content type and response code.