hey-api / openapi-ts

✨ Turn your OpenAPI specification into a beautiful TypeScript client
https://heyapi.vercel.app
Other
1.02k stars 85 forks source link

Option to ignore headers #1020

Open juliusfriedman opened 1 week ago

juliusfriedman commented 1 week ago

Description

Just like in openapi-generator for java it would be nice if there was an option to ignore header parameters e.g.

https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/spring.md

The reason for this is primarily that we have a headerResolver which add the required headers but when using the client generated from this package the header cannot be easily changed.

The problem lies here: https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-typescript/src/transform/header-object.ts

It would be beneficial if there was an option to ignore headers or have options similar to the open api generator cited above.

Please let me know if further information is required.

mrlubos commented 1 week ago

Hey @juliusfriedman, I've heard this come up a few times. Are you able to share a (pseudo) code that demonstrates how you'd like to be able to use the generated client that you can't do today?

juliusfriedman commented 1 week ago

I'm away from keyboard right now, but the root of the issue is that I don't want the header to be a parameter to the method that is generated.

I would only like the header to appear for use on the swagger/open api interface.

I would like to use a headerResolver which depends on the method being fetched which header will be sent.

This implies I can't have the header name hard-coded in the client which is generated nor can I can have the headers become parameters of the method.

I understand for some people it's good BUT if there was an option to ignore headers it would be better.

Right now I work around this by generating 2 different openApi documents... one with the header as a parameter and the other without.

Hopefully this helps, I will try to add more code when I get back to my pc.

Thanks

juliusfriedman commented 1 week ago

service.someMethod() is generated when the header is ignored lets say and service.someMethod(requiredParamter) is generated when there is a header, what's even worse is that when the header is inserted its based on the time the client was generated so if the header changes at runtime then this will render the client useless.

Please let me know if I can explain any better.

mrlubos commented 1 week ago

Can you share a spec that results in this type of useless client?

juliusfriedman commented 1 week ago

Here is the spec of the method

"/api/ForgotPassword/SendEmail/{cultureName}": {
      "post": {
        "tags": [
          "ForgotPassword"
        ],
        "operationId": "ForgotPassword_SendEmail",
        "parameters": [
          {
            "name": "cultureName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "nullable": true
            },
            "x-position": 1
          },
          {
            "type": "string",
            "name": "__RequestVerificationToken",
            "in": "header",
            "required": true,
            "description": "Anti Forgery Token",
            "schema": {
              "type": "string",
              "format": "string"
            }
          }
        ],
        "requestBody": {
          "x-name": "request",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendForgotPasswordEmailRequest"
              }
            }
          },
          "required": true,
          "x-position": 2
        },
        "responses": {
          "200": {
            "description": ""
          }
        }
      }
    },

Here is a method from a generated client which becomes useless

public static forgotPasswordSendEmail({
    cultureName,
    requestVerificationToken,
    requestBody,
  }: {
    cultureName: string | null,
    /**
     * Anti Forgery Token
     */
    requestVerificationToken: string,
    requestBody: SendForgotPasswordEmailRequest,
  }): CancelablePromise<any> {
    return __request(OpenAPI, {
      method: 'POST',
      url: '/api/ForgotPassword/SendEmail/{cultureName}',
      path: {
        'cultureName': cultureName,
      },
      headers: {
        '__RequestVerificationToken': requestVerificationToken,
      },
      body: requestBody,
      mediaType: 'application/json',
    });
  }

This works until the header name changes for the Anti Forgery Token i.e. -> __RequestVerificationToken can change arbitrarily with user configuration and we would have to regenerate the client every time where as with a headerResolver this doesn't happen and the header name is dynamically assigned along with the token.

mrlubos commented 1 week ago

And how do you know what's the new value of that header?

juliusfriedman commented 1 week ago

It depends on the tech stack in use but at a high level, during runtime we could resolve the header from a separate GET request dynamically, this GET request returns the header name and token and we use it on subsequent requests and is why the headerResolver approach works for us.

mrlubos commented 1 week ago

Can you explain why is __RequestVerificationToken required if it is NOT the header you're expecting? Is this an ASP.NET application? I'm trying to find a documentation that describes your behaviour. Right now I don't understand why the OpenAPI spec would define this header as required if you know it will not exist?

juliusfriedman commented 1 week ago

Yes and Yes,

We add it as a required header for the swagger operations so that users can use the swagger interface to test the API without the UI portion of the application.

The header will exist but maybe named something differently by the time the application is running (which is after the client is compiled)

The way we have worked around this is with a headerResolver which generates the call to get the required headerName and token when making requests.

The way we ended up using the client was that we needed to generate a version of the openApi spec WITHTOUT the required parameter which is why I made this issue.

If there was a way to ignore the header it would be the same as me omitting it from the API Spec but only for the open api json.

Hopefully that makes sense :)

mrlubos commented 1 week ago

Okay, do you think an option along the lines of shouldProcessHeader() would work? Would you be looking to use the package in a programmatic way through Node.js?

juliusfriedman commented 1 week ago

Yes, an option like that should definitely work, Just as a FYI we are actually using https://github.com/ferdikoomen/openapi-typescript-codegen which I believe is the prior version of this library.

I went to update to this library but then when I saw this library has the same issue I stopped what I was doing and instead created an issue.

Once we have the ability to skip the header either by command line or code I can experiment and see where its most useful, I believe the command line would be the most useful as of right now.

Thank you and please let me know if you need anything else!