fix: Better UI for S3 download and S3 TS SDK endpoints (#3065)
* Better UI for S3 download and S3 TS SDK endpoints * gitignore typescript client node_modules * USe Windmill BE upload endpoint for TS SDK * Use WM backend endpoint in Python upload SDK endpoint * revert changes * Add expiration for Python * Add downaload endpoint * Add toggle for public S3 resource * revert changes to package.json * Add link to doc page * fix unauthorized bug
This commit is contained in:
committed by
GitHub
parent
5baddea6c6
commit
da6edee450
@@ -372,7 +372,7 @@ class Windmill:
|
||||
except JSONDecodeError as e:
|
||||
raise Exception("Could not generate Boto3 S3 connection settings from the provided resource") from e
|
||||
|
||||
def load_s3_file(self, s3object: S3Object, s3_resource_path: str = ""):
|
||||
def load_s3_file(self, s3object: S3Object, s3_resource_path: str | None) -> bytes:
|
||||
"""
|
||||
Load a file from the workspace s3 bucket and returns the bytes stream.
|
||||
|
||||
@@ -384,22 +384,39 @@ class Windmill:
|
||||
file_content = my_obj["Body"].read().decode("utf-8")
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
s3_resource = self.post(
|
||||
f"/w/{self.workspace}/job_helpers/v2/s3_resource_info",
|
||||
json={} if s3_resource_path == "" else {"s3_resource_path": s3_resource_path},
|
||||
).json()
|
||||
except JSONDecodeError as e:
|
||||
raise Exception("Could not generate Boto3 S3 connection settings from the provided resource") from e
|
||||
|
||||
import boto3
|
||||
part_number = 0
|
||||
file_total_size = None
|
||||
file_content: list[int] = []
|
||||
while True:
|
||||
if part_number is None:
|
||||
break
|
||||
try:
|
||||
part_response = self.post(
|
||||
f"/w/{self.workspace}/job_helpers/multipart_download_s3_file",
|
||||
json={
|
||||
"file_key": s3object.s3,
|
||||
"part_number": part_number,
|
||||
"file_size": file_total_size,
|
||||
"s3_resource_path": s3_resource_path,
|
||||
},
|
||||
).json()
|
||||
except JSONDecodeError as e:
|
||||
raise Exception("Could not generate download S3 file part") from e
|
||||
|
||||
args = self.__boto3_connection_settings(s3_resource)
|
||||
s3client = boto3.client("s3", **args)
|
||||
bucket = s3_resource["bucket"]
|
||||
return s3client.get_object(bucket, Key=s3object["s3"])
|
||||
if len(part_response["part_content"]) > 0:
|
||||
file_content = file_content + part_response["part_content"]
|
||||
part_number = part_response["next_part_number"]
|
||||
file_total_size = part_response["file_size"]
|
||||
return bytes(file_content)
|
||||
|
||||
def write_s3_file(self, s3object: S3Object, file_content: bytes, s3_resource_path: str = ""):
|
||||
def write_s3_file(
|
||||
self,
|
||||
s3object: S3Object | None,
|
||||
file_content: bytes,
|
||||
file_expiration: dt.datetime | None,
|
||||
s3_resource_path: str | None,
|
||||
) -> S3Object:
|
||||
"""
|
||||
Write a file to the workspace S3 bucket
|
||||
|
||||
@@ -412,19 +429,21 @@ class Windmill:
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
s3_resource = self.post(
|
||||
f"/w/{self.workspace}/job_helpers/v2/s3_resource_info",
|
||||
json={} if s3_resource_path == "" else {"s3_resource_path": s3_resource_path},
|
||||
result = self.post(
|
||||
f"/w/{self.workspace}/job_helpers/multipart_upload_s3_file",
|
||||
json={
|
||||
"file_key": s3object.s3 if s3object is not None else None,
|
||||
"part_content": file_content,
|
||||
"parts": [],
|
||||
"is_final": True,
|
||||
"cancel_upload": False,
|
||||
"s3_resource_path": s3_resource_path if s3_resource_path != "" else None,
|
||||
"file_expiration": file_expiration.isoformat() if file_expiration else None,
|
||||
},
|
||||
).json()
|
||||
except JSONDecodeError as e:
|
||||
raise Exception("Could not generate Boto3 S3 connection settings from the provided resource") from e
|
||||
|
||||
import boto3
|
||||
|
||||
args = self.__boto3_connection_settings(s3_resource)
|
||||
s3client = boto3.client("s3", **args)
|
||||
bucket = s3_resource["bucket"]
|
||||
s3client.put_object(bucket, Key=s3object["s3"], Body=file_content)
|
||||
except Exception as e:
|
||||
raise Exception("Could not write file to S3") from e
|
||||
return S3Object(s3=result["file_key"])
|
||||
|
||||
def __boto3_connection_settings(self, s3_resource) -> Boto3ConnectionSettings:
|
||||
endpoint_url_prefix = "https://" if s3_resource["useSSL"] else "http://"
|
||||
@@ -676,6 +695,29 @@ def boto3_connection_settings(s3_resource_path: str = "") -> Boto3ConnectionSett
|
||||
return _client.get_boto3_connection_settings(s3_resource_path)
|
||||
|
||||
|
||||
@init_global_client
|
||||
def load_s3_file(s3object: S3Object, s3_resource_path: str = "") -> bytes:
|
||||
"""
|
||||
Load the content of a file stored in S3
|
||||
"""
|
||||
return _client.load_s3_file(s3object, s3_resource_path if s3_resource_path != "" else None)
|
||||
|
||||
|
||||
@init_global_client
|
||||
def write_s3_file(
|
||||
s3object: S3Object | None,
|
||||
file_content: bytes,
|
||||
file_expiration: dt.datetime | None,
|
||||
s3_resource_path: str = "",
|
||||
) -> S3Object:
|
||||
"""
|
||||
Upload a file to S3
|
||||
"""
|
||||
return _client.write_s3_file(
|
||||
s3object, file_content, file_expiration, s3_resource_path if s3_resource_path != "" else None
|
||||
)
|
||||
|
||||
|
||||
@init_global_client
|
||||
def whoami() -> dict:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user