Skip to content

[BUG] Root server url cannot be used as a relative path #19343

Open
@scrhartley

Description

@scrhartley

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

Generated client will correctly recognise a relative server such as: /mypath and use this as the base path.
The relative server of just the root path / is not recognized and so the client will instead default to localhost as the base path.
A single slash should be recognised as a valid relative path.
This behavior occurs with multiple generator targets (typescript-fetch and java were tested).

openapi-generator version

7.7.0

This is not a regression:
The relevant code URLPathUtils.isRelativeUrl hasn't been touched in 3 years and a comment on the associated pull request pointed this out a year ago (#10057).

OpenAPI declaration file content
{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenAPI definition",
    "version": "v0"
  },
  "servers": [
    {
      "url": "/",
      "description": "Generated server url"
    }
  ],
  "paths": {},
  "components": {}
}
Generation Details

Tested with both JDK 11 and JDK 17.
This was tested with both the typescript-fetch generator and the java generator.

CLI

java -jar openapi-generator-cli.jar generate -g typescript-fetch -i openapi.json -o my-generated-client

Alternatively, with Maven:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>7.7.0</version>
    <executions>
        <execution>
            <goals><goal>generate</goal></goals>
            <configuration>
                <generatorName>typescript-fetch</generatorName>
                <inputSpec>${project.build.directory}/openapi.json</inputSpec>
                <output>${project.build.directory}/ts-openapi</output>
            </configuration>
        </execution>
    </executions>
</plugin>
Steps to reproduce

Using the provided JSON, the generated client (see above) should have the base URL be empty, rather than falling back to localhost.
The reason for empty, rather than a slash, is so that concatting with work like normal with the context path.

In the typescript-fetch client see runtime.ts.

export const BASE_PATH = "http://localhost".replace(/\/+$/, "");

In the java client, see src/main/java/org/openapitools/client/ApiClient.java.

private String basePath = "http://localhost";
Related issues/PRs
Suggest a fix

Code investigation:

When no server is defined, then a server with "/" is used as the default (OpenAPIDeserializer.parseRoot):`

ArrayNode array = getArray("servers", rootNode, false, location, result);
if (array != null && array.size() > 0) {
    openAPI.setServers(getServersList(array, String.format("%s.%s", location, "servers"), result, path));
} else {
    Server defaultServer = new Server();
    defaultServer.setUrl("/");
    List<Server> servers = new ArrayList<>();
    servers.add(defaultServer);
    openAPI.setServers(servers);
}

This then relates to the following in DefaultGenerator.configureGeneratorProperties:

if (URLPathUtils.isRelativeUrl(openAPI.getServers())) {
    basePath = removeTrailingSlash(basePathWithoutHost);
} else {
    basePath = removeTrailingSlash(config.escapeText(URLPathUtils.getHost(openAPI, config.serverVariableOverrides())));
}

This means that when URLPathUtils.getHost is called above, URLPathUtils.sanitizeUrl changes the URL to localhost:

} else if (url.startsWith("/")) {
    url = LOCAL_HOST + url;
    once(LOGGER).info("'host' (OAS 2.0) or 'servers' (OAS 3.0) not defined in the spec. Default to [{}] for server URL [{}]", LOCAL_HOST, url);
}

Patch:

I have created a patch to support /: https://github.com/scrhartley/openapi-generator/tree/root-relative-url

Why is this not a pull request?

Previously, several of the samples didn't specify an explicit server and so would default to /, which would then become localhost as explained above. By changing / to no longer default to localhost, this would affect readmes, api docs, generated client code and comments and generated spec files. In order to avoid this, I changed input spec files to explicitly use localhost so that the only generated client files that would change would be their generated spec files.

The problem with just changing the input specs to localhost is that some clients were already confused by / being specified as the server , but then the basePath becoming localhost. This means that explicitly specifying localhost as the server may cause some clients which had previously been generating a blank base path, to now use localhost. To avoid these clients changing, I instead made their server be blank (a relative url with the trailing slash removed) since I only updated isRelativeUrl to treat / as relative and hence blank server urls are still handled in the legacy manner.

I do not have the expertise to make existing clients handle / as a server in a more graceful manner, nor am I sure if it would be better to just allow some clients' URL to change to localhost, nor whether "" should also be accepted as a relative URL.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions