ring-swagger

ring-swagger & compojure-api
stathissideris 2018-08-14T14:29:32.000100Z

hi

stathissideris 2018-08-14T14:29:56.000100Z

it seems like muuntaja doesn’t like this:

(GET "/echo" req
       :summary "foo"
       :return {:schema s/Any}
       (resp/ok req))

stathissideris 2018-08-14T14:30:15.000100Z

I get:

<h2>HTTP ERROR: 500</h2>
<p>Problem accessing /api/echo. Reason:
<pre>    Malformed application/json in :muuntaja/encode</pre></p>
<hr /><i><small>Powered by Jetty://</small></i>

stathissideris 2018-08-14T14:30:20.000100Z

why is that?

valtteri 2018-08-14T14:38:43.000100Z

req contains probably stuff that can’t be serialized into JSON in a sensible way. Try returning (pr-str req) instead.

stathissideris 2018-08-14T14:40:17.000100Z

thanks!

stathissideris 2018-08-14T14:41:30.000100Z

progress, I now get this 500:

< HTTP/1.1 500 Server Error
< Date: Tue, 14 Aug 2018 14:41:30 GMT
< Content-Type: application/json; charset=utf-8
< Content-Length: 2068
< Server: Jetty(9.2.24.v20180105)
<
* Connection #0 to host localhost left intact
{"schema":"{:schema Any}","errors":"(not (map? a-java.lang.String))","type":"compojure.api.exception/response-validation","coercion":"schema","value":"{:compojure.api.request/muuntaja <<Muuntaja>>, :compojure.api.request/coercion :schema, :compojure.api.request/swagger [{:consumes #{\"application/json\" \"application/transit+msgpack\" \"application/transit+json\" \"application/edn\"}, :produces #{\"application/json\" \"application/transit+msgpack\" \"application/transit+json\" \"application/edn\"}}], :ssl-client-cert nil, :protocol \"HTTP/1.1\", :compojure.api.request/paths {:paths #linked/map [[\"/api/foo\" {:get {:tags #{\"api\"}, :summary \"returns foo\"}}] [\"/api/echo\" {:get {:tags #{\"api\"}, :summary \"echoes the request\", :responses {200 {:schema {:schema Any}, :description \"\"}}}}] [\"/api/hello\" {:get {:tags #{\"api\"}, :parameters {:query {:name java.lang.String}}, :responses {200 {:schema {:message java.lang.String}}, 404 {}, 500 {:schema Any}}, :summary \"greets you\"}}]]}, :remote-addr \"0:0:0:0:0:0:0:1\", :ring.swagger.middleware/data {:info {:title \"Geniki Taxydromiki API\", :description \"\"}, :tags [{:name \"api\", :description \"some apis\"}]}, :params {}, :compojure.api.request/lookup {:compojure.api.swagger/swagger {\"/swagger.json\" {:method :get}}}, :route-handler #function[compojure.core/wrap-response/fn--16907], :route-params {}, :headers {\"user-agent\" \"curl/7.54.0\", \"accept\" \"*/*\", \"host\" \"localhost:8080\"}, :server-port 8080, :muuntaja/request nil, :content-length nil, :form-params {}, :compojure/route [:get \"/echo\"], :query-params {}, :content-type nil, :path-info \"/echo\", :character-encoding nil, :context \"/api\", :uri \"/api/echo\", :server-name \"localhost\", :query-string nil, :muuntaja/response #FormatAndCharset{:format \"application/json\", :charset \"utf-8\"}, :body #object[org.eclipse.jetty.server.HttpInputOverHTTP 0x6c72f437 \"HttpInputOverHTTP@6c72f437\"], :scheme :http, :request-method :get, :route-middleware #function[clojure.core/comp/fn--5529]}","in":["response","body"]}%

stathissideris 2018-08-14T14:42:03.000100Z

I’m not sure how compojure.api decides what content-type to send…

valtteri 2018-08-14T14:44:02.000100Z

And.. I think this definition

:return {:schema s/Any}
means that it’s expecting a map with key :schema and any value from the handler. I think you probably want to have just
:return s/Any

stathissideris 2018-08-14T14:47:03.000100Z

correct! I do get a string now

stathissideris 2018-08-14T14:47:06.000100Z

many thanks

valtteri 2018-08-14T14:47:12.000100Z

No problem!

stathissideris 2018-08-14T14:47:39.000100Z

but still. say I wanted this particular endpoint to return html, how would I do it?

valtteri 2018-08-14T14:51:03.000100Z

Hmmm I don’t remember from top of my head but you could try returning something like this from the handler

{:status 200
 :body "<html><body><p>blabalbal</p></body></html>"
 :headers {"Content-Type" "text/html"}}

stathissideris 2018-08-14T14:51:32.000100Z

(GET "/echo" req
       :summary "echoes the request"
       :return s/Any
       (-> (str "<pre>" (with-out-str (pp/pprint req)) "<pre>")
           resp/ok
           (resp/content-type "text/html")))

stathissideris 2018-08-14T14:52:01.000200Z

this (which I suspect is equivalent to your solution) turns the body into a string first

stathissideris 2018-08-14T14:52:14.000200Z

so it doesn’t render properly

stathissideris 2018-08-14T14:53:12.000100Z

confirmed, the result looks the same as the map that you suggested

stathissideris 2018-08-14T14:53:45.000100Z

I think there is something downstream which quotes my string

stathissideris 2018-08-14T14:54:13.000100Z

(probably muuntaja?)

valtteri 2018-08-14T14:56:08.000100Z

Hmmm maybe it works if you give it a stream instead of string?

valtteri 2018-08-14T15:03:05.000100Z

I tested with reitit library which uses Muuntaja as well and returning a html string from handler with content-type text/html works as expected

valtteri 2018-08-14T15:13:31.000100Z

You could try removing :return s/Any completely and see if that has any effect

valtteri 2018-08-14T15:14:26.000100Z

How are you testing this btw?

stathissideris 2018-08-14T15:15:00.000100Z

So I tried this (don’t really know what I’m doing but it didn’t work):

(api
 {...
  :formats {"text/html" {:decode identity
                         :encode identity}}}
 ...)

stathissideris 2018-08-14T15:15:15.000100Z

for testing I’m just hitting <http://localhost:8080/api/echo>

valtteri 2018-08-14T15:16:07.000200Z

And you’re seeing extra quotes around html in http response?

stathissideris 2018-08-14T15:16:44.000100Z

yes

stathissideris 2018-08-14T15:17:16.000100Z

so it looks like it was serialized as a JSON string

stathissideris 2018-08-14T15:17:59.000100Z

it’s not a huge deal, but I’m evaluating compojure-api and I’d like to understand what it does

valtteri 2018-08-14T15:18:39.000200Z

I can warmly recommend reitit https://github.com/metosin/reitit

valtteri 2018-08-14T15:19:14.000100Z

It’s data driven compared to macros in c-api. Makes it easier to understand and use

stathissideris 2018-08-14T15:19:54.000100Z

for a second it looked like bidi and I panicked 😄

valtteri 2018-08-14T15:20:14.000100Z

However, I’ve had success with c-api in the past as well. 🙂 They’re both good libs but now my vote goes to reitit

stathissideris 2018-08-14T15:20:51.000100Z

I like the more data-driven approach…

stathissideris 2018-08-14T15:21:04.000100Z

and you do get swagger for free, right?

valtteri 2018-08-14T15:21:13.000200Z

Yes

stathissideris 2018-08-14T15:21:21.000100Z

my only qualm is -SNAPSHOT

stathissideris 2018-08-14T15:22:43.000100Z

thanks, I’ll definitely check it out

stathissideris 2018-08-14T15:23:12.000100Z

do you happen to have any inside info on when we could expect a non-snapshot release?

valtteri 2018-08-14T15:24:01.000100Z

@ikitommi is the author

stathissideris 2018-08-14T15:24:50.000200Z

@ikitommi ☝️ any info on when we could expect a non-snapshot release of reitit?

ikitommi 2018-08-14T16:53:34.000100Z

@stathissideris for c-api, if you define a :return for an endpoint, it is an "api-response" and is always returned in the negotiated format, e.g. json. This enables returning primitives like Strings & Numbers. For html, just omit the :return.

stathissideris 2018-08-14T16:54:08.000100Z

oh great, thanks!

ikitommi 2018-08-14T16:54:09.000100Z

I think better would be to have https://github.com/metosin/muuntaja/issues/76

stathissideris 2018-08-14T16:55:09.000100Z

yeah, I guess that would be good too, I think when the user of the API sets their own header it’s like they’re assuming responsibility for formatting the content too, right?

ikitommi 2018-08-14T16:55:51.000100Z

yes, think so too.

ikitommi 2018-08-14T16:56:44.000100Z

the last stable release in reitit is 0.1.3, but the docs are eagerly promoting the next SNAPSHOT. Will try to push out in the next few days. Actually, the data-driven version of Muuntaja is the last thing to do.

stathissideris 2018-08-14T16:59:08.000100Z

you know, I’ve been on a tour of liberator (a while ago), pedestal, yada, compojure etc, and I think reitit looks like the best fit for my brain. That’s why I’m so eager to use it (great work!)

ikitommi 2018-08-14T17:04:09.000100Z

Thanks! It’s for people who like to be in control of things. No magic, no macros, no default middleware / interceptors. Syntax is “hiccup for routing”. Has been fun trying to make it as fast as possible.

stathissideris 2018-08-14T17:10:10.000100Z

yes exactly, the “magic” is my main problem with the rest of the solutions out there… so about the maturity level of reitit, have you used it production yet? would you recommend it for such use? (I will not consider your reply legally binding 😄 )