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

Versionized docs are not showing "servers" unless the base /docs is visited 1st #62

Open
jobewan opened this issue May 6, 2024 · 4 comments

Comments

@jobewan
Copy link

jobewan commented May 6, 2024

Subject of the issue

Versionized docs are not showing "servers" and swagger api executions return 404 unless the base /docs is visited 1st

Your environment

  • python version 3.11.7
  • operating system docker python 3.11.7 image

Steps to reproduce

Run the with_root_path.py example, visit the versionized docs and execute an api call or pull the openapi schema json file

Expected behaviour

The servers should be populated in the versionized openapi shcema and the swagger executions should work

Actual behaviour

This is odd. When using the example with_root_path.py. While using the options: "include_main_docs=False, include_main_openapi_route=False", the versionized docs @ /root/path/api/v[1,2]/docs results in every swagger api execution to return a 404 due to it not using the root path (the api "servers" are not populated in the openapi schema file either). Although, if you change the parameters so that "include_main_docs=True, include_main_openapi_route=True", you can visit the /root/path/docs to see things working as expected, and THEN visit the /root/path/api/v[1,2]/docs paths and those versions work as expected by populating the "servers" param in the schema and the swagger api executions work.

@alexschimpf
Copy link
Owner

@jobewan And just to be clear, which version of fastapi are you using?

@jobewan
Copy link
Author

jobewan commented Jun 3, 2024

fastapi version: 0.106.0

@mat81black
Copy link

mat81black commented Nov 13, 2024

I've encountered the same issue, and I don’t believe it’s due to the FastAPI version, as the error occurs in both version 0.110.0 and the latest version 0.115.5. The behavior is identical to what @jobewan described.

The root of the issue appears to be in the _add_version_docs method in fastapi-versionizer. More precisely from this code:

if self._include_version_openapi_route and self._app.openapi_url is not None:
	@router.get(self._app.openapi_url, include_in_schema=False)
	async def get_openapi() -> Any:
		openapi_params: Dict[str, Any] = {
			'title': title,
			'version': version_str,
			'routes': router.routes,
			'description': self._app.description,
			'terms_of_service': self._app.terms_of_service,
			'contact': self._app.contact,
			'license_info': self._app.license_info,
			'servers': self._app.servers,
			'tags': versioned_tags,
		}


		if hasattr(self._app, 'summary'):
			# Available since OpenAPI 3.1.0, FastAPI 0.99.0
			openapi_params['summary'] = self._app.summary


		return fastapi.openapi.utils.get_openapi(**openapi_params)

The issue is that when accessing a version-specific documentation path, the FastAPI OpenAPI constructor isn’t triggered. Instead, Versionizer’s custom OpenAPI is immediately initiated, which doesn’t properly include the root_path in self.app.servers. As a result, accessing root_path/docs makes everything work correctly because the FastAPI OpenAPI constructor is then applied.

The solution is to add the following lines to the code above:

urls = (server_data.get("url") for server_data in self._app.servers)
server_urls = {url for url in urls if url}

root_path = (self._app.root_path or "").rstrip("/")
if root_path not in server_urls:
	if root_path and self._app.root_path_in_servers:
		self._app.servers.insert(0, {"url": root_path})
		openapi_params["servers"] = self._app.servers
		server_urls.add(root_path)

here is the complete new code.

if self._include_version_openapi_route and self._app.openapi_url is not None:
	@router.get(self._app.openapi_url, include_in_schema=False)
	async def get_openapi() -> Any:
		openapi_params: Dict[str, Any] = {
			'title': title,
			'version': version_str,
			'routes': router.routes,
			'description': self._app.description,
			'terms_of_service': self._app.terms_of_service,
			'contact': self._app.contact,
			'license_info': self._app.license_info,
			'servers': self._app.servers,
			'tags': versioned_tags,
		}

		if hasattr(self._app, 'summary'):
			# Available since OpenAPI 3.1.0, FastAPI 0.99.0
			openapi_params['summary'] = self._app.summary

		urls = (server_data.get("url") for server_data in self._app.servers)
		server_urls = {url for url in urls if url}

		root_path = (self._app.root_path or "").rstrip("/")
		if root_path not in server_urls:
			if root_path and self._app.root_path_in_servers:
				self._app.servers.insert(0, {"url": root_path})
				openapi_params["servers"] = self._app.servers
				server_urls.add(root_path)

		return fastapi.openapi.utils.get_openapi(**openapi_params)

this adds root_path management to fastapi-versionizer.

@alexschimpf
Copy link
Owner

Feel free to open up a PR

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

No branches or pull requests

3 participants