This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-06
Channels
- # adventofcode (181)
- # aws (6)
- # beginners (112)
- # boot (38)
- # cider (11)
- # cljs-dev (12)
- # cljsrn (2)
- # clojure (187)
- # clojure-greece (31)
- # clojure-italy (19)
- # clojure-new-zealand (1)
- # clojure-poland (1)
- # clojure-spec (20)
- # clojure-uk (114)
- # clojurescript (97)
- # core-logic (25)
- # cursive (3)
- # data-science (17)
- # datascript (3)
- # datomic (23)
- # defnpodcast (1)
- # duct (5)
- # emacs (3)
- # fulcro (299)
- # graphql (108)
- # jobs (1)
- # juxt (4)
- # lein-figwheel (7)
- # leiningen (1)
- # lumo (9)
- # nrepl (2)
- # off-topic (10)
- # om (2)
- # onyx (36)
- # pedestal (1)
- # perun (3)
- # re-frame (14)
- # reagent (12)
- # ring (2)
- # rum (11)
- # shadow-cljs (6)
- # spacemacs (4)
- # unrepl (8)
Lacinia do a get on the value of every relevant element from the map returned from resolver. There lacinia do it? It's a get? A select-keys?
It does filter out keys that aren’t expected by your model in the schema. The way it does, I don’t know, you could inspect the code base, its open source!
What would be the reason to know that? Any specific issue that caused something un-desired?
I'm not following the question; perhaps you could pose it in terms of a query, a resolved map, and an expected result map?
I find it!
I just understand that I just want a resolve
on types
{:objects {:user {:fields {:name {:type 'String
:resolve (fn [_ _ c]
(get c :user/nome))}}}}}
Not working 😄Hi, can anyone show me an example, how to report to user of graphql api an error?
@andrewtropin errors in a graphql result should be under a top-level errors
field. see the examples partway down this page: http://graphql.org/learn/validation/
Or, are you asking a about how to do it using a specific graphql implementation?
Yep, I want to know how to do it in lacinia
here’s a test which shows a simple error result: https://github.com/walmartlabs/lacinia/blob/master/test/com/walmartlabs/lacinia/resolver_errors_test.clj#L35
(defn resolve-as
"Invoked by field resolvers to wrap a simple return value as a ResolverResult.
The two-arguments version is a convienience around using [[with-error]].
This is an immediately realized ResolverResult."
([resolved-value]
(->ResolverResultImpl resolved-value))
([resolved-value resolver-errors]
(->ResolverResultImpl (with-error resolved-value resolver-errors))))
There is this function you could use i expectYep, already found resolve-as
, but it works not as expected for me
(defn resolve-token [ctx args value] (resolve/resolve-as {:test :value} {:message :help}))
{ "data": { "get_token": { "token": "{:resolved_value #object[com.walmartlabs.lacinia.resolve$with_error$reify__40981 0x5538705f \"com.walmartlabs.lacinia.resolve$with_error$reify__40981@5538705f\"]}" } } }
instead of top-level :errors key
{:resolved_value #object[com.walmartlabs.lacinia.resolve$with_error$reify__40981 0x5538705f \"com.walmartlabs.lacinia.resolve$with_error$reify__40981@5538705f\"]}
Yep, I just call it inside resolver, you can see it few messages earlier
0.22.1
"token": "{:resolved_value #object[com.walmartlabs.lacinia.resolve$with_error$reify__40981 0x5538705f \"com.walmartlabs.lacinia.resolve$with_error$reify__40981@5538705f\"]}"
(defn resolve-token
[ctx args value]
(resolve/resolve-as {:test :value} {:message :help}))
it is a resolver for token field{
"data": {
"get_token": {
"token": "{:resolved_value #object[com.walmartlabs.lacinia.resolve$with_error$reify__40981 0x5538705f \"com.walmartlabs.lacinia.resolve$with_error$reify__40981@5538705f\"]}"
}
}
}
And that what I get in graphiql.I get an error when I try a keyword as the message {:message :help}
, but it works as a string {:message "help"}
. can you try that?
@guy https://github.com/walmartlabs/lacinia/blob/27536ceecf73750bc243cf58cbbe31fd738dc7b6/src/com/walmartlabs/lacinia/executor.clj#L37
(str "Errors must be nil, a map, or a sequence of maps "
"each containing, at minimum, a :message key.")
to
(str "Errors must be nil, a map, or a sequence of maps "
"each containing, at minimum, a :message key with a string value.")
Perhaps?:get-token {:type :String
:args {:login {:type :String}
:password {:type :String}}
:resolve :authn/get-token}
(defn resolve-token
[ctx args value]
(resolve/resolve-as "test" {:message "help"}))
{
"errors": [
{
"message": "Path de-references through a scalar type.",
"query-path": [
"get_token"
],
"locations": [
{
"line": 31,
"column": 12
}
],
"field": "token"
}
]
}
I moved resolver one level up and now getting an errors key, but it's not what I want
If I use this resolver for inner field it works as I described below. For "root" mutation it shows an error, but different from I passed to resolve-as
It seems like lacinia doesn't check if it has a ResolverResult type
I spotted the problem. I have wrappers for my resolvers, which converts cases, which breaks all things =( Sorry for distracting you.
(defn wrap-resolver [f]
(fn [context arguments value]
(transform-to-sc (f context
(transform-to-kc arguments)
(transform-to-kc value)))))
(attach-resolvers (transform-map-values graphql-resolvers wrap-resolver))
Something like that.Problem solved. It works as expected without those wrappers. Thank you for help.
This is exactly what the new wrap-resolver-result is for! https://github.com/walmartlabs/lacinia/blob/7d8da8af2863f9d6bd7d6a8b02ec895d75b3d8c1/src/com/walmartlabs/lacinia/resolve.clj#L179
Oh, thanks a lot, but I already solved it with :default-field-resolver
I've got re-graph to its first release: https://github.com/oliyh/re-graph
The server side story for graphql is great with Lacinia and lacinia-pedestal, but I found the client side almost non-existent
is that similar to https://github.com/Vincit/venia @oliy?
Hi @guy venia is for constructing the query itself using data structures instead of strings
So they complement each other, you could use them together (I intend to do this as soon as my PR for venia is merged!)
Specifically how to define routes and add non-graphql related routes for things like pinging the server and echoing a request
@admay wouldn’t the non-graphql things just be separate routes?
(route/expand-routes [["/graphql" :get graphql-interceptors :route-name ::graphql-get]
["/ping" :get ping-interceptor :route-name ::ping]])
I’ve never used Pedestal before, I’m more of a Ring/Compojure guy myself. In the Lacinia docs, there’s a section in the service map function (http://walmartlabs.github.io/lacinia-pedestal/com.walmartlabs.lacinia.pedestal.html#var-service-map) about defining routes but I have no idea how to actually use it or wrap around it
@admay that’s a new part of the api that hadn’t noticed. here’s an example that i created using that: https://github.com/sashton/lacinial-pedestal-demo/blob/master/src/graphql_demo/server.clj#L32-L35 I created the project following the steps in the lacinia-pedestal readme: https://github.com/walmartlabs/lacinia-pedestal/blob/master/README.md To see it in action:
$ lein run
$ curl localhost:8888/graphql -X POST -H "content-type: application/graphql" -d '{ hello }'
{"data":{"hello":"world"}}
$ curl localhost:8888/ping
Ping
$ curl localhost:8888/pong
Pong
Just to demonstrate, ping is a handler (like a ring-style), pong is a pedestal interceptor.@admay I updated the tutorial last week to cover the use of service-map: http://lacinia.readthedocs.io/en/latest/tutorial/pedestal.html
With 0.23.0/0.5.0 out, I'm deciding on what the next tutorial will be. Probably start introducing mutations, but with an Atom as the database.
Oh, look. ReadTheDocs now puts ads at the bottom of each page. That's what we get for leaning on their free offering.
@hlship I like the idea of the next bit of functionality being mutations and then maybe using a union type IRL for an update with the object you’re updating and a status object. In other words, add an update mutation and return either the updated record or some kind of status type thing and a failure message. I’m not sure how ‘by the books’ the practice is but those failure messages have been useful to my team in the past
To be honest, I'm still stuggling with understanding/codifying the best approach to errors. The main challenge is whether to use error maps (e.g., with resolve-as
) or a union type (as you discussed) for handling expected errors. Also, classifying errors into categories, such as expected, unexpected, system failure, etc.
I've paid lipservice to doing this virtually first in designing your schema but haven't acted on that as yet.
We have seen some confusions in our iOS and Android teams about what to do; one team treats any error in the :errors
result key as a total failure of the request (such that we actually remove errors in most cases).
But we do need to come up with a reasonable way of marking errors that represent a failure that might be presented to the user ("That gizmo does not exist." or "You don't have authorization to frob that floob.") vs. something more problematic ("java.lang.NullPointerException: We're Screwed").
That is, I would say that in addition to the :message key, there should be a :severity key, and perhaps a :kind key (where :kind is a unique name for the type of exception, such as :object/missing or :auth/insufficient-access).
This is all good sounding stuff! The reason our status
type came to be was a lack of understanding in how to allow errors to propagate to the requestor. We would have a lot of ‘you can’t mutate that’ or ‘that thing doesn’t exist’ type errors that are far from app breaking. Our solution was to check the return value of our database transaction and based on that either return the thing as a thing or return a failure status type with a bit of information.
It was so useful because it allowed the requestor to see things like where conditions from their query, the type that they’re using to query, etc… while keeping the ‘uh oh’ logic on the front end nice and simple.
if (thing.isAnError)
displayTheError(thing)
else
displayTheThing(thing)
We we’re able to avoid having to write a lot of code in our already complicated front end by leveraging the error output on the back endAs far as how Lacinia handles it, I like the idea of the error map returning the error status and message given there’s some ability to work with said map.
I would actually prefer that so that the behavior came for free rather than having to build the schema around handling errors
And what Lacinia adds is that it automatically extends the provided error map with a lot of context (e.g., path to field in query, arguments, line/column in query document).
Anyone got a quick link for hooking in authentication to lacinia-pedestal? I'd like to auth against JWT generated by a separate (Ring) auth server via buddy
, and I'm still getting up to speed on pedestal
https://github.com/jborden/leaderboard-api/blob/master/src/leaderboard_api/handler.clj#L67
Isn't that ring-based, though? Will ring middleware work with pedestal? I'm starting from this: http://lacinia.readthedocs.io/en/latest/tutorial/pedestal.html
OH! I misread your question! Try this instead! https://github.com/sashton/lacinial-pedestal-demo/blob/master/src/graphql_demo/server.clj#L32-L35
Interesting. Thanks, I will give it a look