Skip to content
This repository has been archived by the owner on Feb 25, 2022. It is now read-only.

Selecting a server or setting a custom endpoint #657

Open
Lucidiot opened this issue Apr 3, 2019 · 4 comments
Open

Selecting a server or setting a custom endpoint #657

Lucidiot opened this issue Apr 3, 2019 · 4 comments

Comments

@Lucidiot
Copy link
Contributor

Lucidiot commented Apr 3, 2019

OpenAPI 3 allows for multiple Server Objects to be defined on a single document; however, it is not currently possible to select which server is wanted with the APIStar client, nor it is to set a custom base URL, except with setting the URL on the schema document itself and updating every Link URL.

I guess adding a base_url=... keyword argument to apistar.Client isn't hard, but how should one select an existing server from the schema, or list them? Currently, the URL from the first server is used: https://github.com/encode/apistar/blob/master/apistar/schemas/openapi.py#L357

@tomchristie
Copy link
Member

Yup - not really sure just yet.

Thanks for digging into all this so much!

I've a bit pushed for time right now so might not be reviewing apistar tickets for the next few days, but I'll get back onto it in due course.

@MattFisher
Copy link

I just ran into this too.

According to the spec for the paths object, the path should be appended to the server object's url

The field name MUST begin with a slash. The path is appended (no relative URL resolution) to the expanded URL from the Server Object's url field in order to construct the full URL.

but OpenAPI.get_link uses urljoin to set the Link's url, which strips any path (like /api/v1) off the url that comes from the server object.
https://github.com/encode/apistar/blob/master/apistar/schemas/openapi.py#L465

@pjxiao
Copy link

pjxiao commented Feb 5, 2020

+1

I tried to avoid this by stripping leading / in paths as follows, but this caused typesystem.base.ValidationError.

doc['paths'] = {
    path.lstrip('/'): path_item_obj
    for path, path_item_obj in doc['paths'].items()
}
apistar.Client(doc)

This is because properties of Paths object are required leading /.
https://github.com/encode/apistar/blob/2edeb694/apistar/schemas/openapi.py#L102

@Lucidiot
Copy link
Contributor Author

Lucidiot commented Feb 5, 2020

The required leading slash is a requirement from OpenAPI itself (see the Paths Object specs).

Here is the workaround we used:

from urllib.parse import urlsplit

class MyClient(apistar.Client):

    def __init__(self, *args, base_url=None, **kwargs):
        # Let APIStar do its parsing
        super().__init__(*args, **kwargs)

        # Strip scheme, hostname and absolute path from all link URLs
        for link_info in self.document.walk_links():
            original_url = urlsplit(link_info.link.url)
            new_url = ('', '', *original_url[2:])
            link_info.link.url = urlunsplit(new_url).lstrip('/')

        if base_url:
            # Ensure the base URL ends with a slash to prevent issues:
            # urljoin('http://a/b', 'c') → http://a/c
            # urljoin('http://a/b/', 'c') → http://a/b/c
            if not base_url.endswith('/'):
                base_url += '/'

            self.document.base_url = base_url

This goes through every parsed link in the Document instance to remove the scheme, hostname and leading slash, resulting in every API endpoint having a relative URL (a/b/c instead of http://myserver.com/a/b/c), then updates Document.url to a custom base URL when specified. This then allows Client.get_url to still use urljoin and do any of its checks, but with any base URL, including custom base paths.

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

No branches or pull requests

4 participants