Syntax error macroexpanding clojure.core/let - clojure

I am using shadowcljs and I am trying to create a mutation on the server using code based of the example code. I have a really small mutation that always throws this error. Why is that?
[jchat.server-components.pathom-wrappers :refer [defmutation defresolver]]
(defmutation reset-users-db
"Removes all users"
[]
{::pc/output [:message]}
{:message "ok"})
Syntax error macroexpanding clojure.core/let at (user.clj:47:1). nil -
failed: simple-symbol? at: [:bindings :form :local-symbol] spec:
:clojure.core.specs.alpha/local-name nil - failed: vector? at:
[:bindings :form :seq-destructure] spec:
:clojure.core.specs.alpha/seq-binding-form nil - failed: map? at:
[:bindings :form :map-destructure] spec:
:clojure.core.specs.alpha/map-bindings nil - failed: map? at:
[:bindings :form :map-destructure] spec:
:clojure.core.specs.alpha/map-special-binding
Row 47 is the defmutation starting row.
With macroexpand:
(macroexpand '(defmutation reset-users-db
"Removes all users"
[]
{::pc/output [:message]}
{:message "ok"}))
=>
(do
(com.wsscode.pathom.connect/defmutation
reset-users-db
[env__26870__auto__ params__26871__auto__]
#:com.wsscode.pathom.connect{:output [:message]}
(clojure.core/let [nil env__26870__auto__ nil params__26871__auto__] {:message "ok"}))
(jchat.server-components.pathom-wrappers/register! reset-users-db))

Thanks to Carcigenicate helping me debug and understand the debuigging we found that the mutation required 2 in params.
Such as:
(defmutation reset-users-db
"Removes all users"
[env params]
{::pc/output [:message]}
(println "test"))

Related

Tests breaks after I imported a single library

I'm using clojure new CLI to create a project and to run the tests I do:
clojure -M:test:runner
this is my test
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :as auth]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 0))))
;; this is working good and responding:
;; Ran 1 tests containing 1 assertions.
This project will read from stdin. My first step is to get data from a file and then write it to the stdout for testing. Because of this I want import BufferedReader to read the file:
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :as auth])
;; I just added this line
(:import (java.io BufferedReader))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 0))))
;; This is not working and it's returning
;; Ran 0 tests containing 0 assertions.
So now is no longer doing any tests.
The most likely reason is that you have some sort of syntax error in your code.
Your code should be:
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :as auth])
;; I just added this line
(:import (java.io BufferedReader)))
Note: three closing ) there, not two like you have in your code.
Here's what I just did:
(! 734)-> clojure -X:new :name auth.authorize
Generating a project called auth.authorize based on the 'lib' template.
The lib template is intended for library projects, not applications.
(! 735)-> cd auth.authorize/
(! 736)-> vi test/auth/authorize_test.clj
# to change the test to (= 0 0)
(! 737)-> cat test/auth/authorize_test.clj
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :refer :all]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 0))))
(! 738)-> clojure -M:test:runner
Running tests in #{"test"}
Testing auth.authorize-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
(! 739)-> vi test/auth/authorize_test.clj
# add in the :import
(! 740)-> cat test/auth/authorize_test.clj
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :refer :all])
(:import (java.io BufferedReader)))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 0))))
(! 741)-> clojure -M:test:runner
Running tests in #{"test"}
Testing auth.authorize-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
And you can see that it runs the test correctly.
Adding to Sean's answer, this is what the compiler sees:
(ns auth.authorize-test
(:require [clojure.test :refer :all]
[auth.authorize :as auth])
(:import (java.io BufferedReader)) ; should terminate ns form here with another `)` char
; Due to missing `)` above, this whole `deftest` form is inside the 'ns' form
; and is ignored w/o an error msg (you could consider this a compiler defect)
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 0))))
) ; end of ns form - should be after `import` keyword
So you see, the missing ) on the :import line is the cause of the problem. It causes the deftest form to be inside of the ns form, which makes no sense and is a syntax error. I'm surprised the compiler doesn't have an error for this, which I would consider a compiler bug/defect/problem, since it just silently fails instead of printing an error message of some sort.
Update
Perhaps you are using an older release of Clojure? I just tried it with this config:
--------------------------------------
Clojure 1.10.2-alpha1 Java 15
--------------------------------------
and I get an error:
Syntax error macroexpanding clojure.core/ns at (tst/demo/core.clj:1:1).
deftest - failed: #{:refer-clojure} at: [:ns-clauses :refer-clojure :clause] spec: :clojure.core.specs.alpha/ns-refer-clojure
deftest - failed: #{:require} at: [:ns-clauses :require :clause] spec: :clojure.core.specs.alpha/ns-require
deftest - failed: #{:import} at: [:ns-clauses :import :clause] spec: :clojure.core.specs.alpha/ns-import
deftest - failed: #{:use} at: [:ns-clauses :use :clause] spec: :clojure.core.specs.alpha/ns-use
deftest - failed: #{:refer} at: [:ns-clauses :refer :clause] spec: :clojure.core.specs.alpha/ns-refer
deftest - failed: #{:load} at: [:ns-clauses :load :clause] spec: :clojure.core.specs.alpha/ns-load
deftest - failed: #{:gen-class} at: [:ns-clauses :gen-class :clause] spec: :clojure.core.specs.alpha/ns-gen-class
Full report at:
/tmp/clojure-8770599721946785921.edn
Tests failed.
and the /tmp/clojure.....edn file looks like:
{:clojure.main/message
"Syntax error macroexpanding clojure.core/ns at (tst/demo/core.clj:1:1).\ndeftest - failed: #{:refer-clojure} at: [:ns-clauses :refer-clojure :clause] spec: :clojure.core.specs.alpha/ns-refer-clojure\ndeftest - failed: #{:require} at: [:ns-clauses :require :clause] spec: :clojure.core.specs.alpha/ns-require\ndeftest - failed: #{:import} at: [:ns-clauses :import :clause] spec: :clojure.core.specs.alpha/ns-import\ndeftest - failed: #{:use} at: [:ns-clauses :use :clause] spec: :clojure.core.specs.alpha/ns-use\ndeftest - failed: #{:refer} at: [:ns-clauses :refer :clause] spec: :clojure.core.specs.alpha/ns-refer\ndeftest - failed: #{:load} at: [:ns-clauses :load :clause] spec: :clojure.core.specs.alpha/ns-load\ndeftest - failed: #{:gen-class} at: [:ns-clauses :gen-class :clause] spec: :clojure.core.specs.alpha/ns-gen-class\n",
:clojure.main/triage
{:clojure.error/cause
"Call to clojure.core/ns did not conform to spec.",
:clojure.error/phase :macro-syntax-check,
:clojure.error/symbol clojure.core/ns,
:clojure.error/column 1,
:clojure.error/line 1,
:clojure.error/class clojure.lang.ExceptionInfo,
:clojure.error/source "core.clj",
:clojure.error/spec
{:clojure.spec.alpha/problems
({:path [:ns-clauses :refer-clojure :clause],
:pred #{:refer-clojure},
:val deftest,
:via
[:clojure.core.specs.alpha/ns-form
:clojure.core.specs.alpha/ns-refer-clojure
:clojure.core.specs.alpha/ns-refer-clojure],
:in [3 0]}
{:path [:ns-clauses :require :clause],
:pred #{:require},
:val deftest,
:via
[:clojure.core.specs.alpha/ns-form
:clojure.core.specs.alpha/ns-require
:clojure.core.specs.alpha/ns-require],
:in [3 0]}
{:path [:ns-clauses :import :clause],
:pred #{:import},
:val deftest,
:via
[:clojure.core.specs.alpha/ns-form
:clojure.core.specs.alpha/ns-import
:clojure.core.specs.alpha/ns-import],
:in [3 0]}
....
So it appears the current version of Clojure (which uses clojure.spec to check ns forms) does detect the error in the code.

Clojure nested json response

I am new to clojure and I am trying to make a simple API with 3 endpoints.
I am trying to implement an endpoint which will take each row of a query and put it to a json.
So I have an SQLite database. These are my entries for example:
{:timestamp 2020-09-11 14:29:30, :lat 36.0, :long 36.0, :user michav}
{:timestamp 2020-09-11 14:31:47, :lat 36.0, :long 36.0, :user michav}
So I want a json response like the below:
{:get
:status 200
:body {:timestamp "2020-09-11 14:29:30"
:lat 36.0
:long 36.0
:user "michav"}
{:timestamp "2020-09-11 14:31:47"
:lat 36.0
:long 36.0
:user "michav"}
}
}
Here is my code that I am trying to fix it in order to get the above result.
(def db
{:classname "org.sqlite.JDBC"
:subprotocol "sqlite"
:subname "db/database.db"
})
(defn getparameter [req pname] (get (:params req) pname))
(defn output
"execute query and return lazy sequence"
[]
(query db ["select * from traffic_users"]))
(defn print-result-set
"prints the result set in tabular form"
[result-set]
(doseq [row result-set]
(println row)))
(defn request-example [req]
(response {:get {:status 200
:body (-> (doseq [row output]
{:timestamp (getparameter row :timestamp) :lat (getparameter row :lat) :long (getparameter row :long) :user (getparameter row :user)} ))
}}))
(defroutes my_routes
(GET "/request" [] request-example)
(route/resources "/"))
(def app (-> #'my_routes wrap-cookies wrap-keyword-params wrap-params wrap-json-response))
The error I encounter is the following :
java.lang.IllegalArgumentException: Don't know how to create ISeq from: mybank.core$output
RT.java:557 clojure.lang.RT.seqFrom
RT.java:537 clojure.lang.RT.seq
core.clj:137 clojure.core/seq
core.clj:137 clojure.core/seq
core.clj:65 mybank.core/request-example[fn]
core.clj:65 mybank.core/request-example
core.clj:63 mybank.core/request-example
response.clj:47 compojure.response/eval1399[fn]
response.clj:7 compojure.response/eval1321[fn]
core.clj:158 compojure.core/wrap-response[fn]
core.clj:128 compojure.core/wrap-route-middleware[fn]
core.clj:137 compojure.core/wrap-route-info[fn]
core.clj:146 compojure.core/wrap-route-matches[fn]
core.clj:185 compojure.core/routing[fn]
core.clj:2701 clojure.core/some
core.clj:2692 clojure.core/some
core.clj:185 compojure.core/routing
core.clj:182 compojure.core/routing
RestFn.java:139 clojure.lang.RestFn.applyTo
core.clj:667 clojure.core/apply
core.clj:660 clojure.core/apply
core.clj:192 compojure.core/routes[fn]
Var.java:384 clojure.lang.Var.invoke
cookies.clj:171 ring.middleware.cookies/wrap-cookies[fn]
keyword_params.clj:32 ring.middleware.keyword-params/wrap-keyword-params[fn]
params.clj:57 ring.middleware.params/wrap-params[fn]
json.clj:42 ring.middleware.json/wrap-json-response[fn]
Var.java:384 clojure.lang.Var.invoke
reload.clj:18 ring.middleware.reload/wrap-reload[fn]
stacktrace.clj:17 ring.middleware.stacktrace/wrap-stacktrace-log[fn]
stacktrace.clj:80 ring.middleware.stacktrace/wrap-stacktrace-web[fn]
jetty.clj:27 ring.adapter.jetty/proxy-handler[fn]
(Unknown Source) ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle
HandlerWrapper.java:127 org.eclipse.jetty.server.handler.HandlerWrapper.handle
Server.java:500 org.eclipse.jetty.server.Server.handle
HttpChannel.java:386 org.eclipse.jetty.server.HttpChannel.lambda$handle$1
HttpChannel.java:562 org.eclipse.jetty.server.HttpChannel.dispatch
HttpChannel.java:378 org.eclipse.jetty.server.HttpChannel.handle
HttpConnection.java:270 org.eclipse.jetty.server.HttpConnection.onFillable
AbstractConnection.java:311 org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded
FillInterest.java:103 org.eclipse.jetty.io.FillInterest.fillable
ChannelEndPoint.java:117 org.eclipse.jetty.io.ChannelEndPoint$2.run
EatWhatYouKill.java:336 org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask
EatWhatYouKill.java:313 org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce
EatWhatYouKill.java:171 org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce
EatWhatYouKill.java:135 org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce
QueuedThreadPool.java:806 org.eclipse.jetty.util.thread.QueuedThreadPool.runJob
QueuedThreadPool.java:938 org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run
(Unknown Source) java.lang.Thread.run
Your mistake is on this line:
(doseq [row output] ...
The error message
Don't know how to create ISeq from: mybank.core$output
gives the clue. The variable output is the function, not a sequence like doseq expects. You meant to call the output function, so you need to use parentheses to create a function call like:
(doseq [row (output)] ...
Update
You need to include an external library to convert between EDN data and a JSON string. My favorite way is my own library Tupelo Clojure. Use it like this:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(let [data [{:timestamp "2020-09-11 14:29:30", :lat 36.0, :long 36.0, :user "michav"}
{:timestamp "2020-09-11 14:31:47", :lat 36.0, :long 36.0, :user "michav"}]]
(println (edn->json data)))
with result:
[{"timestamp":"2020-09-11 14:29:30","lat":36.0,"long":36.0,"user":"michav"},
{"timestamp":"2020-09-11 14:31:47","lat":36.0,"long":36.0,"user":"michav"}]
You will need a line like this in your project.clj:
[tupelo "20.08.27"]
Please also see this template project for an easy way to get started. Enjoy!

Clojure - use a core.async channel with Yada/Aleph

I am trying to use Clojure manifold library, and in order to understand it, I need wanted to convert a core.async channel into a manifold stream.
I would like to create the equivalent the following using a core.async channel :
(require '[manifold.stream :as s])
(s/periodically 100 #(str " ok "))
;; Here is what I tried, it fails with an error 500
(let [ch (chan)]
(go-loop []
(>! ch " ok ")
(<! (timeout 100))
(recur))
(s/->source ch))
I am trying to feed a core.async channel into yada. The first code sample, using manifold.stream/periodic works, not the others using core.async. I tried on yada 1.0.0 and 1.1.0-SNAPSHOT.
Using manifold.stream/periodic works :
(def get-stream
(yada (fn [ctx]
(-> (:response ctx)
(assoc :status 202)
(assoc :body (s/periodically 1000 #(str (System/currentTimeMillis) " ")))))
{:representations [{:media-type "application/json"
:charset "UTF-8"}
{:media-type "application/edn"
:charset "UTF-8"}]}))
Using manifold.stream/->source returns an error 500 :
(def get-stream
(yada (fn [ctx]
(-> (:response ctx)
(assoc :status 202)
;; Similar to this : https://github.com/juxt/yada/blob/94f3ee93de155a8513b27e0508608691ed556a55/dev/src/yada/dev/async.clj
(assoc :body (let [ch (chan)]
(go-loop []
(>! ch " ok ")
(<! (timeout 100))
(recur))
(s/->source ch)))))
{:representations [{:media-type "application/json"
:charset "UTF-8"}
{:media-type "application/edn"
:charset "UTF-8"}]}))
;; Error on the page :
;; 500: Unknown
;; Error on GET
;; #error {
;; :cause "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: manifold.stream.async.CoreAsyncSource"
;; :via
;; [{:type clojure.lang.ExceptionInfo
;; :message "Error on GET"
;; :data {:response #yada.response.Response{:representation {:media-type #yada.media-type[application/json;q=1.0], :charset #yada.charset.CharsetMap{:alias "UTF-8", :quality 1.0}}, :vary #{:media-type}}, :resource #function[backend.routes.examples.ts/fn--57734]}
;; :at [clojure.core$ex_info invoke "core.clj" 4593]}
;; {:type java.lang.IllegalArgumentException
;; :message "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: manifold.stream.async.CoreAsyncSource"
;; :at [clojure.core$_cache_protocol_fn invoke "core_deftype.clj" 554]}]
;; :trace
Third attempt, with a core.async channel (different error 500) :
(def get-stream
(yada (fn [ctx]
(-> (:response ctx)
(assoc :status 202)
(assoc :body (chan)))
{:representations [{:media-type "application/json"
:charset "UTF-8"}
{:media-type "application/edn"
:charset "UTF-8"}]}))
;; Error on the page :
;; 500: Unknown
;; Error on GET
;; #error {
;; :cause "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: clojure.core.async.impl.channels.ManyToManyChannel"
;; :via
;; [{:type clojure.lang.ExceptionInfo
;; :message "Error on GET"
;; :data {:response #yada.response.Response{:representation {:media-type #yada.media-type[application/json;q=1.0], :charset #yada.charset.CharsetMap{:alias "UTF-8", :quality 1.0}}, :vary #{:media-type}}, :resource #function[backend.routes.api.subscribe/subscribe$fn--64130]}
;; :at [clojure.core$ex_info invoke "core.clj" 4593]}
;; {:type java.lang.IllegalArgumentException
;; :message "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: clojure.core.async.impl.channels.ManyToManyChannel"
;; :at [clojure.core$_cache_protocol_fn invoke "core_deftype.clj" 554]}]
;; :trace
The key error is this:
"No implementation of method: :to-body of protocol:
#'yada.body/MessageBody
found for class: manifold.stream.async.CoreAsyncSource"
It reveals that the yada version you are using is trying to coerce a body type it doesn't understand. More recent versions of yada are more permissive about what you can send through to the web-server, and allow anything through that it doesn't know how to transform.

Clojure throws an exception when I don't expect it to

I have this code to get data from sumo logic and other services.
core.clj has this, which parses the arguments and routes it to the right function in route.clj
(def cli-options
[
["-a" "--app APPNAME" "set app. app can be:
sumologic or jira"]
["-?" "--help"]
])
(defn -main
[& args]
(let [{:keys [options summary errors arguments]} (parse-opts args cli-options)]
(cond
(:app options) (route/to (:app options) options arguments)
:else (print_usage summary))))
route.clj has this:
(defn to
[app options arguments]
(case app
"jira" (jira/respond options arguments)
"sumologic" (sumo/respond)))
And then sumo.clj has this. there are other functions, of course, but showing just the relevant parts.
(defn get-env-var
[var]
(let [result (System/getenv var)]
(if (nil? result)
(throw (Exception. (str "Environment variable: " var " not set. Aborting")))
result)))
(def access_key
(let [user (get-env-var "SUMO_ID")
pass (get-env-var "SUMO_KEY")]
[user pass]))
(defn respond
[]
(let [{:keys [status body error] :as response} (http/get endpoint rest-options)]
(if error
(println error)
(print-response body))))
When I run the program using leiningen as lein run -- -? or even just lein run, I get this error, even though I haven't explicitly called the sumologic function. What am I doing wrong and what are things that I can do differently?
Caused by: java.lang.Exception: Environment variable: SUMO_KEY not set. Aborting
at clarion.sumo$get_env_var.invoke(sumo.clj:14)
at clarion.sumo$fn__3765.invoke(sumo.clj:19)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3553)
You have def'd access_key so it is being evaluated when you load the application. You probably want to make it a function instead.

can't connect my compojure app to postgres db (following heroku tutorial)

i'm following along the heroku compojure tutorial. When I get to the point in the tutorial where a table is created, I get the following error message:
user=> (require '[clojure.java.jdbc :as sql])
nil
user=> (sql/with-connection (System/getenv "DATABASE_URL")
#_=> (sql/create-table :testing [:data :text]))
IllegalArgumentException db-spec postgresql://localhost:5432/lord is missing a required parameter clojure.java.jdbc.internal/get-connection (internal.clj:147)
I've tried adding the name of the linux user i created the db with and that users's password
to the DATABASE_URL but no luck. I'm missing something here and i'm not sure what it is. Where is db-spec defined might be the right question but i'm not exactly sure.
Try putting the connection into a map like this (this is mysql specific but you get the gist):
(def db-conn {
:classname "com.mysql.jdbc.Driver" ;must be in classpath
:subprotocol "mysql"
:subname (str "//" DBHOST ":" DBPORT "/" db-name)
; Any additional keys are passed to the driver as driver-specific properties.
:user DBUSER
:password DBPASS})
(sql/with-connection db-conn
(sql/create-table :testing [:data :text]))))
Hope this helps.