Skip to content

feat: update file management to support project_id and user_file model #17

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

Merged
merged 1 commit into from
Jan 2, 2025
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
8 changes: 7 additions & 1 deletion src/spaceone/file_manager/conf/router_conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
ROUTER = [
{
"router_path": "spaceone.file_manager.interface.rest.files:router",
"router_path": "spaceone.file_manager.interface.rest.file:router",
"router_options": {
"prefix": "/files",
},
},
{
"router_path": "spaceone.file_manager.interface.rest.user_file:router",
"router_options": {
"prefix": "/files",
},
Expand Down
10 changes: 4 additions & 6 deletions src/spaceone/file_manager/connector/aws_s3_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,16 @@ def get_download_url(self, file_id, file_name):

return response

def check_file(self, file_id, file_name):
object_name = self._generate_object_name(file_id, file_name)
def check_file(self, remote_file_path):
try:
self.client.get_object(Bucket=self.bucket_name, Key=object_name)
self.client.head_object(Bucket=self.bucket_name, Key=remote_file_path)
return True
except Exception as e:
_LOGGER.debug(f"[check_file] get_object error: {e}")
return False

def delete_file(self, file_id, file_name):
object_name = self._generate_object_name(file_id, file_name)
self.client.delete_object(Bucket=self.bucket_name, Key=object_name)
def delete_file(self, remote_file_path):
self.client.delete_object(Bucket=self.bucket_name, Key=remote_file_path)

@staticmethod
def _generate_object_name(file_id, file_name):
Expand Down
4 changes: 2 additions & 2 deletions src/spaceone/file_manager/connector/file_base_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ def get_download_url(self, file_id: str, file_name: str) -> str:
pass

@abc.abstractmethod
def check_file(self, file_id: str, file_name: str) -> bool:
def check_file(self, remote_file_path:str) -> bool:
pass

@abc.abstractmethod
def delete_file(self, file_id: str, file_name: str) -> None:
def delete_file(self, remote_file_path:str ) -> None:
pass

@abc.abstractmethod
Expand Down
96 changes: 96 additions & 0 deletions src/spaceone/file_manager/connector/minio_connector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import logging
import boto3
from io import BytesIO
import botocore
from botocore.client import Config as BotocoreConfig

from spaceone.core.error import *
from spaceone.file_manager.connector.file_base_connector import FileBaseConnector

__all__ = ["MinIOS3Connector"]
_LOGGER = logging.getLogger(__name__)

class MinIOS3Connector(FileBaseConnector):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.client = None
self.bucket_name = None
self._create_session()
self._set_bucket()

def _create_session(self):
endpoint = self.config.get("minio_endpoint")
access_key_id = self.config.get("minio_access_key_id")
secret_access_key = self.config.get("minio_secret_access_key")
region_name = self.config.get("region_name")

if endpoint is None:
raise ERROR_CONNECTOR_CONFIGURATION(backend="MinIOS3Connector")

if region_name is None:
raise ERROR_CONNECTOR_CONFIGURATION(backend="MinIOS3Connector")


if access_key_id and secret_access_key:
self.client = boto3.client(
"s3",
endpoint_url=endpoint,
aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name=region_name,
config=BotocoreConfig(signature_version='s3v4'),
)
else:
self.client = boto3.client("s3", region_name=region_name)

def _set_bucket(self):
bucket_name = self.config.get("bucket_name")

if bucket_name is None:
raise ERROR_CONNECTOR_CONFIGURATION(backend="MinIOS3Connector")

self.bucket_name = bucket_name

def get_upload_url(self, file_id, file_name):
object_name = self._generate_object_name(file_id, file_name)
response = self.client.generate_presigned_post(self.bucket_name, object_name)
return response["url"], response["fields"]

def get_download_url(self, file_id: str, file_name: str) -> str:
object_name = self._generate_object_name(file_id, file_name)
response = self.client.generate_presigned_url(
"get_object", Params={"Bucket": self.bucket_name, "Key": object_name}, ExpiresIn=86400
)
return response

def check_file(self, file_id, file_name):
object_name = self._generate_object_name(file_id, file_name)
try:
self.client.get_object(Bucket=self.bucket_name, Key=object_name)
return True
except botocore.exceptions.ClientError as e:
_LOGGER.debug(f"[check_file] get_object error: {e}")
return False

def delete_file(self, file_id, file_name):
object_name = self._generate_object_name(file_id, file_name)
self.client.delete_object(Bucket=self.bucket_name, Key=object_name)

@staticmethod
def _generate_object_name(file_id, file_name):
return f"{file_id}/{file_name}"

def upload_file(self, remote_file_path:str, data: bytes) -> None:
file_obj = BytesIO(data)
if self.client is None:
raise ERROR_CONNECTOR_CONFIGURATION(backend="MinIOS3Connector")
self.client.upload_fileobj(file_obj, self.bucket_name, remote_file_path)

def download_file(self, remote_file_path:str) :

if self.client is None:
raise ERROR_CONNECTOR_CONFIGURATION(backend="MinIOS3Connector")

obj = self.client.get_object(Bucket=self.bucket_name, Key=remote_file_path)
return obj["Body"]
17 changes: 15 additions & 2 deletions src/spaceone/file_manager/error/custom.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
from spaceone.core.error import *


class ERROR_NOT_AUTHORIZATION_USER(ERROR_BASE):
_message = "Not authorization user. (user_id = {user_id}, domain_id = {domain_id})"

class ERROR_NOT_SUPPORTED_RESOURCE_GROUP(ERROR_BASE):
_message = "Not supported resource group. (resource_group = {resource_group})"

class ERROR_NOT_DEFINED_FILE_BACKEND(ERROR_BASE):
_message = "File backend not defined. (backend = {backend})"

class ERROR_FILE_DOWNLOAD_URL_EXIST(ERROR_BASE):
_message = "File download url is not exist. (file_id = {file_id})"
_message = "File download url is not exist. (file_id = {file_id}, name = {name})"


class ERROR_FILE_DOWNLOAD_FAILED(ERROR_BASE):
_message = "File download failed. (file_id = {file_id})"
_message = "File download failed. (name = {name})"

class ERROR_NOT_MATCH_USER_ID(ERROR_BASE):
_message = "Not match user_id. (user_id = {user_id}, domain_id = {domain_id})"

class ERROR_FILE_UPLOAD_FAILED(ERROR_BASE):
_message = "File upload failed. (name = {name})"


class ERROR_FILE_DELETE_FAILED(ERROR_BASE):
_message = "File delete failed. (name = {name})"
38 changes: 38 additions & 0 deletions src/spaceone/file_manager/interface/grpc/userfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from spaceone.api.file_manager.v1 import userfile_pb2, userfile_pb2_grpc
from spaceone.core.pygrpc import BaseAPI
from spaceone.file_manager.service.user_file_service import UserFileService


class UserFile(BaseAPI, userfile_pb2_grpc.UserFileServicer):
pb2 = userfile_pb2
pb2_grpc = userfile_pb2_grpc

def update(self, request, context):
params, metadata = self.parse_request(request, context)
file_svc = UserFileService(metadata)
response: dict = file_svc.update(params)
return self.dict_to_message(response)

def delete(self, request, context):
params, metadata = self.parse_request(request, context)
file_svc = UserFileService(metadata)
file_svc.delete(params)
return self.empty()

def get(self, request, context):
params, metadata = self.parse_request(request, context)
file_svc = UserFileService(metadata)
response: dict = file_svc.get(params)
return self.dict_to_message(response)

def list(self, request, context):
params, metadata = self.parse_request(request, context)
file_svc = UserFileService(metadata)
response: dict = file_svc.list(params)
return self.dict_to_message(response)

def stat(self, request, context):
params, metadata = self.parse_request(request, context)
file_svc = UserFileService(metadata)
response: dict = file_svc.stat(params)
return self.dict_to_message(response)
Loading
Loading