cyclosproject / ng-openapi-gen

An OpenAPI 3.0 codegen for Angular
MIT License
397 stars 132 forks source link

Multiple values for Accept endpoint are not mapped #225

Closed drenda closed 2 years ago

drenda commented 2 years ago

I've an endpoint like this, used to download a PDF report. This endpoint could thrown an error that is returned as a JSON model. In fact in the declaration it's set we can receive either a PDF or a JSON.

    "/api/v1/appointments/pdf": {
      "get": {
        "tags": [
          "Appointment"
        ],
        "summary": "Download a PDF of the appointments identified by the given RSQL query",
        "operationId": "downloadPdfAppointments",
        "parameters": [
          {
            "name": "TimeZone",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "query",
            "in": "query",
            "description": "The RSQL query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "schema": {
              "type": "string",
              "enum": [
                "application/pdf,application/json"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "If the resource has not been found",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Not authorized",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },

But the implementation is like this:

 /**
   * Download a PDF of the appointments identified by the given RSQL query.
   *
   *
   *
   * This method provides access to the full `HttpResponse`, allowing access to response headers.
   * To access only the response body, use `downloadPdfAppointments$Pdf()` instead.
   *
   * This method doesn't expect any request body.
   */
  downloadPdfAppointments$Pdf$Response(params: {
    TimeZone: string;

    /**
     * The RSQL query
     */
    query?: string;
    Accept?: 'application/pdf,application/json';
  }): Observable<StrictHttpResponse<Blob>> {

    const rb = new RequestBuilder(this.rootUrl, AppointmentService.DownloadPdfAppointmentsPath, 'get');
    if (params) {
      rb.header('TimeZone', params.TimeZone, {});
      rb.query('query', params.query, {});
      rb.header('Accept', params.Accept, {});
    }

    return this.http.request(rb.build({
      responseType: 'blob',
      accept: 'application/pdf'
    })).pipe(
      filter((r: any) => r instanceof HttpResponse),
      map((r: HttpResponse<any>) => {
        return r as StrictHttpResponse<Blob>;
      })
    );
  }

where you can see the http.request accept just an application/pdf, so when there is an excpetion, the server is not able to serve the client because the accept doesn't permit to reply back with a JSON content.

Is this a bug or a know limit of the library? Is there any workaround for this that doesn't imply to modify manually the generated service?

Thanks

luisfpg commented 2 years ago

This is a known limitation in the Angular HttpClient. See #166. You cannot specify to accept a blob for status code 200 and json for others, for example. So you should parse the blob yourself in this case in order to extract the corresponding object.

YassinHajaj commented 1 year ago

@luisfpg I'm not sure I understand.

In the case of the 200, depending on the Accept header, the server should be able to return either a PDF, or a JSON right ? (let's put exceptions aside for now, just focus on a valid request/response with 200 status).

Shouldn't it be able to generate two methods in the client. One for the PDF and another one for the JSON ?

luisfpg commented 1 year ago

In this case, of having 2 different content types, 2 different methods will be generated. The OP was about different response types (json vs blob).