Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/pip_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ spaceone-api
typing-inspect
python-multipart
PyJWT
openpyxl
2 changes: 1 addition & 1 deletion src/cloudforet/console_api_v2/conf/global_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"default": {},
"local": {
"backend": "spaceone.core.cache.local_cache.LocalCache",
"max_size": 128,
"max_size": 1024,
"ttl": 300,
},
}
Expand Down
7 changes: 7 additions & 0 deletions src/cloudforet/console_api_v2/conf/router_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
"tags": ["console-api > extension > agent"],
},
},
{
"router_path": "cloudforet.console_api_v2.interface.rest.extension.excel:router",
"router_options": {
"prefix": "/console-api/extension/excel",
"tags": ["console-api > extension > excel"],
},
},
{
"router_path": "cloudforet.console_api_v2.interface.rest.swagger:router",
},
Expand Down
68 changes: 68 additions & 0 deletions src/cloudforet/console_api_v2/interface/rest/extension/excel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import json
import logging
import inspect
from fastapi import APIRouter, Depends, Request, Query
from fastapi.concurrency import run_in_threadpool
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from starlette.responses import StreamingResponse

from spaceone.core import cache
from spaceone.core.error import ERROR_REQUIRED_PARAMETER, ERROR_CACHE_CONFIGURATION
from spaceone.core.fastapi.api import BaseAPI, exception_handler

from cloudforet.console_api_v2.service.excel_service import ExcelService

_LOGGER = logging.getLogger(__name__)
_AUTH_SCHEME = HTTPBearer(auto_error=False)

router = APIRouter(include_in_schema=True)

SERVICE = "console-api"
RESOURCE = "Excel"


@router.post("/export")
@exception_handler
async def export(
request: Request, token: HTTPAuthorizationCredentials = Depends(_AUTH_SCHEME)
) -> dict:
base_api = BaseAPI()
base_api.service = SERVICE
verb = inspect.currentframe().f_code.co_name

if token:
params, metadata = await base_api.parse_request(
request, token=token.credentials, resource=RESOURCE, verb=verb
)
else:
params, metadata = await base_api.parse_request(
request, None, resource=RESOURCE, verb=verb
)

excel_service = ExcelService(metadata)

response = await run_in_threadpool(excel_service.export, params)
return response


@router.get("/download")
@exception_handler
async def download(key: str = Query(default=None)) -> StreamingResponse:
base_api = BaseAPI()
base_api.service = SERVICE

if not key:
raise ERROR_REQUIRED_PARAMETER(key="key")

if not cache.is_set(alias="local"):
raise ERROR_CACHE_CONFIGURATION(alias="local")

json_str = cache.get(key=f"console-api:excel:{key}", alias="local")
json_obj = json.loads(json_str)

params = json_obj["request_body"]
metadata = json_obj["auth_info"]

excel_service = ExcelService(metadata)
response = await run_in_threadpool(excel_service.download, params)
return response
45 changes: 44 additions & 1 deletion src/cloudforet/console_api_v2/manager/cloudforet_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import Tuple, Generator

from spaceone.core.connector.space_connector import SpaceConnector
from spaceone.core.manager import BaseManager
Expand All @@ -10,16 +11,58 @@
class CloudforetManager(BaseManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._page_size = 1000

def dispatch_api(self, grpc_method: str, params: dict, token: str = None):
service, resource, verb = self._parse_grpc_method(grpc_method)
space_connector = SpaceConnector(service=service, token=token)
return space_connector.dispatch(f"{resource}.{verb}", params)

def paginate_api(
self, grpc_method: str, params: dict, token: str = None, limit: int = 1000
) -> Generator[dict, None, None]:
start = 1
paged_params = params.copy()
while True:
query = paged_params.get("query", {})
page_info = query.get("page", {})
page_info["start"] = start
page_info["limit"] = max(page_info.get("limit", self._page_size), limit)

paged_params["query"].update({"page": page_info})

response = self.dispatch_api(grpc_method, paged_params, token)
results = response.get("results", [])
yield response

if len(results) < page_info["limit"]:
break

start += page_info["limit"]

@staticmethod
def _parse_grpc_method(grpc_method):
def _parse_grpc_method(grpc_method: str) -> Tuple[str, str, str]:
try:
service, resource, verb = grpc_method.split(".")
return service, resource, verb
except Exception as e:
raise ERROR_PARSE_GRPC_METHOD(grpc_method=grpc_method, reason=e)

@classmethod
def convert_grpc_method_from_url(cls, url: str) -> str:
try:
parts = url.strip("/").split("/")
print(parts)
Copy link

Copilot AI May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the debug print statement or replace it with a logging call to avoid accidentally exposing internal state in production.

Suggested change
print(parts)
_LOGGER.debug(parts)

Copilot uses AI. Check for mistakes.

if len(parts) != 3:
raise ValueError("Path must have at least two segments")

service = parts[0].replace("-", "_") # snake_case
r_source = "".join(
word.capitalize() for word in parts[1].split("-")
) # PascalCase
verb = parts[2].replace("-", "_")

return ".".join([service, r_source, verb])
except Exception as e:
raise ERROR_PARSE_GRPC_METHOD(grpc_method=url, reason=e)
6 changes: 0 additions & 6 deletions src/cloudforet/console_api_v2/model/auth/request.py

This file was deleted.

24 changes: 24 additions & 0 deletions src/cloudforet/console_api_v2/model/excel/request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Union
from pydantic import BaseModel

__all__ = ["ExcelExportRequest"]


class ExcelSource(BaseModel):
url: Union[str, None] = None
param: Union[dict, None] = None
data: Union[dict, list, None] = None


class ExcelTemplate(BaseModel):
options: Union[dict, None] = None
fields: Union[list, None] = None


class ExcelExportRequest(BaseModel):
source: ExcelSource
template: ExcelTemplate


class ExcelDownloadRequest(BaseModel):
key: str
47 changes: 0 additions & 47 deletions src/cloudforet/console_api_v2/model/resource.py

This file was deleted.

Loading
Loading