specmatic icon indicating copy to clipboard operation
specmatic copied to clipboard

Specmatic Stub handling of binary responses (MimeType & Content)

Open aeoncl opened this issue 1 year ago • 2 comments

Description I have an API that has application/pdf as return type for one of the endpoints. Currently, if i generate a stub from an interaction, i have no way to load bytes in the response (using externalValue). I can only type an inline string.

This has two effects:

  • first i cannot correctly mock this interaction, because the calling app will need valid bytes to be sent back.
  • second, the mime type sent back is incorrect, Specmatic looks at the pattern from the response example to generate the response. This means if Specmatic parses the example body as a StringValue, it will set the return MimeType to text/plain, which isn't compatible with the contract.

There are multiple behaviors that adds up to create the mimetype behaviour:

  • Specmatic creates the example patterns from the example body, and doesn't look at the contract at all. This is why what is a "String format Binary" in the contract turns in a simple String in the example, generating the incorrect mimeType
  • Specmatic doesn't use the contract at all for responses MimeTypes, just the Value class of the body

Steps to reproduce

1. Here's what you need, there is a contract, an interraction & 1 pdf files:

Contract:

openapi: 3.0.3
info:
  contact:
    url: http://balbala
  title: GigaUpload Service
  description: Service to upload things and break Specmatic :( sorry guyz
  version: 1.0.0
servers:
  - url: "http://{hostname}:{port}/{basePath}/"
    variables:
      hostname:
        default: localhost
      port:
        default: "8080"
      basePath:
        default: rest
  - url: "http://localhost:9000"
tags:
  - name: UploadStuff
paths:
  "/uploadstuff":
    post:
      tags:
        - UploadStuff
      operationId: UploadStuffToServer
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                uploadRequest:
                  $ref: "#/components/schemas/UploadRequest"
                pdfFiles:
                  type: array
                  items:
                    type: string
                    format: binary
              required:
                - uploadRequest
                - pdfFiles
            encoding:
              UploadRequest:
                contentType: application/json
              pdfFiles:
                contentType: application/pdf
      responses:
        "200":
          description: "The document has been sent successfully send and...we're sending it back, because reasons. You're not my dad."
          content:
            application/pdf:
              schema:
                type: string
                format: binary
components:
  schemas:
    UploadRequest:
      type: object
      title: UploadRequest
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        coolNameYourFriendsGiveYou:
          type: string
          minLength: 1
          maxLength: 255
      required:
        - name
        - coolNameYourFriendsGiveYou

Interraction:

{
  "http-request": {
    "method": "POST",
    "path": "/uploadstuff",
    "multipart-formdata": [
      {
        "name": "pdfFiles",
        "file": "(string)",
        "filename": "%%changeme%%\\test.pdf",
        "contentType": "application/pdf"
      },
      {
        "name": "uploadRequest",
        "content": {
          "name": "Chris",
          "coolNameYourFriendsGiveYou": "C-Dawg"
        },
        "contentType": "application/json"
      }
    ]
  },
  "http-response": {
    "status": 200,
    "body": "feafaefaf"
  }
}

PdfFile: test.pdf

Don't forget to update the file path in the interaction

2. Run specmatic in stub mode with the interaction loaded

3. Make a POST request to localhost:9000/uploadstuff of type multipart/form-data

The parts are:

  • uploadRequest of type json:
{
    "coolNameYourFriendsGiveYou": "C-Dawg",
    "name": "Chris"
}
  • pdfFiles: contains your test.pdf

image

Expected behavior I Expect the response to be of mimetype application/pdf like it's written in the contract. I Expect valid pdf bytes to be the body of the response

Screenshots Response mimetype is text/plain: image

System Information:

  • OS & version: Windows 11
  • Browser & version: Insomnia 2023
  • Specmatic version: 0.77
  • JDK version: OpenJDK 17

Additional context There are multiple ways we could solve these problems:

  • Add support for externalValue in the responses
  • At the OpenAPI parsing time (in OpenAPISpecification.kt) set the mimetype to the HttpResponsePattern from the contract and drop using the body value as MimeType source

aeoncl avatar Sep 27 '23 09:09 aeoncl

This was hard to convey, let me know if something is not clear ! I will investigate the possible fixes that i talk about in the additional context. Please tell me what you think about them, how would you go about fixing this / adding this feature?

aeoncl avatar Sep 27 '23 09:09 aeoncl

@aeoncl really appreciate the effort you have put into this detailed explanation. We'll read through it and get back to you.

joelrosario avatar Sep 28 '23 09:09 joelrosario