graphql

erwinrooijakkers 2019-09-17T14:52:00.005300Z

Hi all

erwinrooijakkers 2019-09-17T14:52:06.005600Z

How do I add an extra interceptor to Pedestal Lacinia?

erwinrooijakkers 2019-09-17T14:52:45.006400Z

This does not work:

(def my-interceptor
  {:name ::my-interceptor
   :enter (fn [context]
            (log/info "hello!" context)
            context)})

(defn interceptors [compiled-schema]
  (let [options {}]
    (conj (pedestal/default-interceptors compiled-schema options)
          my-interceptor)))

(defrecord Server [schema-provider server port]
  component/Lifecycle
  (start [this]
    (let [compiled-schema (:schema schema-provider)]
      (assoc this :server (-> compiled-schema
                              (pedestal/service-map {:graphiql true
                                                     :port port
                                                     :interceptors (interceptors compiled-schema)})
                              http/create-server
                              http/start))))
  (stop [this]
    (http/stop server)
    (assoc this :server nil)))

erwinrooijakkers 2019-09-18T09:41:38.009600Z

Thanks!

erwinrooijakkers 2019-09-18T09:42:06.009800Z

What does use of inject look like?

erwinrooijakkers 2019-09-18T09:43:33.010Z

Ah I see a test like this:

deftest inject-before
  (let [fred {:name :fred}
        barney {:name :barney}
        wilma {:name :wilma}]
    (is (= [fred wilma barney]
           (inject [fred barney] wilma :before :barney)))))

erwinrooijakkers 2019-09-18T09:44:24.010200Z

And I should put it before :name :io.pedestal.http.impl.servlet-interceptor/ring-response

erwinrooijakkers 2019-09-18T14:48:11.014900Z

Adding after inject-app-context does not provide data on the context…

erwinrooijakkers 2019-09-18T14:48:29.015100Z

(defn- inject-roles-interceptor [interceptors]
  (pedestal/inject interceptors
                   roles-interceptor
                   :after
                   ::pedestal/inject-app-context))

erwinrooijakkers 2019-09-18T14:48:36.015300Z

But the context is empty in the resolver

erwinrooijakkers 2019-09-18T14:48:57.015500Z

(defn- use-roles-resolver
  [ds {:keys [columns]}]
  (fn [{:auth/keys [roles] :as context} data _]
    (log/info roles)
    (log/info context)
 ))

(defn- resolver-map
  [ds]
  {:query/component-basic (use-roles-resolver ds)})
Does not print the roles that are added by the interceptor…

erwinrooijakkers 2019-09-18T14:49:23.015900Z

It does not have an added key

erwinrooijakkers 2019-09-18T14:49:30.016100Z

Any idea why? Should I inject somehwere else?

nenadalm 2019-09-18T16:57:30.016700Z

I didn't notice example interceptor from you, did I miss it or did you not post it? Did you add the required key into :lacinia-app-context inside of the context? Example of interceptor (untested) that adds key :custom-role-key inside context with value :some-value

(def roles-interceptor
  {:enter (fn [context]
    (assoc-in context [:lacinia-app-context :custom-role-key] :some-value})

erwinrooijakkers 2019-09-19T07:57:57.017Z

Aaaaaaaaaaaha

erwinrooijakkers 2019-09-19T07:58:01.017200Z

I added it to the context

erwinrooijakkers 2019-09-19T07:58:05.017400Z

Not the lacinia-app-context

erwinrooijakkers 2019-09-19T07:58:11.017600Z

Awesome thanks a lot

erwinrooijakkers 2019-09-19T08:15:48.018800Z

Hmm that also does not work

erwinrooijakkers 2019-09-19T08:33:25.019Z

I now try replacing the full inject-app-context interceptor with my own code

erwinrooijakkers 2019-09-19T08:52:34.020800Z

This also does not work:

(defn my-own-inject-app-context-interceptor
  "Adds a `:lacinia-app-context` key to the request, used when executing the query.
  The provided app-context map is augmented with the request map, as
  key `:request`.

  The interceptor extracts authentication information from the request and
  exposes that as the app-context's `:auth/roles` key."
  {:added "0.2.0"}
  [app-context]
  (interceptor
    {:name ::inject-app-context
     :enter (fn [context]
              (let [roles (get-roles context)]
                (assoc-in context [:request :lacinia-app-context]
                          (assoc app-context :request (:request context) :auth/roles roles)))}))

(defn- inject-roles-interceptor [interceptors]
  "Replace the default interceptor that creates the app context with one that also
  validates and retrieves the roles from the JWT and adds those to the
  app-context under the `:auth/roles` key."
  (pedestal/inject interceptors
                   my-own-inject-app-context-interceptor
                   :replace
                   ::pedestal/inject-app-context))

erwinrooijakkers 2019-09-19T08:52:40.021Z

Nothing available

erwinrooijakkers 2019-09-19T08:52:43.021200Z

Not even the request then

erwinrooijakkers 2019-09-19T08:54:53.021400Z

Ah maybe this one should begin with :request

erwinrooijakkers 2019-09-19T08:54:56.021600Z

(def roles-interceptor
  {:enter (fn [context]
    (assoc-in context [:lacinia-app-context :custom-role-key] :some-value})

erwinrooijakkers 2019-09-19T08:55:03.021800Z

So :request :lacinina-app-context

erwinrooijakkers 2019-09-17T15:37:31.006800Z

Goal is to access the JWT token in the x-auth-token header of the HTTP request

erwinrooijakkers 2019-09-17T15:55:58.007Z

Ah I heard from a colleague that the request is also available in the context of a resolver

nenadalm 2019-09-17T18:55:54.007100Z

It should work as default-interceptors function returns vector of interceptors: https://github.com/walmartlabs/lacinia-pedestal/blob/2e7a7b82742c09a9f65ff30ecccb7c4e8f906799/src/com/walmartlabs/lacinia/pedestal.clj#L416 The thing is though that the last of the default interceptors probably adds response to the context: https://github.com/walmartlabs/lacinia-pedestal/blob/2e7a7b82742c09a9f65ff30ecccb7c4e8f906799/src/com/walmartlabs/lacinia/pedestal.clj#L354 which causes pedestal to omit rest of the interceptors: http://pedestal.io/reference/servlet-interceptor#_early_termination so your interceptor even if present is not called. If you want to add your own interceptor, you should add it before last default interceptor. You can use inject helper fn for that: https://github.com/walmartlabs/lacinia-pedestal/blob/2e7a7b82742c09a9f65ff30ecccb7c4e8f906799/src/com/walmartlabs/lacinia/pedestal.clj#L81 and yes. :request is available to field resolvers, thanks to this interceptor: https://github.com/walmartlabs/lacinia-pedestal/blob/2e7a7b82742c09a9f65ff30ecccb7c4e8f906799/src/com/walmartlabs/lacinia/pedestal.clj#L318 (you can create your own interceptor that will add more stuff into context and inject it after inject-app-context-interceptor)

orestis 2019-09-17T19:12:58.007500Z

Yep, under :request