Fork me on GitHub
#pedestal
<
2021-08-10
>
v3ga13:08:33

Newbie issue continued. For the life of me I can’t delete a record with my route. “delete-book!” works as far as the db transaction goes but for my route I keep getting a 404. Once I have all of the basics i’ll be writing blog posts on this since the documentation is sparse/geared towards people with a bit more experience.

Joe Lane13:08:17

Doesn’t look like the delete-book interceptor returns a ring response. Does it actually delete the book but just return 404?

v3ga13:08:54

No, it’s not deleting the book either.

Joe Lane13:08:13

Ok so does the request ever get to that handler? Pprint the ctx before attempting to get the book ID

v3ga13:08:41

No it doesn’t get that far. This kicks out when I attempt “DELETE http://locahost:3000/book/11 => :initiated21-08-10 13:42:17 Akuma.local INFO [io.pedestal.http:116] - {:msg “DELETE /book/11”, :line 80}

souenzzo14:08:32

Your function delete-book returns something from db/delete-book! This something do not have a :status map Pedestal default not-found handler will seach for {:status ...} in the response, if not found, then it will put 404. A possible solution

(def delete-book
  {:name ::delete-book
   :enter (fn [context]
            (let [id (-> context :path-params :id)]
              (db/delete-book! id)
              {:status 202}))})
https://github.com/pedestal/pedestal/blob/master/service/src/io/pedestal/http.clj#L93

v3ga14:08:44

@U2J4FRT2T ohh…. hmm, where can I learn more how all of this works? Just reading the source code? It’s hard to figure out where to begin and what direction to go.

Joe Lane14:08:05

> Doesn’t look like the delete-book interceptor returns a ring response. Handlers must return ring-responses

v3ga14:08:56

@U0CJ19XAM at one point I returned with this but was running into the same issue.

souenzzo14:08:39

The code in most of cases is simple to read. I'm happy with response-for and other tools like scope capture that allow me to quickly experiment, explore, prototype and understand the behavior of the library. Would be nice if more people write tutorials about pedestal, or contribute to the official docs.

v3ga14:08:13

Yeah… I think as I figure things out I’ll make some blog posts. There’s actually a decent book that uses pedestal all the way through but they also throw in Datomic and focus on building microservices. There’s good information but they drown you with other advanced topics.

souenzzo14:08:28

In your particular case, you are in a "unspecified area", once "handlers should return ring response map" and you are not returning it, it's hard to document/explain which behavior will occur

souenzzo14:08:47

But for sure, it's a common mistake, so it worth a effort to do better docs/explain on it.

v3ga14:08:49

@U2J4FRT2T hmm adding {:status 202} didn’t make it work either. I’m still having trouble believing its even reaching the interceptor. I tried to print ‘context’ out with hashp and it never gets there…. I’m going to take a step back and focus on getting a grasp on context/request/response maps. I’m sure i’ll be back on here in a few but for now I think its better that I slow down and take a step back. Completely lost.

souenzzo14:08:28

(def delete-book
  {:name ::delete-book
   :enter (fn [context]
            (let [id (-> context :path-params :id)]
              (db/delete-book! id)
             (assoc context :response  {:status 202})))})

souenzzo14:08:00

(-> context :request :path-params :id)

👍 2
souenzzo14:08:45

a handler is a function that receive a request and returns a response, as in ring meaning. an interceptor has enter/leave/error stages, each of these receives and returns a context, which contains a request map, maybe a response map. error stages receives 2 args: ctx and exception

souenzzo14:08:41

With this form, you can prototype and explore pedestal in a quickly way:

(require '[io.pedestal.http :as http]
  '[io.pedestal.test :refer [response-for]])

(let [routes #{["/book/:id" :delete
                [{:name  :book-delete
                  :enter (fn [ctx]
                           (def _ctx ctx)
                           (assoc ctx :response {:status 202}))}]]}
      service-fn (-> {::http/routes routes}
                   http/default-interceptors
                   http/create-servlet
                   ::http/service-fn)]
  (response-for service-fn
    :delete "/book/123"))
=> {:status  202,
    :body    "",
    :headers {"Strict-Transport-Security"         "max-age=31536000; includeSubdomains",
              "X-Frame-Options"                   "DENY",
              "X-Content-Type-Options"            "nosniff",
              "X-XSS-Protection"                  "1; mode=block",
              "X-Download-Options"                "noopen",
              "X-Permitted-Cross-Domain-Policies" "none",
              "Content-Security-Policy"           "object-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;"}}
;; now you can inspect ctx
_ctx 
{:request {.... } ...}
Where I do (def _ctx ctx), you can use (tap> ctx) or any other "scope capture" method. There is many libraries that do that. You can also create a deftest to do that even quickier or capture a scope in your "real" running app It will help you to inspect/experiment

❤️ 2
v3ga14:08:51

@U2J4FRT2T ok, going to toy with it. I’ve been meaning to toy with tap> as well.