Skip to content

Commit 9a3bdbd

Browse files
committed
Export using CSV signed url
1 parent 4614076 commit 9a3bdbd

2 files changed

Lines changed: 60 additions & 20 deletions

File tree

wavefront/server/modules/plugins_module/plugins_module/controllers/datasource_controller.py

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
from user_management_module.utils.user_utils import get_current_user
4040
from plugins_module.services.dynamic_query_service import DynamicQueryService
4141
from db_repo_module.cache.cache_manager import CacheManager
42-
from ..utils.helper import generate_cache_key, validate_yaml_query
42+
from ..utils.helper import (
43+
generate_cache_key,
44+
generate_export_filename_hash,
45+
validate_yaml_query,
46+
)
4347
import csv
4448
import io
4549
import yaml
@@ -765,9 +769,8 @@ async def export_dynamic_query_csv(
765769
cloud_manager: CloudStorageManager = Depends(
766770
Provide[PluginsContainer.cloud_manager]
767771
),
768-
config: PluginsContainer.config.provided = Depends(
769-
Provide[PluginsContainer.config]
770-
),
772+
config: dict = Depends(Provide[PluginsContainer.config]),
773+
force_fetch: int = Query(0),
771774
):
772775
"""Execute the dynamic query and return results as a downloadable CSV file."""
773776
role_id, user_id, _ = get_current_user(request)
@@ -804,6 +807,41 @@ async def export_dynamic_query_csv(
804807
rls_filters = fetch_data_filters(rls_filters)
805808
rls_filter_str = f"{ ' $and '.join(rls_filters)}"
806809

810+
# Bucket and filename: hash of $filter, limit, offset, dynamic_query_params
811+
provider = config['cloud_config']['cloud_provider']
812+
bucket_name = (
813+
config['aws']['aws_asset_storage_bucket']
814+
if provider == 'aws'
815+
else config['gcp']['gcp_asset_storage_bucket']
816+
)
817+
export_hash = generate_export_filename_hash(
818+
filter=filter,
819+
limit=limit,
820+
offset=offset,
821+
params=dynamic_query_params.params if dynamic_query_params else None,
822+
)
823+
filename = f'export_{query_id}_{export_hash}.csv'
824+
file_key = f'dynamic_query_exports/{filename}'
825+
826+
# If not force_fetch, return existing file from bucket if present
827+
if not force_fetch:
828+
existing_keys, _ = cloud_manager.list_files(
829+
bucket_name=bucket_name,
830+
prefix=file_key,
831+
page_size=1,
832+
page_number=1,
833+
)
834+
if existing_keys and existing_keys[0] == file_key:
835+
signed_url = cloud_manager.generate_presigned_url(
836+
bucket_name=bucket_name, key=file_key, type='GET'
837+
)
838+
return JSONResponse(
839+
status_code=status.HTTP_200_OK,
840+
content=response_formatter.buildSuccessResponse(
841+
{'export_url': signed_url}
842+
),
843+
)
844+
807845
datasource_plugin = DatasourcePlugin(datasource_type, datasource_config)
808846
res: Dict[str, Any] = await datasource_plugin.execute_dynamic_query(
809847
yaml_query,
@@ -833,30 +871,15 @@ async def export_dynamic_query_csv(
833871

834872
serialized_res = serialize_values(res[first_key]['result'])
835873

836-
# Convert rows to CSV bytes
874+
# Convert rows to CSV bytes and store in bucket
837875
csv_bytes = _serialized_rows_to_csv(serialized_res)
838-
filename = f'export_{query_id}.csv'
839-
840-
# Store CSV in main application bucket under dynamic_query_updates folder
841-
# and return a signed URL to the file
842-
provider = config.cloud_config.cloud_provider
843-
bucket_name = (
844-
config.aws.aws_asset_storage_bucket
845-
if provider == 'aws'
846-
else config.gcp.gcp_asset_storage_bucket
847-
)
848-
849-
file_key = f'dynamic_query_updates/{filename}'
850-
851-
# Save the CSV to cloud storage
852876
cloud_manager.save_small_file(
853877
file_content=csv_bytes,
854878
bucket_name=bucket_name,
855879
key=file_key,
856880
content_type='text/csv',
857881
)
858882

859-
# Generate a signed URL for downloading the file
860883
signed_url = cloud_manager.generate_presigned_url(
861884
bucket_name=bucket_name, key=file_key, type='GET'
862885
)

wavefront/server/modules/plugins_module/plugins_module/utils/helper.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,23 @@ def generate_cache_key(
5353
return f'dynamic_query:{hash_digest}'
5454

5555

56+
def generate_export_filename_hash(
57+
filter: str = None,
58+
limit: int = None,
59+
offset: int = None,
60+
params: dict[str, str] = None,
61+
) -> str:
62+
"""Generate a short hash for export filename from $filter, limit, offset, and dynamic_query params."""
63+
key_dict = {
64+
'filter': filter,
65+
'limit': limit,
66+
'offset': offset,
67+
'params': params or {},
68+
}
69+
key_json = json.dumps(key_dict, sort_keys=True, separators=(',', ':'))
70+
return hashlib.md5(key_json.encode()).hexdigest()
71+
72+
5673
def validate_yaml_query(yaml_query: dict) -> bool:
5774
"""
5875
Validate the structure of a dynamic query YAML file.

0 commit comments

Comments
 (0)