openapi-library / OpenAPIValidators

Use Jest or Chai to assert that HTTP responses satisfy an OpenAPI spec
MIT License
189 stars 35 forks source link

support servers with paths relative to another path #247

Open DetachHead opened 2 years ago

DetachHead commented 2 years ago

OpenAPI version 3

Is your feature request related to a problem? Please describe. i'm testing an api with different environments at different paths, like so:

and the schema hosted at http://example.com/dev/openApi be like:

{
    "servers": [
        {
            "url": "/api"
        }
    ]
}

meaning that the full path to the server is http://example.com/dev/api, but the validator fails because it expects http://example.com/api

Describe the solution you'd like according to the openAPI specification, a server url "MAY be relative, to indicate that the host location is relative to the location where the OpenAPI document is being served"

so perhaps something like the jestOpenAPI function can be updated to also support passing a url where the schema is hosted, ie:

jestOpenAPI('http://example.com/dev/openApi')

this way, the validator is aware of the relative location of the servers (ie. /dev/api)

Describe alternatives you've considered something like

const jestOpenApiFromUrl = async (url: string) => {
    const schema: OpenAPIV3.Document = (await axios.get(url)).data
    const relativePath = dropRight(new URL(url).pathname.split('/')).join('/')
    if (typeof relativePath !== 'undefined')
        schema.servers?.forEach((server) => (server.url = path.join(relativePath, server.url)))
    jestOpenAPI(schema)
}

Additional context Add any other context or screenshots about the feature request here.

Are you going to resolve the issue?

rwalle61 commented 2 years ago

Thanks @DetachHead! Sounds like a good enhancement. I don't have time to fix this myself now but anyone who wants this please submit a PR (I can provide support or suggestions). Or upvote this issue so we know how popular it is 🙂

Workaround

Would this work for your use case?

beforeAll(async () => {
  const schema: OpenAPIV3.Document = (await axios.get('http://example.com/dev/openApi')).data;
  schema.servers?.forEach((server) => {
    if (server.url.startsWith('/') { // we only want to change relative server URLs, not absolute ones
      server.url = new URL('/openApi', server.url).href; // prefer this to `path.join` because https://stackoverflow.com/a/16301947/9864252
    }
  }
  jestOpenAPI(schema);
});

Solution

The jestOpenAPI function does not support passing a url where the schema is hosted because fetching the schema from the url would make jestOpenAPI async, and users would have to await it in a beforeAll. That complexity was previously not worth it because one can easily fetch the schema themselves. However your use case would benefit from us providing an async jestOpenAPIFromUrl.

The implementation could be as you've written, or we could do jestOpenAPI(schema, { documentLocation: url }) and handle the relative path internally. That might make it easier to handle OpenAPI 2 documents, which have schema.basePath rather than schema.servers.

We should test the new jestOpenAPI flag similar to existing setup tests and using it with an openapi document, similar to these tests.