ahx / openapi_first

openapi_first is a Ruby gem for request / response validation and contract-testing against an OpenAPI API description. It makes APIFirst easy and reliable.
MIT License
105 stars 12 forks source link

Binary response gets converted to an array and throws a response validation error #281

Open gobijan opened 1 month ago

gobijan commented 1 month ago

I've stumbled on this error:

My API responds with an binary stream (file attachment). In my openapi.yml the response is described like this:

...
content:
            "*/*":
              schema:
                type: string
                format: binary
...

Running my tests I have response validation enabled via the middleware:

if Rails.env.test?
  use OpenapiFirst::Middlewares::ResponseValidation,
    spec: Rails.root.join("public", "api", "v1", "openapi.yml"),
    raise_error: true
end

Now I have the problem that in the gem the private method read_body in openapi_first/middlewares/response_validation.rb is defined like this:

def read_body(body)
        return body.to_ary if body.respond_to?(:to_ary)

        result = []
        body.each { |part| result << part }
        result
end

The body is parsed into an ActionDispatch::Response::RackBody This class responds to to_ary (though the original reponse was a binary stream string)

Now I get the validation error because it is not expecting an array but a string.

Am I missing something?

Cheers Bijan

ahx commented 1 month ago

Hi. ~Can you try https://github.com/ahx/openapi_first/pull/282 to see if that fixes the problem?~ I just released 2.0.4, which should fix this. Thanks for the report.

gobijan commented 1 month ago

Hey, thx for your effort. I'll be able to check tomorrow. Will of course let you know :)

gobijan commented 3 weeks ago

I've been on a rather long break... Sorry to come back to this just now. I upgraded and am still hitting this error:

Api::V1::ImapProvidersControllerTest#test_should_show_attachment:
RuntimeError: Neutered Exception OpenapiFirst::ResponseInvalidError: Response body is invalid: value at root is not a string
    test/controllers/api/v1/attachments_controller_test.rb:19:in `block in <class:ImapProvidersControllerTest>'

Am I missing something obvious? 🤔

For context:

This is my test

  test "should show attachment" do
    get api_v1_provider_message_attachment_url(@provider, @message_with_attachment_id, @attachment_id),
      headers: auth_header(@api_key),
      as: :json
    assert_response :success
  end

And this is the openapi.yml spec part:


/api/v1/providers/{provider_id}/messages/{message_id}/attachments/{attachment_id}:
    get:
      summary: Fetch an attached file
      tags:
        - Messages
      description: |-
        Fetch an attached file from a Message.
                           This endpoint will return the file as a binary stream.
                           You will use the download links in the Message response
                           to determine which file to download.
      operationId: messages_attachments_show
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/provider_id"
        - $ref: "#/components/parameters/message_id"
        - $ref: "#/components/parameters/attachment_id"
        - $ref: "#/components/parameters/mailbox"
      responses:
        "200":
          description: successful
          headers:
            Content-Disposition:
              schema:
                type: string
              description: Content-Disposition Header Detail
            Content-Transfer-Encoding:
              schema:
                type: string
              description: Content-Transfer-Encoding Header Detail
          content:
            "*/*":
              schema:
                type: string
                format: binary
        "400":
          description: bad_request
          content:
            application/vnd.api+json:
              schema:
                $ref: "#/components/schemas/jsonApiErrors"
        "401":
          description: unauthorized
        "403":
          description: forbidden
        "404":
          description: not_found
          content:
            application/vnd.api+json:
              schema:
                $ref: "#/components/schemas/jsonApiErrors"
        "422":
          description: unprocessable entity
          content:
            application/vnd.api+json:
              schema:
                $ref: "#/components/schemas/jsonApiErrors"
ahx commented 3 weeks ago

Hi. Thanks for taking a look at this again. I think I should try to reproduce this in a real Rails app. I have started to put something together based on based on the train-travel API project. I will try to add a second API description to the example Rails app to reproduce this issue. I will probably try to do that at the end of this week.

What does "Neutered Exception" even mean?

gobijan commented 3 weeks ago

Well... Basically that it doesn't crash and instead gets somehow handled 👯 :D Let me know if I can be of help debugging.

ahx commented 1 week ago

Well, two weeks have passed and I did not get to work on this. Sorry. If you have time to compile an example app with a failing rspec request test, that would be very helpful.