I found solution:
(-> (peridot/session (yada/handler core/graphql))
(peridot/request "/graphql"
:content-type "application/graphql"
:request-method :post
:body "{ game_by_id(id: \"1237\") { name designers { name }}}")
:response
(deref)
:body
(bs/to-string)
;(edn/read-string)
)
I would like to make a handler to bidi routes handler instead of 1 resource, but this one I don’t know how to do
(-> (peridot/session (as-handler core/handler))
(peridot/request "/graphql"
:content-type "application/graphql"
:request-method :post
:body "{ game_by_id(id: \"1237\") { name designers { name }}}")
:response
(deref)
:body
(bs/to-string)
;(edn/read-string)
)
^ yeah, this one work perfect with routing 🙂oh there is the same issue about :body like with response-for. It is nil.
It looks like it is impossible to use :post with :body for testing with yada, it needs to be with aleph server.
that’s why I use this macro:
https://github.com/juxt/yada/blob/ba10db43e1a332f0bfd352cd8ea39f176190c4ff/src/yada/test.clj#L40
or I use integration testing, I think that’s even better if you want to test your service as an end consumer
yeah, I will probably end with one of this solution
Not sure why :body is nil, but maybe I don’t have to know 🙂
I think because the interceptor chain doesn’t have the right interceptor which does the schema thing
if I remember correctly, long time ago 🙂
My main issues is probably about not understanding difference between ring vs aleph + manifold.
one difference is that aleph is designed for asynchronous requests
that one I understand 🙂
probably only that one haha 😉
Do you understand why Manifold is needed? I understand general description, but I don’t really understand what it is about on deeper level. Why http/get
can’t return normal string instead.
manifold gives an abstraction over futures/promises and whatnot. it’s used in yada to have asynchronous interceptors
(ytest/with-aleph url (as-handler core/handler)
@(http/post (str url "/graphql")))
Syntax error (ExceptionInfo) compiling at (core_test.clj:88:3).
status: 404
How to use your macro? I always have 404. I was trying use core/handler
on many ways but always 404(def handler
["/" [["authentication" authentication]
["graphql" graphql]
[true not-found]]])
@kwladyka yada is async/non-blocking - http/get
can't return a String
, that would be a blocking API, so it has to return a Promise<String>
- and yada is using Manifold's Deferred
as its promise implementation
it uses Manifold's Deferred
instead of clojure's standard promise
because Manifold's Deferred
supports completion callbacks and therefore the composition of promise values required to implement such things as interceptor chains (and most useful non-blocking computations with promises)
it could also use another non-blocking abstraction, like core.async
, but that would probably lead to a very different API
i much prefer promises over core.async
for point values
“so yada is async and in the same time use promises to be even more async and give better performance”
core.async
is ok for streams of values - although Manifold's stream
plays very nicely with Deferred
so is my go to on clojure
@kwladyka yada uses promises as its principal async abstraction, rather than to improve performance
it's built on aleph, which is also Deferred
based, so Deferred
is the natural async abstraction for yada to use
Hmm I am probably not enough experienced to imagine specific situations and benefits from it, but I understand general concept 🙂
Thanks
@kwladyka how to use the macro, just do a search on github: https://github.com/juxt/yada/search?q=with-aleph&unscoped_q=with-aleph
resource (new-classpath-resource "static")
<- it is instead of real resource, so I am not sure about real use case
and this test expect 404
Which is what I get 🙂
But I expect something different
@borkdude I saw that test, but still I don’t know how to use it
@kwladyka Another example:
(with-aleph url
(y/resource {:methods
{:post {:parameters {:body {:foo s/Str}}
:response (fn [ctx] {:a 1})
:produces #{"application/json"}
:consumes #{"application/json"}}}})
(let [response (client/request
(request :post url {}))
body (cheshire/decode (str (:body response)) true)]
(is (= "missing-required-key" (-> body :error :foo)))
(is (= "Schema validation error" (-> body :message)))))
note that (y/resource ...)
is my custom yada resource with error handler builtin
What is client/request
?
that’s just a random HTTP client, shouldn’t matter. but in this case it’s clj-http: [clj-http.client :as client]
any http client should do, since it’s just a webserver running on some port
Syntax error (ClassCastException) compiling at (core_test.clj:101:3).
class clojure.lang.PersistentArrayMap cannot be cast to class [B (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; [B is in module java.base of loader 'bootstrap')
Jan 20, 2019 9:37:23 PM io.netty.util.concurrent.DefaultPromise notifyListener0
WARNING: An exception was thrown by aleph.netty$wrap_future$reify__15341.operationComplete()
java.lang.NoClassDefFoundError: Could not initialize class manifold.deferred.Deferred$fn__2465
at manifold.deferred.Deferred.success(deferred.clj:398)
at manifold.deferred$success_BANG_.invokeStatic(deferred.clj:243)
at manifold.deferred$success_BANG_.invoke(deferred.clj:240)
at aleph.netty$wrap_future$reify__15341.operationComplete(netty.clj:199)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:511)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:485)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:424)
at io.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:94)
at io.netty.util.concurrent.MultithreadEventExecutorGroup$1.operationComplete(MultithreadEventExecutorGroup.java:117)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:511)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:485)
at io.netty.util.concurrent.DefaultPromise.access$000(DefaultPromise.java:33)
at io.netty.util.concurrent.DefaultPromise$1.run(DefaultPromise.java:435)
at io.netty.util.concurrent.GlobalEventExecutor$TaskRunner.run(GlobalEventExecutor.java:248)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
It doesn’t work for meunless your’s y/resource
is much different, than yada/resource
(ytest/with-aleph url core/graphql
(-> @(http/post url #_(str url "/graphql") {:content-type "application/json"
:body "{\"email\":\"<mailto:foo@example.com|foo@example.com>\",\"password\":\"qwaszx\"}"
:throw-exceptions false})
:body
(slurp)))
but this work!now I have to solve how to do it with full routing
And that one is harder
vmodel# (vhosts-model [:* ["/" resource#]])
- oh it is probably not designed for that purpose
@kwladyka my y/resource is a custom yada resource, that has our interceptor chain and error interceptor chain.
I’m testing the behavior of the error rendering in that example
Am I understand it correctly ? This macro is designed to test only 1 resource. There is no way to use it with bidi routing?
no, it’s only designed to test a single resource. for full testing I’d use integration testing
Do you have Docker architecture? Kubernetes for testing or something similar? If yes what do you use for integration testing?
Separate Clojure app only for testing? Bash?
We just test against a running instance with a database with real data in it (some would call it a staging environment)
but it could also be done with a script which spins up your app and then starts the tests in a different process
however you like it
sure, just like to listen other people experience 🙂
Personally I will choose probably Dockerfile + bash / Clojure tests for this project on staging environment kubernetes
I’m doing something similar with http://re-find.it which is just a SPA without a server, there I start a webserver that serves the static content, and then start some browser tests: https://github.com/borkdude/re-find.web/blob/master/test/re_find/web_test.clj#L152
Docker makes sense too. But for the app we’re using yada for, it’s very resource intensive in terms of memory and needs a lot of other services. That’s why we test it “live”
“live” mean on production?
a staging environment that’s the version before it goes to production
> That’s why we test it “live” Not sure what you mean here by “live”
manually?
I mean, it’s an environment that’s running 100% of the time and also used for QA
no, scripted of course
I just use clojure.test + clj-http for the API tests
and etaoin for browser tests
the browser is running in a Docker container
all of this isn’t directly related to whether you use yada, ring, pedestal, just choices you can make
Yeah, I want to make SaaS label printer as my private project. I did the job about generate labels very fast. Making Ops, GraphQL, SaaS will take me probably a few months heh
Probably I will choose similar way for testing