Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allOf / oneOf issue with multiple content types in response or requests objects #243

Open
1 task
jkone27 opened this issue May 14, 2024 · 1 comment
Open
1 task

Comments

@jkone27
Copy link
Contributor

jkone27 commented May 14, 2024

Description

when multiple content types are generated or when anyways oneOf / allOf is used in response type, swagger provider results in 'obj' response type, or request type also (e.g. with json , text, and other formts, does not filter only application/json but breaks and returns object)

Repro steps

Please provide the steps required to reproduce the problem

  1. Type provider type definition with parameters
type ChangeApi = OpenApiClientProvider<"test.yaml">
  1. Sample schema or relevant schema part
openapi: 3.0.1
info:
  title: Acme.TestServiceBlaBla.WebService
  version: v1
paths:
  '/orders/{orderNumber}/change-eligibility':
    get:
      tags:
        - EligibilityChange
      summary: Gets eligibilities of products for the order by a orderNumber.
      parameters:
        - name: orderNumber
          in: path
          description: The order number.
          required: true
          schema:
            type: string
        - name: enableCacmeSelfService
          in: query
          description: ''
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Success
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/EligibilityResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/EligibilityResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/EligibilityResponse'
        '404':
          description: Not Found
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
        '500':
          description: Server Error
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
        '409':
          description: Conflict
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/EligibilityErrorResponse'
  '/flights/exchange':
    post:
      tags:
        - FlightExchange
      requestBody:
        content:
          application/json-patch+json:
            schema:
              allOf:
                - $ref: '#/components/schemas/FlightExchangeRequest'
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/FlightExchangeRequest'
          text/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/FlightExchangeRequest'
          application/*+json:
            schema:
              allOf:
                - $ref: '#/components/schemas/FlightExchangeRequest'
      responses:
        '200':
          description: Success
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/FlightExchangeResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeResponse'
        '500':
          description: Server Error
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
  '/flights/exchange/{exchangeRequestId}':
    get:
      tags:
        - FlightExchange
      parameters:
        - name: exchangeRequestId
          in: path
          required: true
          description: for Cacme this will be the key identifier of an offer
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/FlightExchangeDetailResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeDetailResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeDetailResponse'
        '404':
          description: Not Found
        '500':
          description: Server Error
  '/flights/exchange/{exchangeRequestId}/status':
    get:
      tags:
        - FlightExchange
      parameters:
        - name: exchangeRequestId
          in: path
          required: true
          description: for Cacme this will be the key identifier of an offer
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/FlightExchangeStatusResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeStatusResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/FlightExchangeStatusResponse'
        '404':
          description: Not Found
        '500':
          description: Server Error
  '/flights/exchange/{exchangeRequestId}/submit':
    post:
      tags:
        - FlightExchange
      parameters:
        - name: exchangeRequestId
          in: path
          required: true
          description: for Cacme this will be the key identifier of an offer
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlightSubmitExchangeResponse'
  '/orders/{orderNumber}/flights/{productId}/reshop':
    post:
      tags:
        - Reshop
      parameters:
        - name: orderNumber
          in: path
          required: true
          schema:
            type: string
        - name: productId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json-patch+json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ReshopRequest'
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ReshopRequest'
          text/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ReshopRequest'
          application/*+json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ReshopRequest'
      responses:
        '200':
          description: Success
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ReshopResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ReshopResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ReshopResponse'
        '204':
          description: No Content
        '500':
          description: Server Error
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ReshopErrorResponse'
components:
  schemas:
    FlightSubmitExchangeResponse:
      type: object
      properties:
        paymentIdentifier:
          type: string
          nullable: false
    FlightExchangeStatusResponse:
      type: object
      properties:
        status:
          type: string
          description: confirmed | timeout | error
          nullable: false
    AmountWithCurrency:
      type: object
      properties:
        value:
          type: number
          format: double
        currencyCode:
          type: string
          nullable: true
      additionalProperties: false
    Baggage:
      type: object
      properties:
        quantity:
          type: integer
          format: int32
          nullable: true
        unit:
          type: string
          nullable: true
        value:
          type: integer
          format: int32
          nullable: true
      additionalProperties: false
    CancellationDetails:
      type: object
      properties:
        automatedCancellation:
          type: boolean
        cancellable:
          type: boolean
        reason:
          type: string
          nullable: true
        feeDetails:
          allOf:
            - $ref: '#/components/schemas/FeeDetails'
          nullable: true
        refundDetails:
          allOf:
            - $ref: '#/components/schemas/RefundDetails'
          nullable: true
        nonRefundableDetails:
          allOf:
            - $ref: '#/components/schemas/NonRefundableDetails'
          nullable: true
        totalPaid:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        revisedTotal:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        totalRefund:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
      additionalProperties: false
    ChangeDetail:
      required:
        - exchangeType
        - value
      type: object
      properties:
        exchangeType:
          minLength: 1
          type: string
        value:
          minLength: 1
          type: string
      additionalProperties: false
    ChangeDetails:
      type: object
      properties:
        automatedChange:
          type: boolean
        changeable:
          type: boolean
        reason:
          type: string
          nullable: true
        validity:
          allOf:
            - $ref: '#/components/schemas/ChangeValidity'
          nullable: true
      additionalProperties: false
    ChangeValidity:
      type: object
      properties:
        dates:
          allOf:
            - $ref: '#/components/schemas/ChangeValidityDates'
          nullable: true
      additionalProperties: false
    ChangeValidityDates:
      type: object
      properties:
        legs:
          type: array
          items:
            $ref: '#/components/schemas/ChangeValidityDatesLeg'
          nullable: true
      additionalProperties: false
    ChangeValidityDatesLeg:
      type: object
      properties:
        id:
          type: integer
          format: int32
        minDepartureDate:
          type: string
          nullable: true
        maxDepartureDate:
          type: string
          nullable: true
      additionalProperties: false
    Commission:
      type: object
      properties:
        amount:
          type: number
          format: double
        type:
          type: string
          nullable: true
      additionalProperties: false
    EligibilityErrorResponse:
      type: object
      properties:
        details:
          nullable: true
        errorCode:
          type: string
          nullable: true
        errorInfo:
          type: string
          nullable: true
      additionalProperties: false
    EligibilityItemResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
        flowType:
          allOf:
            - $ref: '#/components/schemas/FlowType'
        voidWindowExpiry:
          type: string
          format: date-time
          nullable: true
        cancellationDetails:
          allOf:
            - $ref: '#/components/schemas/CancellationDetails'
          nullable: true
        changeDetails:
          allOf:
            - $ref: '#/components/schemas/ChangeDetails'
          nullable: true
        isWithinVoidWindow:
          type: boolean
      additionalProperties: false
    EligibilityResponse:
      type: object
      properties:
        eligibilityList:
          type: array
          items:
            $ref: '#/components/schemas/EligibilityItemResponse'
          nullable: true
      additionalProperties: false
    ExchangeItineraryFare:
      type: object
      properties:
        fareId:
          type: string
          nullable: true
        changeFees:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        fareGroup:
          allOf:
            - $ref: '#/components/schemas/FareGroup'
          nullable: true
        legs:
          type: array
          items:
            $ref: '#/components/schemas/Leg'
          nullable: true
        isFreeChange:
          type: boolean
      additionalProperties: false
    ExchangeLeg:
      required:
        - changeDetails
        - legId
      type: object
      properties:
        legId:
          type: integer
          format: int32
        changeDetails:
          type: array
          items:
            $ref: '#/components/schemas/ChangeDetail'
      additionalProperties: false
    FareGroup:
      type: object
      properties:
        brandName:
          type: string
          nullable: true
        currency:
          type: string
          nullable: true
        fareType:
          type: string
          nullable: true
        id:
          type: integer
          format: int32
          nullable: true
        marketingCarrier:
          type: string
          nullable: true
        passengerGroups:
          type: array
          items:
            $ref: '#/components/schemas/PassengerGroup'
          nullable: true
        passiveSegmentIds:
          type: array
          items:
            type: integer
            format: int32
          nullable: true
        pointOfSale:
          type: string
          nullable: true
        provider:
          type: string
          nullable: true
        providerAccount:
          type: string
          nullable: true
        segmentIds:
          type: array
          items:
            type: integer
            format: int32
          nullable: true
        validatingCarrier:
          type: string
          nullable: true
      additionalProperties: false
    FareInfo:
      type: object
      properties:
        baggage:
          allOf:
            - $ref: '#/components/schemas/Baggage'
          nullable: true
          readOnly: true
        baggageAllowance:
          type: string
          nullable: true
          readOnly: true
        segmentId:
          type: integer
          format: int32
          nullable: true
          readOnly: true
      additionalProperties: false
    FarePrice:
      type: object
      properties:
        baseFare:
          type: number
          format: double
        tax:
          type: number
          format: double
        taxElements:
          type: array
          items:
            $ref: '#/components/schemas/TaxElement'
          nullable: true
          readOnly: true
      additionalProperties: false
    Fee:
      type: object
      properties:
        amount:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        type:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
      additionalProperties: false
    FeeDetails:
      type: object
      properties:
        fees:
          type: array
          items:
            $ref: '#/components/schemas/Fee'
          nullable: true
        feesTotal:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
      additionalProperties: false
    FlightExchangeDetailResponse:
      type: object
      properties:
        productId:
          type: string
          format: uuid
        quoteId:
          type: string
          nullable: true
        orderNumber:
          type: string
          nullable: true
        feeDetails:
          allOf:
            - $ref: '#/components/schemas/FeeDetails'
          nullable: true
        filteredFare:
          allOf:
            - $ref: '#/components/schemas/ExchangeItineraryFare'
          nullable: true
      additionalProperties: false
    FlightExchangeRequest:
      type: object
      properties:
        quoteId:
          type: string
          nullable: true
        fareId:
          type: string
          nullable: true
        productId:
          type: string
          format: uuid
        orderNumber:
          type: string
          nullable: true
        legs:
          type: array
          items:
            type: integer
            format: int32
          nullable: true
      additionalProperties: false
    FlightExchangeResponse:
      type: object
      properties:
        exchangeRequestId:
          type: string
          nullable: true
          description: for Cacme this is the key identifier of an Offer
        productId:
          type: string
          format: uuid
        orderNumber:
          type: string
          nullable: true
        feeDetails:
          allOf:
            - $ref: '#/components/schemas/FeeDetails'
          nullable: true
        orderSource:
          type: string
          nullable: true
      additionalProperties: false
    FlowType:
      enum:
        - SingleFlight
        - CowFlight
        - EtdPackage
      type: string
    Leg:
      type: object
      properties:
        id:
          type: integer
          format: int32
        legRef:
          type: integer
          format: int32
        arrivalCode:
          type: string
          nullable: true
        arrivalCountry:
          type: string
          nullable: true
        arrivalDateTime:
          type: string
          format: date-time
        arrivalRegion:
          type: string
          nullable: true
        departureCode:
          type: string
          nullable: true
        departureCountry:
          type: string
          nullable: true
        departureDateTime:
          type: string
          format: date-time
        departureRegion:
          type: string
          nullable: true
        distanceKms:
          type: integer
          format: int32
          nullable: true
        durationMins:
          type: integer
          format: int32
          nullable: true
        marketingCarrier:
          type: string
          nullable: true
        segments:
          type: array
          items:
            $ref: '#/components/schemas/Segment'
          nullable: true
        cabinClassCompatibility:
          type: boolean
      additionalProperties: false
    NonRefundableDetails:
      type: object
      properties:
        fees:
          type: array
          items:
            $ref: '#/components/schemas/NonRefundableFee'
          nullable: true
        nonRefundableFeesTotal:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
      additionalProperties: false
    NonRefundableFee:
      type: object
      properties:
        amount:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        type:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
      additionalProperties: false
    PassengerFare:
      type: object
      properties:
        newFarePrice:
          allOf:
            - $ref: '#/components/schemas/FarePrice'
          nullable: true
        fareDifference:
          allOf:
            - $ref: '#/components/schemas/FarePrice'
          nullable: true
        id:
          type: integer
          format: int32
        numberOfPassengers:
          type: integer
          format: int32
        passengerType:
          type: string
          nullable: true
        passengerTypeCode:
          type: string
          nullable: true
      additionalProperties: false
    PassengerGroup:
      type: object
      properties:
        commission:
          allOf:
            - $ref: '#/components/schemas/Commission'
          nullable: true
        fareInfos:
          type: array
          items:
            $ref: '#/components/schemas/FareInfo'
          nullable: true
        passengerFare:
          allOf:
            - $ref: '#/components/schemas/PassengerFare'
          nullable: true
        passengerIds:
          type: array
          items:
            type: integer
            format: int32
          nullable: true
      additionalProperties: false
    Refund:
      type: object
      properties:
        amount:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
        type:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
      additionalProperties: false
    RefundDetails:
      type: object
      properties:
        refunds:
          type: array
          items:
            $ref: '#/components/schemas/Refund'
          nullable: true
        refundsTotal:
          allOf:
            - $ref: '#/components/schemas/AmountWithCurrency'
          nullable: true
      additionalProperties: false
    ReshopErrorResponse:
      type: object
      properties:
        details:
          nullable: true
        errorCode:
          type: string
          nullable: true
        errorInfo:
          type: string
          nullable: true
      additionalProperties: false
    ReshopRequest:
      required:
        - exchangeLegs
        - orderNumber
        - productId
      type: object
      properties:
        orderNumber:
          minLength: 1
          type: string
        productId:
          type: string
          format: uuid
        exchangeLegs:
          type: array
          items:
            $ref: '#/components/schemas/ExchangeLeg'
      additionalProperties: false
    ReshopResponse:
      type: object
      properties:
        orderNumber:
          type: string
          nullable: true
        productId:
          type: string
          format: uuid
        quoteId:
          type: string
          nullable: true
        exchangeItineraryFares:
          type: array
          items:
            $ref: '#/components/schemas/ExchangeItineraryFare'
          nullable: true
      additionalProperties: false
    Segment:
      type: object
      properties:
        airlineLocator:
          type: string
          nullable: true
        arrivalCode:
          type: string
          nullable: true
        arrivalCountry:
          type: string
          nullable: true
        arrivalDateTime:
          type: string
          format: date-time
        arrivalRegion:
          type: string
          nullable: true
        arrivalTerminal:
          type: string
          nullable: true
        availabilitySource:
          type: string
          nullable: true
        bookingClass:
          type: string
          nullable: true
        cabinClassType:
          type: string
          nullable: true
        codeShareDetail:
          type: string
          nullable: true
        connection:
          type: boolean
          nullable: true
        departureCode:
          type: string
          nullable: true
        departureCountry:
          type: string
          nullable: true
        departureDateTime:
          type: string
          format: date-time
        departureRegion:
          type: string
          nullable: true
        departureTerminal:
          type: string
          nullable: true
        distanceKms:
          type: integer
          format: int32
          nullable: true
        durationMins:
          type: integer
          format: int32
          nullable: true
        equipment:
          type: string
          nullable: true
        flightNumber:
          type: string
          nullable: true
        id:
          type: integer
          format: int32
        isFlown:
          type: boolean
          nullable: true
        marketingCarrier:
          type: string
          nullable: true
        operatingCarrier:
          type: string
          nullable: true
        seatsAvailable:
          type: integer
          format: int32
          nullable: true
        statusCode:
          type: string
          nullable: true
        stops:
          type: integer
          format: int32
          nullable: true
        technicalStops:
          type: array
          items:
            $ref: '#/components/schemas/TechnicalStop'
          nullable: true
      additionalProperties: false
    TaxElement:
      type: object
      properties:
        amount:
          type: number
          format: double
        code:
          type: string
          nullable: true
      additionalProperties: false
    TechnicalStop:
      type: object
      properties:
        airport:
          type: string
          nullable: true
        arrivalDateTime:
          type: string
          format: date-time
        departureDateTime:
          type: string
          format: date-time
        durationMins:
          type: integer
          format: int32
          nullable: true
      additionalProperties: false
  securitySchemes:
    Bearer:
      type: apiKey
      description: 'JWT Authorization header using the Bearer scheme. Example: "Authorization: Bearer {token}"'
      name: Authorization
      in: header
security:
  - Bearer: [ ]

https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/

Expected behavior

instead of object, swagger provider can pick the first matching json specifications for returned responses, or for accepted requests

fallback strategies instead of 'object' ?

Actual behavior

obj replaces the type in the signature of the method, either response type or parameter type

Known workarounds

build the object externally and pass it as object, if serializer handles it, it should still work

Affected Type Providers

  • SwaggerClientProvider (didnt test... not interested)
  • [ X] OpenApiClientProvider

Related information

  • Operating system
  • Branch
  • .NET Runtime, CoreCLR or Mono Version
  • Performance information, links to performance testing scripts
@jkone27
Copy link
Contributor Author

jkone27 commented May 14, 2024

with this same schema also has trouble processing Some integer as option, so i am testing now with PreferNullable = true, to see if that helps. cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants