Skip to content

Commit 1359cc9

Browse files
committed
feat: add override openapi step when start fastapi application
Signed-off-by: ImMin5 <mino@megazone.com> (cherry picked from commit c8c799d)
1 parent 9b4c59d commit 1359cc9

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

src/spaceone/core/fastapi/server.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import logging
22
import uvicorn
3+
import json
4+
import os
35

46
from fastapi import FastAPI, Request
57
from fastapi.middleware.cors import CORSMiddleware
8+
from fastapi.openapi.docs import get_swagger_ui_html
9+
from fastapi.responses import HTMLResponse
610

711
from spaceone.core import config
812
from spaceone.core.logger import set_logger
@@ -130,10 +134,107 @@ def _init_fast_api():
130134
)
131135

132136

137+
def _get_all_services_from_openapi_json_files(openapi_json_path):
138+
services = []
139+
openapi_json_files = os.listdir(openapi_json_path)
140+
for openapi_json_file in openapi_json_files:
141+
services.append('_'.join(openapi_json_file.split('_')[:-1]).lower())
142+
return services
143+
144+
145+
def _sort_services(services):
146+
return sorted(services, key=lambda x: ('identity' not in x, 'inventory' not in x, 'cost-analysis' not in x,
147+
'monitoring' not in x, 'notification' not in x, 'repository' not in x, x))
148+
149+
150+
def _create_openapi_json(app, service_name):
151+
swagger_path = config.get_global('EXTENSION_SWAGGER_PATH')
152+
swagger_path = os.path.join(swagger_path, f"{service_name.replace('-', '_')}_openapi.json")
153+
try:
154+
with open(swagger_path, 'r') as f:
155+
custom_openapi_schema = json.loads(f.read())
156+
custom_openapi_schema['openapi'] = app.openapi().get('openapi')
157+
description = custom_openapi_schema['info']['summary']
158+
app.openapi()['info']['description'] += f"| **{service_name.replace('-', ' ').title()}** | {description} | [/{service_name}/docs](/{service_name}/docs) |\n"
159+
160+
with open(swagger_path, 'w') as f:
161+
json.dump(custom_openapi_schema, f, indent=2)
162+
except Exception as e:
163+
_LOGGER.error(f'[_create_openapi_json] {swagger_path} : {e}', exc_info=True)
164+
165+
166+
def _override_openapi(app):
167+
extension_swagger_path = config.get_global('EXTENSION_SWAGGER_PATH')
168+
if not os.path.exists(extension_swagger_path):
169+
_LOGGER.info(f'[_override_openapi] Extension Swagger Path is not exists. (path = {extension_swagger_path})')
170+
return app
171+
172+
services = _get_all_services_from_openapi_json_files(extension_swagger_path)
173+
services = _sort_services(services)
174+
_openapi_info = app.openapi().get('info')
175+
_openapi_version = app.openapi().get('openapi')
176+
177+
app.openapi()['info']['description'] += "\n<br><br>\n"
178+
app.openapi()['info']['description'] += "\n## List of Services\n"
179+
app.openapi()['info']['description'] += "\n[Home](/docs)\n"
180+
app.openapi()['info']['description'] += "| **Service** | **Description** | **URL** |\n"
181+
app.openapi()['info']['description'] += "|:---|:--- |:---|\n"
182+
183+
for service in services:
184+
service = service.replace('_', '-')
185+
_create_openapi_json(app, service_name=service)
186+
build_docs(app, prefix=f"/{service}", service_name=service)
187+
188+
app.openapi()['info'][
189+
'description'] += "| **Console API** | Service that offers features exclusive to the Console API. | [/docs](/docs#console-api%20%3E%20api) |\n"
190+
191+
return app
192+
193+
194+
def build_docs(
195+
app: FastAPI,
196+
prefix: str,
197+
service_name: str
198+
) -> None:
199+
async def get_openapi():
200+
swagger_path = config.get_global('EXTENSION_SWAGGER_PATH')
201+
swagger_path = os.path.join(swagger_path, f"{service_name.replace('-','_')}_openapi.json")
202+
with open(swagger_path, 'r') as f:
203+
custom_openapi_schema = json.loads(f.read())
204+
return custom_openapi_schema
205+
206+
get_openapi.__name__ = get_openapi.__name__ + prefix
207+
app.add_api_route(prefix + "/openapi.json", get_openapi, include_in_schema=False)
208+
209+
async def swagger_ui_html() -> HTMLResponse:
210+
return get_swagger_ui_html(
211+
openapi_url=prefix + "/openapi.json",
212+
title=f'{service_name.title().replace("-"," ")} API' + ' - Swagger UI',
213+
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
214+
init_oauth=app.swagger_ui_init_oauth,
215+
)
216+
217+
swagger_ui_html.__name__ = swagger_ui_html.__name__ + prefix
218+
app.add_api_route(prefix + "/docs", swagger_ui_html, include_in_schema=False)
219+
220+
221+
def _get_all_services_list(app):
222+
services = []
223+
for route in app.routes:
224+
path = route.path.split('/')
225+
if len(path) == 4:
226+
services.append(path[1].replace('-', '_'))
227+
228+
services = list(set(services))
229+
sorted_services = sorted(services, key=lambda x: ('identity' not in x, 'inventory' not in x, 'cost_analysis' not in x, x))
230+
return sorted_services
231+
232+
133233
def fast_api_app():
134234
app = _init_fast_api()
135235
app = _add_middlewares(app)
136236
app = _include_routers(app)
237+
app = _override_openapi(app)
137238
return app
138239

139240

0 commit comments

Comments
 (0)