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

OpenAPI 3.1.0 support: changing server url #2967

Closed
transfluxus opened this issue May 22, 2023 · 11 comments
Closed

OpenAPI 3.1.0 support: changing server url #2967

transfluxus opened this issue May 22, 2023 · 11 comments

Comments

@transfluxus
Copy link

Q&A (please complete the following information)

  • OS: Ubuntu
  • Environment: firefox 113.0.1, node 18
  • Method of installation: npm
  • Swagger-Client version: 3.19.7
  • Swagger/OpenAPI version: OpenAPI 3.0

How can we help?

I have noticed a difference in behaviour between version 3.16 and 3.19, and I am trying to replicate the result in 3.19.
Basically I want to change the server url dynamically.
Before I would just go into the swaggerClient.spec.servers and change the array, but now this does not seem to have any effect on the following requests. How can I achieve changing the server url in the latest version?
thanks!

@char0n
Copy link
Member

char0n commented May 26, 2023

Hi @transfluxus,

Unfortunately the info you provided about the issue is very limited and not really actionable.
I would need a full working example code + fixture where I can see what is happening. Would you be able to provide one?

@transfluxus
Copy link
Author

Hi char0n,

thanks for getting back.
I just put together this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://unpkg.com/swagger-client"></script>
</head>
<body>

<script async>
    async function make_client() {
        const response = await fetch("localcontextshub_openapi.json")
        const spec = await response.json()
        return await SwaggerClient("http://localhost:9999/localcontextshub_openapi.json", {spec})
    }

    let swaggerClient;
    make_client().then(client => {
        swaggerClient = client
        console.log(swaggerClient)
        swaggerClient.execute({"operationId": "projects_list"}).then(response => {
            console.log(response)
        }, error => {
            console.error(error)
        })

        swaggerClient.spec.servers = [
            {
                "url": "https://localcontextshub.org"
            }
        ]

        swaggerClient.execute({"operationId": "projects_list"}).then(response => {
            console.log(response)
        }, error => {
            console.error(error)
        })
    })
</script>
</body>
</html>

which works 🥳. However in my application I am forced to use version 3.16.1
because of #2187
I am not sure, if that causes the problem, or if there is something else wrong in my app.. But I already tested a bit...
Anyway since it works, this ca be closed...

@char0n
Copy link
Member

char0n commented May 26, 2023

All right, closing.

in my application I am forced to use version 3.16.1 because of #2187

Can you elaborate on this? What in #2187 is forcing you to use 3.16.1? I've already provided fix for the regression introduced there in v3.18.1...v3.18.2

@char0n char0n closed this as completed May 26, 2023
@transfluxus
Copy link
Author

transfluxus commented May 29, 2023

I cannot tell now, cuz when just again updating the package to 3.19.8, it does not use the right server even if the spec looks ok
https://gist.github.com/transfluxus/46e2f6035d5f2ae2f642e53fdc569b11

It is not making the request to the right url (but the local server, since the server array is empty intially)

I went into it with the debugger and in the buildRequest function the url is not set from the spec...
In that line before the path is set, the req.url is just an empty string.
req.url += pathName; // Have not yet replaced the path parameters.

debug screenshot

the problem with lodash was, ... that it was basically not imported, so "get" would be null and it would say something like "schema not found" because it was supposed to get the security schema with lodash get

@char0n
Copy link
Member

char0n commented May 29, 2023

@transfluxus thanks for more info. Does it only fail for OpenAPI 3.1.0 and work for OpenAPI 3.0.3 or it fails for both?

@transfluxus
Copy link
Author

transfluxus commented May 29, 2023

I went deeper with the debugger. It seems that this problem is also related to lodash.
in
baseUrl
in
oas3BaseUrl
that line
const servers = getIn(spec, ['paths', pathName, (method || '').toLowerCase(), 'servers']) || getIn(spec, ['paths', pathName, 'servers']) || getIn(spec, ['servers']);
results an empty array.

Interesting! changing the openapi version to 3.0.3 works!

@char0n
Copy link
Member

char0n commented May 29, 2023

All right, thanks for investigation. So it's OpenAPI 3.1.0 related regression.

@char0n char0n reopened this May 29, 2023
@char0n char0n self-assigned this May 29, 2023
@char0n char0n changed the title changing server url. OpenAPI 3.1.0 support: changing server url May 29, 2023
@char0n
Copy link
Member

char0n commented Nov 2, 2023

After deep investigation, here are my observations:

TODO:

  • document contextUrl option (related issue, PR)
  • document server option (PR)
  • amend ApiDOM servers field normalization (PR)
  • amend SwaggerClient code (PR)

Observation 1

The reason why it works for you for OpenAPI 3.0.x definition and doesn't for OpenAPI 3.1.0 definition, is that for OpenAPI 3.1.0 swagger-client uses ApiDOM to facilitate URI resolution and dereferencing mechanism. For OpenAPI 2.0 and OpenAPI 3.0.x internal swagger-client specmap mechanism is used for URI resolution and dereferencing. specmap suffers with many bugs and some of these bugs are unsolvable given the current architecture.

Part of OpenAPI 3.1.0 dereferencing is using something called normalization. For servers specifically it normalizes overrides defined by the OpenAPI 3.1.0 spec:

 List of Server Objects can be defined in OpenAPI 3.1 on multiple levels:

  - OpenAPI.servers
  - PathItem.servers
  - Operation.servers

 If an alternative server object is specified at the Path Item Object level, it will override OpenAPI.servers.
 If an alternative server object is specified at the Operation Object level, it will override PathItem.servers and OpenAPI.servers respectively.

Now legacy specmap doesn't do that and this is the reason why we're seeing the difference.

Observation 2

Your make_client function fetches the definition explicitly and then passing the url of the definition and content to SwaggerClient. This is not necessary, here is the simplified version of the function:

async function make_client() {
  return await SwaggerClient("http://localhost:9999/localcontextshub_openapi.json");
}

SwaggerClient fetches the definition for you and resolve it. Unless your definition is behind some authentication, this is all you need to do.

Observation 3

You're mutating the resolved spec, to achieve sending requests to proper server.

swaggerClient.spec.servers = [
    {
        "url": "https://localcontextshub.org"
    }
]

You shouldn't do this. Rather when making request you should use server option to tell the client which server to use.

swaggerClient.execute({
  operationId: "projects_list",
  server: "https://localcontextshub.org",
}).then(response => {
    console.log(response)
}, error => {
    console.error(error)
})

Now with this assumes that the URL https://localcontextshub.org is actually defined as one of your Server Objects. If the URL doesn't match any defined Server Object or there are no Server Objects defined, your request will be sent against http://localhost:9999/ (original definition fetch url and default contextUrl).

In your particular case, assuming you want to send request against any arbitrary server, not just one defined in servers fields, the best way to achieve this is to define you OpenAPI.servers as following:

{
  "servers": [
    {"url": "https://ridagop.net"},
    {"url": "/"}
  ]
}

Now this allows you to perform making request against arbitrary URL:

swaggerClient.execute({
  operationId: "projects_list",
  server: '/',
  contextUrl: "https://httpbin.org",
}).then(response => {
    console.log(response)
}, error => {
    console.error(error)
})

NOTE: both contextUrl and server options are undocumented in https://github.com/swagger-api/swagger-js/blob/master/docs/usage/http-client-for-oas-operations.md. I'll make sure they are documented.


Created an issue with OpenAPI spec authors to clarify how servers fixed field should behave: OAI/OpenAPI-Specification#3427


I'll be adding observations as I move forward with this.

char0n added a commit that referenced this issue Nov 3, 2023
char0n added a commit that referenced this issue Nov 3, 2023
…3221)

This change is specific to OpenAPI 2.0 and OpenAPI 3.0.x.

Refs #2967
swagger-bot pushed a commit that referenced this issue Nov 3, 2023
## [3.24.3](v3.24.2...v3.24.3) (2023-11-03)

### Bug Fixes

* **execute:** handle Server Objects overrides for OpenAPI 2.0/3.0.x ([#3221](#3221)) ([d45c9ab](d45c9ab)), closes [#2967](#2967)
@char0n
Copy link
Member

char0n commented Nov 3, 2023

Fix for OpenAPI 2.0/3.0x released in v3.24.3.

swagger-bot pushed a commit that referenced this issue Nov 3, 2023
## [3.24.4](v3.24.3...v3.24.4) (2023-11-03)

### Bug Fixes

* **execute:** handle Server Objects overrides for OpenAPI 3.1.0 ([#3223](#3223)) ([13fed9c](13fed9c)), closes [#2967](#2967)
@char0n
Copy link
Member

char0n commented Nov 3, 2023

Fix for OpenAPI 3.1.0 released in v3.24.4.

@char0n char0n closed this as completed Nov 3, 2023
@transfluxus
Copy link
Author

fantastic, thanks a lot. I will consider your observations and try the corresponding changes

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