ClojureScript - Ajax - Retrieving json - json

I have my dependencies
(ns test.core
(:require [reagent.core :as reagent :refer [atom]]
[ajax.core :refer [GET]]))
I then have my handler that handles the responses to my ajax call
(defn handle-response [response]
(println (type response))
(println film))
THe data from my call is JSON, in the browser it looks like this:
{"id":3,"name":"Sicario","star":"Emily Blunt","rating":5}
When I run the above Clojure, I see this:
cljs.core/PersistentArrayMap core.cljs:192:23
{id 3, name Sicario, star Emily Blunt, rating 5}
Is their a way for me to go from the PersistArrayMap, and destructure it into id, name, star and rating?
I tried
(let [film (js->clj (.parse js/JSON response) :keywordize-keys true)]
...)
Hoping I would get a map named film, but no avail!
I think maybe I could use (get-in), but can't get the syntax right for this either.
Thanks,

Got it,
I was missing :response-format :json and :keywords? true from my GET call
Now, it looks like this:
(GET (str "https://localhost:5001/api/films/" film-name)
{:response-format :json
:keywords? true
And it all works.

Related

Upload a json object to S3 using Clojure?

I am trying to figure out how I would upload a Json object to S3 using clojure through lambda. I am not doing any json type checking and I am just trying to write a handler such that as soon as receives a json object, it uploads that object to the bucket. Here is my code:
(ns clojurehandler.s3_clojure_handler
(:gen-class
:name "clojurehandler.S3ClojureHandler"
:implements [com.amazonaws.services.lambda.runtime.RequestStreamHandler])
(:require [clojure.data.json :as json]
[clojure.string :as s]
[clojure.java.io :as io]
[aws.sdk.s3 :as s3]))
(def cred {:access-key "..", :secret-key ".."})
(defn handle-event [event]
(s3/put-object cred
"bucket-name" "name" event
:content-type "application/json"))
(defn -handleRequest [this is os context]
(handle-event (io/reader is)))
I am using this library: https://github.com/weavejester/clj-aws-s3
which I know has been deprecated but it seems like it should do what I am aiming to do.
I have already set the role of my lambda function to enable putting objects in S3 buckets, but I get a task timed out after 3 seconds error when I try to test it. What am I doing wrong?
Thanks

Decode, in clojure, a JSON, clojure.data.json & cheshire.core, can't custom decode w/cheshire

My project parses JSONs, with a read/write library, called:
cheshire.core
I was having problems, trying to get the decode (func) to work, so I started messing around with:
data.json
My JSON contains data that consists of a field named "zone" this contains a vector with :keys inside, like so {:zone : [:hand :table]} that is stored into strings within the vector stored like so: {"zone" : ["hand" "table"]}
So I figured out how to convert the sample data using:
(mapv keyword {"zone" : ["hand"]})
which was great, I then needed to figure out how to implement a decoder for cheshire, I couldn't do this with my logic, I only spent like an hour working on this, but I had been using data.json, and the decoder function is relatively easy I think.
I got my project to work, here is some sample code:
(ns clojure-noob.core (:require
[cheshire.core :refer [decode]]
[clojure.data.json :as j-data]
) (:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[& args]
)
this is using cheshire:
(let [init (decode "{\"zone\" : [\"hand\"]}" true
(fn [field-name]
(if (= field-name "zone")
(mapv keyword [])
[])))]
(println (str init)))
this is using data.json:
(defn my-value-reader [key value]
(if (= key :zone)
(mapv keyword value)
value))
(let [init (j-data/read-str
"{\"zone\" : [\"hand\"]}"
:value-fn my-value-reader
:key-fn keyword)]
(println (str init)))
I want the bottom result of these two from the console:
{:zone ["hand"]}
{:zone [:hand]}
The problem is I would like to do this using cheshire 😎
p.s. I am reading the factory section of cheshire? maybe this easier?
I would agree with #TaylorWood. Don't mess with the decoder, just do a bite in a time. First, parse json. Second, transform the result.
(def data "{\"zone\" : [\"hand\"]}")
(-> data
(cheshire.core/decode true)
(update-in ["zone"] (partial mapv keyword)))
#=> {:zone [:hand]}
I recommend you use a tool like schema.tools to coerce the input. You can add a second pass that attempts to coerce JSON strings into richer clojure types.
Here's some sample code!
;; require all the dependencies. See links below for libraries you need to add
(require '[cheshire.core :as json])
(require '[schema.core :as s])
(require '[schema.coerce :as sc])
(require '[schema-tools.core :as st])
;; your data (as before)
(def data "{\"zone\" : [\"hand\"]}")
;; a schema that wants an array of keywords
(s/defschema MyData {:zone [s/Keyword]})
;; use `select-schema` along with a JSON coercion matcher
(-> data
(json/decode true)
(st/select-schema MyData sc/json-coercion-matcher))
;; output: {:zone [:hand]}
Using defschema to define the shape of data you want gives you a general solution for serializing into JSON while getting the full benefit of Clojure's value types. Instead of explicitly "doing" the work of transforming, your schema describes the expected outcome, and hopefully coercions can do the right thing!
Links to libraries:
- https://github.com/plumatic/schema
- https://github.com/metosin/schema-tools#coercion
Note: you can do a similar thing with clojure.spec using metosin/spec-tools. Check out their readme for some help.

ClojureScript AJAX POST to send a json data

I am trying to implement AJAX POST in ClojureScript.
The following is the code i am using
(defn handler [response]
(.log js/console (str response)))
(defn test-post [name email]
(let [data {:name name :email email}]
(POST "http://localhost:5000/Add/"
{
:format {"Accept" :json}
:body (.stringify js/JSON (clj->js data))
:handler handler
:error-handler (fn [r] (prn r))
:response-format {"Content-Type" "application/json;charset=utf-8"}
}
)))
When do i call the Post method. On form Submit? Also the post request is hitting the url but the json data is not present.
I assume you're using cljs-ajax for sending data.
What you really need in something like this:
(let [data {:name name :email email}]
(POST "/Add"
{:format :json
:params data
:handler handler
:error-handler (fn [r] (prn r))})))
You can just pass a plain Clojure object as params, just set :json as data format (default is :transit).
The second question is rather open and depends on your setup. I think the simplest to use is reagent, here is a nice example with sending form data.

How to get the selflink in a compojure handler?

When defining a compojure handler e.g. by using the defroutes macro, I can do something like this:
(defroutes home-routes
(GET "/myhome/:id" [ id ] (home-page)))
(defn home-page [ id ]
( ... do something ... ))
So I know how to pass a piece of the path parameter. But imagine I want to return a HAL+JSON object with a selflink. How would I get defroutes to pass the whole URI to the home-page function?
The Ring request map contains all the necessary information to construct a "selflink". Specifically, :scheme, :server-name, :server-port, and :uri values can be assembled into full request URL. When I faced this problem I created Ring middleware that adds the assembled request URL to the Ring request map. I could then use the request URL in my handlers as long as I pass the request map (or some subset of it) into the handler. The following snippet shows one way of implementing this:
(defroutes app-routes
(GET "/myhome/:id" [id :as {:keys [self-link]}] (home-page id self-link))
(route/resources "/")
(route/not-found "Not Found"))
(defn wrap-request-add-self-link [handler]
(fn add-self-link [{:keys [scheme server-name server-port uri] :as r}]
(let [link (str (name scheme) "://" server-name ":" server-port uri)]
(handler (assoc r :self-link link)))))
(def app
(-> app-routes
handler/site
wrap-request-add-self-link))

Parse luminus json response

I have a rather simple problem I thought. I have a luminus web project with clojurescript and cljs-ajax. What I do is an ajax request to my server and in my error-handler I want to parse a json string to display a specific error message. I just cannot get it working.
This is my code:
Clojurescript:
(defn error-handler [response]
(js/alert (aget response "message")))
; (js/alert (:message response ))) neither works here
(defn test-connection []
(POST "/url"
{:params (get-form-data)
:response-format :json
:handler succ-handler
:error-handler error-handler}))
And my serverside code:
(defn connect-jira []
{:body {:message "No connection to JIRA. Please check your details."}}
)
(defroutes jira-routes
(POST "url" []
(connect-jira)))
What is the idiomatic way to return a custom error message and parse that with clojurescript?
Update
I tracked my problem down some more. There is a difference between the success and the error handler of the POST request. Lets say my server side looks like this:
(defn connect-jira []
{:status 200 :body {:message "No connection to JIRA. Please check your details."}})
And my success handler like this:
(defn succ-handler [{:keys [message]}]
(.log js/console message))
Then its all fine, the correct messages is logged to the console.
But if my server side looks like this (different statu):
(defn connect-jira []
{:status 500 :body {:message "No connection to JIRA. Please check your details."}})
I call the error handler on cljs side
(defn err-handler [{:keys [message]}]
(.log js/console message))
But the message is "null"
I also tried to overwrite the status-text on server side, but did not succeed. So, how do I pass a message to the error handler from server side?
Are you using this library for your AJAX calls?
https://github.com/yogthos/cljs-ajax
If so, your error handler would need to look something more like this:
(defn err-handler [{:keys [response]}]
(.log js/console (:message response)))