I'm using lacinia+pedestal to set up a graphql service, most of the queries I've seen in the tutorial need an arg (iD) e.g. games_by_id, but I'd like to retrieve all objects without an arg:
(defn resolve-all-drivers
[drivers-map context args value]
drivers-map)
schema-data.edn
:all_drivers
{:type (list Driver)
:description "Get all the drivers"
:resolve :query/all-drivers}
}
schema:
:Driver {:description "A collection of drivers"
:fields {:id {:type (non-null ID)}
:name {:type (non-null String)}
:email {:type (non-null String)}}}
In GraphiQL:
{
all_drivers {
name
}
}
Any idea how I can change this to give me a whole list without args?
Updated the resolver map:
(defn resolver-map
[component]
(let [trips-data (-> (io/resource "trips-data.edn")
slurp
edn/read-string)
trips-map (entity-map trips-data :trips)
cars-map (entity-map trips-data :cars)
drivers-map (get trips-data :drivers)]
{:query/trip-by-id (partial resolve-trip-by-id trips-map)
:query/drivers-by-id (partial resolve-drivers-by-id drivers-map)
:query/all-drivers (partial resolve-all-drivers drivers-map)
:Trip/cars (partial resolve-trip-cars cars-map)
:Car/trips (partial resolve-car-trips trips-map)}))
Related
I'm a newbie to clojure and i'm trying to convert a messages that come in a particular format into another.
ie, i have to convert something like:
{
:image-url ["https://image.png"],
:topic "Some title",
:id "88ebaf91-a01d-4683-9aa7-629bb3ecea01",
:short-description "Some Description",
:mobile-deeplink "https://deeplink.com/link",
:partner-name "partner"}
Into something like
{
:title "Some title",
:id "88ebaf91-a01d-4683-9aa7-629bb3ecea01",
:content {
:url ["https://image.png"],
:description "Some Description",
:deeplink "https://deeplink.com/link",
:partner "partner"}}
So in effect, there is a combination of renaming keys and nesting the flat map
What I have done so far was something on the lines of:
(let [message-map {
:image-url :purl
:topic :title
:partner-name :partner
:short-description :description
:mobile-deeplink :deeplink}]
(defn- map-to-body
[message]
(-> message
(clojure.set/rename-keys message-map)
;;some sort of (assoc-in) <- this is where i need help in
)))
Combining assoc-in, a path conversion table, and reduce could be more self-describing and maintainable. You could choose to reduce over either the conversion table or the input message, whichever makes more sense for the data you have.
(defn transform [m]
(let [pp '([:image-url [:content :url]]
[:topic [:title]]
[:id [:id]]
[:short-description [:content :description]]
;; etc.
)]
(reduce
(fn [o [mk ok]]
(assoc-in o ok (get m mk)))
{}
pp)))
You could chain-assoc-in here, but I think you are easier off
using select-keys. select-keys lets you extract only the keys
from a map into a new map, you need. So you can select :id/:title for
the outer map and the rest to assoc to :content.
E.g.
(require 'clojure.set)
(defn transform
[message]
(let [message-map {:image-url :url
:topic :title
:partner-name :partner
:short-description :description
:mobile-deeplink :deeplink}
renamed (clojure.set/rename-keys message message-map)]
(assoc ; XXX
(select-keys renamed [:title :id])
:content (select-keys renamed [:url :description :deeplink :partner]))))
(def src {:image-url ["https://image.png"],
:topic "Some title",
:id "88ebaf91-a01d-4683-9aa7-629bb3ecea01",
:short-description "Some Description",
:mobile-deeplink "https://deeplink.com/link",
:partner-name "partner"})
(def tgt {:title "Some title",
:id "88ebaf91-a01d-4683-9aa7-629bb3ecea01",
:content {
:url ["https://image.png"],
:description "Some Description",
:deeplink "https://deeplink.com/link",
:partner "partner"}})
(assert (= (transform src) tgt))
I'm building my very first web app, and I am having a hard time accessing individual fields of a form when the user submits the form. Here's what I have:
(defroutes app
(GET "/" [] homepage)
(POST "/city" request display-city)
(route/resources "/")
(route/not-found "Not Found"))
(defn display-city [request]
(html5
[:div {:class "the-city"}
[:h2 "ALL ABOUT YOUR CITY"]
[:ul
[:li "Your city is " (str request) "! That's all"]]]))
;; and here's the hiccup form:
[:form {:action "/city" :method "post"}
(anti-forgery-field)
[:p "Enter your home address"]
[:div
[:label {:for "street-field"} "Street:"]
[:input {:id "street-field"
:type "text"
:name "street"}]]
[:div
[:label {:for "city-field"} "City:"]
[:input {:id "city-field"
:type "text"
:name "city"}]
[:div
[:label {:for "state-field"} "State:"]
[:input {:id "state-field"
:type "text"
:name "state"}]
[:label {:for "zip-field"} "ZIP:"]
[:input {:id "zip-field"
:type "text"
:name "zip"
:size "10"}]]
[:div.button
[:button {:type "submit"} "Submit"]]]])
;; When I run the code above, I can see the entire form that's submitted via (str request), in what looks to be a Clojure map. But I can't figure out how to extract individual "key/vals" (from that address form, I'd like to extract the city), or how to store those results in a way that I can use it. Any ideas?
This is a super basic /city page that I am trying to get running to understand how things work before building bigger things. Thanks!
In your request map, there should be a key :form-params with a map of key/value pairs that were POSTed. Here's how you could get an individual value out:
(get-in request [:form-params :city])
Or you could destructure :form-params map to bind many values at once:
(let [{:keys [city state zip]} (:form-params request)]
(format "%s, %s %s" city state zip))
I have many JSON objects, and I am trying to filter those objects by the date. These objects are being parsed from several JSON files using Cheshire.core, meaning that the JSON objects are in a collection. The date is being passed in in the following format "YYYY-MM-DD" (eg. 2015-01-10). I have tried using the filter and contains? functions to do this, but I am having no luck so far. How can I filter these JSON objects by my chosen date?
Current Clojure code:
(def filter-by-date?
(fn [orders-data date-chosen]
(contains? (get (get orders-data :date) :date) date-chosen)))
(prn (filter (filter-by-date? orders-data "2017-12-25")))
Example JSON object:
{
"id":"05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
"date":{
"date":"2015-01-10T19:11:41.000Z"
},
"total":{
"GBP":57.45
}
}
JSON after parsing with Cheshire:
[({:id "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-01-10T19:11:41.000Z"},
:total {:GBP 57.45}}) ({:id "325bd04-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-02-23T10:15:14.000Z"},
:total {:GBP 32.90}})]
First, I'm going to assume you've parsed the JSON first into something like this:
(def parsed-JSON {:id "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-01-10T19:11:41.000Z"},
:total {:GBP 57.45}})
The main problem is the fact that the date as stored in the JSON contains time information, so you aren't going to be able to check it directly using equality.
You can get around this by using clojure.string/starts-with? to check for prefixes. I'm using s/ here as an alias for clojure.string:
(defn filter-by-date [date jsons]
(filter #(s/starts-with? (get-in % [:date :date]) date)
jsons))
You were close, but I made a few changes:
You can't use contains? like that. From the docs of contains?: Returns true if key is present in the given collection, otherwise returns false. It can't be used to check for substrings; it's used to test for the presence of a key in a collection.
Use -in postfix versions to access nested structures instead of using multiple calls. I'm using (get-in ...) here instead of (get (get ...)).
You're using (def ... (fn [])) which makes things more complicated than they need to be. This is essentially what defn does, although defn also adds some more stuff as well.
To address the new information, you can just flatten the nested sequences containing the JSONs first:
(->> nested-json-colls ; The data at the bottom of the question
(flatten)
(filter-by-date "2015-01-10"))
#!/usr/bin/env boot
(defn deps [new-deps]
(merge-env! :dependencies new-deps))
(deps '[[org.clojure/clojure "1.9.0"]
[cheshire "5.8.0"]])
(require '[cheshire.core :as json]
'[clojure.string :as str])
(def orders-data-str
"[{
\"id\":\"987654\",
\"date\":{
\"date\":\"2015-01-10T19:11:41.000Z\"
},
\"total\":{
\"GBP\":57.45
}
},
{
\"id\":\"123456\",
\"date\":{
\"date\":\"2016-01-10T19:11:41.000Z\"
},
\"total\":{
\"GBP\":23.15
}
}]")
(def orders (json/parse-string orders-data-str true))
(def ret (filter #(clojure.string/includes? (get-in % [:date :date]) "2015-01-") orders))
(println ret) ; ({:id 987654, :date {:date 2015-01-10T19:11:41.000Z}, :total {:GBP 57.45}})
You can convert the date string to Date object using any DateTime library like joda-time and then do a proper filter if required.
clj-time has functions for parsing strings and comparing date-time objects. So you could do something like:
(ns filter-by-time-example
(:require [clj-time.coerce :as tc]
[clj-time.core :as t]))
(def objs [{"id" nil
"date" {"date" "2015-01-12T19:11:41.000Z"}
"total" nil}
{"id" "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f"
"date" {"date" "2015-01-10T19:11:41.000Z"}
"total" {"GBP" :57.45}}
{"id" nil
"date" {"date" "2015-01-11T19:11:41.000Z"}
"total" nil}])
(defn filter-by-day
[objs y m d]
(let [start (t/date-time y m d)
end (t/plus start (t/days 1))]
(filter #(->> (get-in % ["date" "date"])
tc/from-string
(t/within? start end)) objs)))
(clojure.pprint/pprint (filter-by-day objs 2015 1 10)) ;; Returns second obj
If you're going to repeatedly do this (e.g. for multiple days) you could parse all dates in your collection into date-time objects with
(map #(update-in % ["date" "date"] tc/from-string) objs)
and then just work with that collection to avoid repeating the parsing step.
(ns filter-by-time-example
(:require [clj-time.format :as f]
[clj-time.core :as t]
[cheshire.core :as cheshire]))
(->> json-coll
(map (fn [json] (cheshire/parse-string json true)))
(map (fn [record] (assoc record :dt-date (f/format (get-in record [:date :date])))))
(filter (fn [record] (t/after? (tf/format "2017-12-25") (:dt-date record))))
(map (fn [record] (dissoc record :dt-date))))
Maybe something like this? You might need to change the filter for your usecase but as :dt-time is now a jodo.DateTime you can leverage all the clj-time predicates.
I am having some trouble with POST from ajax.
I want to add a user to my database, so I am using POST and the data I want to send is in the form {:id id :pass pass} This is my POST
(defn add-user! [user]
(POST "/add-user!"
{:params user}))
All I want to do is enter information in the form specified above into this POST so I can send it to the database. I know that the argument,to the POST, is in the right form and the queries to the database and my routes are correct but I've made a mistake with the POST and I cannot figure out my mistake.
I am calling add-user! by
(defonce fields (atom {}))
(defn add-user! [user]
(POST "/add-user!"
{:params user}))
(defn content
[]
[:div
[:div
[:p "Enter Name:"
[:input
{:type :text
:name :name
:on-change #(swap! fields assoc :id (-> % .-target .-value))
:value (:id #fields)}]]
[:p "Enter Pass:"
[:input
{:type :text
:name :pass
:on-change #(swap! fields assoc :pass (-> % .-target .-value))
:value (:pass #fields)}]]
[:input
{:type :submit
:on-click #(do
(add-user! #fields))
:value "Enter"}]]
[:div
[:p "Id is " (:id #fields)]
[:p "Pass is " (:pass #fields)]]])
My query to the database in a clj file is
(defn add-user! [user]
(sql/insert! db :users user))
where sql is [clojure.java.jdbc :as sql]
There is not really enough information here to help you debug this fully, but I suspect that you need to modify your POST to:
(defn add-user! [user]
(POST "/add-user!"
{:format :json
:params user}))
If you don't provide :format, cljs-ajax defaults to sending Transit data, which would definitely confuse a server expecting JSON.
:format - specifies the format for the body of the request (Transit, JSON, etc.). Also sets the appropriate Content-Type header. Defaults to :transit if not provided. - JulianBirch/cljs-ajax#getpostput
Happened to me with this code:
(POST "/admin/tests/load"
{:params {:test-id "83"}
:headers {"x-csrf-token" csrf-field}
:handler (fn [r] (do (.log js/console r) (swap! test-state r)))
:format :json
:response-format :json
:error-handler (fn [r] (prn r))})))
"params" always showed up empty "{}". Then I tried:
(POST "/admin/tests/load"
{:params {:test-id "83"}
:headers {"x-csrf-token" csrf-field}} )
and all started working well, even after adding the other options. I know, weird.
I have a reagent atom which consists a vector of key/value maps.
How can I remove a key/value pair with certain key value which happens to be UUID? I have tried:
(swap! state/items (dissoc #state/items id))
but I get:
core.cljs:270 Uncaught Error: No protocol method IMap.-dissoc defined for type cljs.core/LazySeq: ({:id #uuid "e1f9341f-bc02-4c17-a594-b9b5ede72214", :description "foo bar"})
I think you need to use remove. So this should help:
(def data [{:id "e1f9341f-bc02-4c17-a594-b9b5ede72214" :description "Foo"} {:id "e1f9341f-bc02-4c17-a594-b9b5ede72214" :description "Bar"}] )
(remove #(= (:id %) "e1f9341f-bc02-4c17-a594-b9b5ede72214") data)
;; => ()
The swap! function takes the old value and returns the updated value. So data from above will be the old value. Your function you pass to swap! will thus look like this:
(fn [old]
(remove #(= (:id %) "e1f9341f-bc02-4c17-a594-b9b5ede72214") old))