Skip to content

[BUG] scala-sttp doesn't appear to be compatible with sttp 2.2.3 #7181

Open
@resinten

Description

@resinten

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?
Description

As an educational effort to learn the openapi spec, I was working on writing one by hand. I need to use scala sttp version 2.2.3 because the sttp ZIO backend for the default 2.2.0 isn't compatible with the 1.0.0 release of ZIO as far as I can tell. However, there's a breaking change in the ResponseError type in sttp between 2.2.0 & 2.2.3. Previously it was a class with a body: String field. In 2.2.3 it does not have any fields but the two subclasses have the body field.

Generated ApiInvoker.scala (note: ex.body - ex is a ResponseError type)

    def result(implicit backend: SttpBackend[R, Nothing, Nothing]): R[T] = {
      val responseT = request.send()
      val ME: MonadError[R] = backend.responseMonad
      ME.flatMap(responseT) {
        response =>
          response.body match {
            case Left(ex) => ME.error[T](new HttpException(response.code, response.statusText, ex.body))
            case Right(value) => ME.unit(value)
          }
      }
    }

You can see the differences in the ResponseError type between the two versions here:

In sttp 2.2.0

sealed abstract class ResponseError[+T] extends Exception {
  def body: String
}
case class HttpError(body: String) extends ResponseError[Nothing]
case class DeserializationError[T](body: String, error: T) extends ResponseError[T]

In sttp 2.2.3

sealed abstract class ResponseError[+T](error: String) extends Exception(error)
case class HttpError(body: String, statusCode: StatusCode)
    extends ResponseError[Nothing](s"statusCode: $statusCode, response: $body")
case class DeserializationError[T: ShowError](body: String, error: T)
    extends ResponseError[T](implicitly[ShowError[T]].show(error))

I originally tried this with 4.3.1. It was suggested that I tried 5.0.0-beta to see if this was resolved. I tried it and specified the sttpClientVersion=2.2.3 property (see below) and noticed that the resulting build.sbt file still shows the dependency version for sttp as 2.2.0. I played around more by setting the mainPackage property and saw that the output package didn't change. This makes me wonder if the scala-sttp generator is properly reading the config properties.

openapi-generator version

Occurs with both 4.3.1 and 5.0.0-beta

OpenAPI declaration file content or url
# openapi.yaml
openapi: "3.0.3"
info:
  title: Twitch API Test
  version: "1.0"
servers:
  - url: https://api.twitch.tv/
    description: Primary
paths:
  /oauth2/token:
    servers:
      - url: https://id.twitch.tv/
        description: ID Server
    post:
      operationId: authenticate
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema: { $ref: "#/components/schemas/AuthenticateResponse" }
      parameters:
        - name: client_id
          in: query
          required: true
          schema: { type: string }
        - name: client_secret
          in: query
          required: true
          schema: { type: string }
        - name: grant_type
          in: query
          required: true
          schema:
            type: string
            default: client_credentials
        - name: scope
          in: query
          required: false
          schema: { type: string }
  /helix/games:
    get:
      operationId: fetchGames
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FetchGamesResponse" }
      parameters:
        - name: id
          in: query
          required: true
          schema: { type: string }
  /helix/streams:
    get:
      operationId: fetchStreams
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FetchStreamsResponse" }
      parameters:
        - name: after
          in: query
          schema: { type:  string }
        - name: before
          in: query
          schema: { type: string }
        - name: first
          in: query
          schema:
            type: integer
            format: int33
        - name: game_id
          in: query
          schema: { type: string }
        - name: language
          in: query
          schema: { type: string }
        - name: user_id
          in: query
          schema: { type: string }
        - name: user_login
          in: query
          schema: { type: string }
      security:
        - twitch: []
components:
  securitySchemes:
    twitch:
      type: http
      scheme: bearer
  schemas:
    AuthenticateResponse:
      type: object
      required:
        - access_token
        - expires_in
        - scope
        - token_type
      properties:
        access_token: { type: string }
        expires_in:
          type: integer
          format: int64
        scope: { type: string }
        token_type: { type: string }
    FetchGamesResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            type: object
            required:
              - box_art_url
              - id
              - name
            properties:
              box_art_url: { type: string }
              id: { type: string }
              name: { type: string }
        pagination: { $ref: "#/components/schemas/Pagination" }
    FetchStreamsResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            type: object
            required:
              - id
              - user_id
              - user_name
              - game_id
              - type
              - title
              - viewer_count
              - started_at
              - language
              - thumbail_url
            properties:
              id: { type: string }
              user_id: { type: string }
              user_name: { type: string }
              game_id: { type: string }
              type: { type: string }
              title: { type: string }
              viewer_count: { type: string }
              started_at:
                type: string
                format: date-time
              language: { type: string }
              thumbnail_url: { type: string }
    Pagination:
      type: object
      properties:
        cursor: { type: string }
Generation Details
openapi-generator-cli generate \
  -i openapi.yaml \
  -g scala-sttp \
  --additional-properties sttpClientVersion=2.2.3,mainPackage=tv.twitch.api \
  -o twitch-api
Steps to reproduce

It was sufficient for me to just run the above command with the provided yaml file. After running, I checked the generated build.sbt and saw that it still references sttp 2.2.0 instead of 2.2.3. There is a breaking change between these two versions - the ResponseError type is subclassed in 2.2.3 and the body field does not exist on the root ResponseError type, but is referenced in the generated ApiInvoker.scala file. It also appears to ignore the mainPackage property as set above and still uses the default value.

Suggest a fix

Not sure what the fix is as I don't know how deep the issue goes. I'm hoping that it's just a simple fix of not reading the correct config keys that are described in the generator docs. It may also require some work to make sure that the generator is compatible with newer versions of the sttp client.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions