oliyh / martian

The HTTP abstraction library for Clojure/script, supporting OpenAPI, Swagger, Schema, re-frame and more
MIT License
529 stars 44 forks source link

Error from simple spec file. #161

Closed michaelwhitford closed 1 year ago

michaelwhitford commented 1 year ago

I have hit an error that I cannot seem to trace back. I have built a very simple scratch project that shows the error using an open api that I use for testing (https://swapi.dev).

The error I see when trying to get a response from martian for the api:

openapi.openapi=> (martian/response-for swapi :planets)
Execution error (ClassCastException) at cheshire.core/parse-string (core.clj:209).
class clojure.lang.PersistentArrayMap cannot be cast to class java.lang.String (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')

The repository where you can see the code: Removed, code added to next comment.

This exact same spec file works from the swagger editor, and it loads the response just fine, without me having to make any changes.

michaelwhitford commented 1 year ago

I got this working, using martian.clj-http/default-interceptors was the issue, I replaced that with martian.core/default-interceptors and it is now working. Inlining the working code here so I can remove the scratch repo.

(ns openapi.openapi
  "Testing openapi spec files"
  (:require [martian.core :as martian]
            [martian.yaml :as yaml]
            [martian.interceptors :as interceptors]
            [martian.clj-http :as martian-http]))

(def spec (yaml/yaml->edn (slurp "specs/swapi.yaml")))

(def custom-interceptors (concat martian/default-interceptors
                                 [interceptors/default-encode-body
                                  interceptors/default-coerce-response
                                  martian-http/perform-request]))

(def swapi (martian/bootstrap-openapi "https://swapi.dev/api"
                                      spec
                                      {:interceptors custom-interceptors}))

(comment
  (martian/explore swapi)
  (martian/request-for swapi :planets)
  (martian/response-for swapi :planets))

And the simple spec file in yaml:

openapi: 3.0.2
info:
  title: Star Wars API
  description: Custom spec for swapi.dev
  version: 0.1

servers:
  - url: https://swapi.dev/api
    description: "The Star Wars API server"

paths:
  /planets/:
    get:
      summary: Returns a list of planets
      operationId: planets
      responses:
        '200':
          description: A list of planets
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    description: "total count of planets"
                    type: integer
                    format: "int32"
oliyh commented 1 year ago

Hi,

Glad you got this working. Your solution surprises me - your interceptors are the same as those defined by martian.clj-http/interceptors, are they not?

My guess was that the return content-type was not application/json, or something along those lines, and it was trying to parse text as json.

michaelwhitford commented 1 year ago

Yeah in the minimal reproduction of the issue I didn't show that I am using more custom interceptors that add authentication headers and the insecure? parameter to the request. I was doing this, which did not work, and caused the issue:

(def custom-interceptors (concat martian-http/default-interceptors
                                 [add-insecure-param
                                  add-basic-auth-header
                                  interceptors/default-encode-body
                                  interceptors/default-coerce-response
                                  martian-http/perform-request]))
oliyh commented 1 year ago

Ah yes you have to be mindful of the order of interceptors and also ensure that perform-request is at the end of the chain.

Thanks for the closure.