bifrostsdk 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bifrostsdk-1.0.0.dist-info/LICENSE +21 -0
- bifrostsdk-1.0.0.dist-info/METADATA +124 -0
- bifrostsdk-1.0.0.dist-info/RECORD +48 -0
- bifrostsdk-1.0.0.dist-info/WHEEL +5 -0
- bifrostsdk-1.0.0.dist-info/top_level.txt +1 -0
- byfrost/__init__.py +46 -0
- byfrost/__init__.pyi +81 -0
- byfrost/bifrost.py +148 -0
- byfrost/bifrost.pyi +36 -0
- byfrost/gcs/__init__.py +3 -0
- byfrost/gcs/__init__.pyi +3 -0
- byfrost/gcs/gcs.py +189 -0
- byfrost/gcs/gcs.pyi +65 -0
- byfrost/pinata/__init__.py +0 -0
- byfrost/pinata/__init__.pyi +0 -0
- byfrost/pinata/pinata.py +221 -0
- byfrost/pinata/pinata.pyi +54 -0
- byfrost/s3/__init__.py +0 -0
- byfrost/s3/__init__.pyi +0 -0
- byfrost/s3/s3.py +188 -0
- byfrost/s3/s3.pyi +65 -0
- byfrost/shared/__init__.py +3 -0
- byfrost/shared/config/__init__.py +29 -0
- byfrost/shared/config/__init__.pyi +29 -0
- byfrost/shared/config/option.py +21 -0
- byfrost/shared/config/provider.py +22 -0
- byfrost/shared/config/request.py +24 -0
- byfrost/shared/config/url.py +24 -0
- byfrost/shared/errors/__init__.py +16 -0
- byfrost/shared/errors/__init__.pyi +16 -0
- byfrost/shared/errors/color.py +18 -0
- byfrost/shared/errors/constant.py +30 -0
- byfrost/shared/errors/interface.py +21 -0
- byfrost/shared/errors/interface.pyi +21 -0
- byfrost/shared/errors/loga.py +28 -0
- byfrost/shared/errors/loga.pyi +21 -0
- byfrost/shared/request/__init__.py +1 -0
- byfrost/shared/request/__init__.pyi +1 -0
- byfrost/shared/request/builder.py +51 -0
- byfrost/shared/types/__init__.py +1 -0
- byfrost/shared/types/dataclass/__init__.py +4 -0
- byfrost/shared/types/dataclass/__init__.pyi +4 -0
- byfrost/shared/types/dataclass/bridge.py +155 -0
- byfrost/shared/types/dataclass/file.py +222 -0
- byfrost/shared/types/typeddict/__init__.py +12 -0
- byfrost/shared/types/typeddict/__init__.pyi +12 -0
- byfrost/shared/types/typeddict/bridge.py +85 -0
- byfrost/shared/types/typeddict/file.py +203 -0
byfrost/s3/s3.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Bifrost interface for Amazon Simple Storage Service"""
|
|
2
|
+
|
|
3
|
+
from byfrost.shared import errors
|
|
4
|
+
from byfrost.shared.config import provider
|
|
5
|
+
from ..shared.types.dataclass import file as bfile
|
|
6
|
+
from ..shared.types.dataclass import bridge
|
|
7
|
+
from typing import List, Tuple
|
|
8
|
+
from ..shared.errors.interface import BifrostError
|
|
9
|
+
from ..shared.config import option, url
|
|
10
|
+
from ..shared.errors import loga
|
|
11
|
+
import boto3
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SimpleStorageService:
|
|
16
|
+
def __init__(self, bc: bridge.BridgeConfig, client: boto3.client):
|
|
17
|
+
self.provider = provider.providers[bc.provider]
|
|
18
|
+
self.default_bucket = bc.default_bucket
|
|
19
|
+
self.region = bc.region
|
|
20
|
+
self.access_key = bc.access_key
|
|
21
|
+
self.secret_key = bc.secret_key
|
|
22
|
+
self.default_timeout = bc.default_timeout
|
|
23
|
+
self.client = client
|
|
24
|
+
self.enable_debug = bc.enable_debug
|
|
25
|
+
self.public_read = bc.public_read
|
|
26
|
+
self.use_async = bc.use_async
|
|
27
|
+
|
|
28
|
+
def upload_file(
|
|
29
|
+
self, file_face: bfile.File
|
|
30
|
+
) -> Tuple[bfile.UploadedFile, BifrostError]:
|
|
31
|
+
"""upload_file uploads a file to Amazon Simple Storage Service and returns an error if one occurs."""
|
|
32
|
+
if file_face is None:
|
|
33
|
+
return None, BifrostError("file is none", errors.ErrBadRequest)
|
|
34
|
+
|
|
35
|
+
# verify that the file is derived from File
|
|
36
|
+
if not isinstance(file_face, bfile.File):
|
|
37
|
+
return None, BifrostError(
|
|
38
|
+
"invalid file type: {}".format(type(file_face)), errors.ErrBadRequest
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# verify that the file has a valid path
|
|
42
|
+
if file_face.path is None or file_face.path == "":
|
|
43
|
+
return None, BifrostError("no path specified", errors.ErrBadRequest)
|
|
44
|
+
|
|
45
|
+
if file_face.filename is None or file_face.filename == "":
|
|
46
|
+
file_face.filename = os.path.basename(file_face.path)
|
|
47
|
+
else:
|
|
48
|
+
base_name, ext = os.path.splitext(file_face.filename)
|
|
49
|
+
if ext == "": # If the provided filename has no extension
|
|
50
|
+
# Extract the extension from the path and append it to the filename
|
|
51
|
+
_, ext_from_path = os.path.splitext(file_face.path)
|
|
52
|
+
file_face.filename += ext_from_path
|
|
53
|
+
|
|
54
|
+
# verify that the file has a valid options
|
|
55
|
+
if file_face.options is None:
|
|
56
|
+
file_face.options = bfile.Options()
|
|
57
|
+
|
|
58
|
+
# verify the connection
|
|
59
|
+
if not self.is_connected():
|
|
60
|
+
return None, BifrostError(
|
|
61
|
+
"no active Amazon Simple Storage Service client", errors.ErrClientError
|
|
62
|
+
)
|
|
63
|
+
# verify that the file has a valid acl
|
|
64
|
+
if (
|
|
65
|
+
file_face.options.acl is None
|
|
66
|
+
or file_face.options.acl == ""
|
|
67
|
+
and self.public_read
|
|
68
|
+
):
|
|
69
|
+
file_face.options.acl = option.ACLPublicRead
|
|
70
|
+
|
|
71
|
+
# verify that the file has a valid metadata
|
|
72
|
+
if file_face.options.metadata is None:
|
|
73
|
+
file_face.options.metadata = {}
|
|
74
|
+
|
|
75
|
+
# upload file to Amazon Simple Storage Service
|
|
76
|
+
obj = self.client.Object(self.default_bucket, file_face.filename)
|
|
77
|
+
with open(file_face.path, "rb") as f:
|
|
78
|
+
obj.put(
|
|
79
|
+
Body=f,
|
|
80
|
+
ACL=file_face.options.acl,
|
|
81
|
+
Metadata=file_face.options.metadata,
|
|
82
|
+
)
|
|
83
|
+
obj = obj.get()
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
bfile.UploadedFile(
|
|
87
|
+
name=file_face.filename,
|
|
88
|
+
bucket=self.default_bucket,
|
|
89
|
+
path=file_face.path,
|
|
90
|
+
size=obj["ContentLength"],
|
|
91
|
+
url=url.URLSimpleStorageService.format(
|
|
92
|
+
self.default_bucket, self.region, file_face.filename
|
|
93
|
+
),
|
|
94
|
+
preview=url.URLSimpleStorageService.format(
|
|
95
|
+
self.default_bucket, self.region, file_face.filename
|
|
96
|
+
),
|
|
97
|
+
provider_object=obj,
|
|
98
|
+
),
|
|
99
|
+
None,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def upload_multi_file(
|
|
103
|
+
self,
|
|
104
|
+
multi_face: bfile.MultiFile,
|
|
105
|
+
) -> Tuple[List[bfile.UploadedFile], BifrostError]:
|
|
106
|
+
"""upload multi file uploads multiple files to Amazon Simple Storage Service and returns an error if one occurs."""
|
|
107
|
+
if multi_face is None:
|
|
108
|
+
return None, BifrostError("multi file is none", errors.ErrBadRequest)
|
|
109
|
+
|
|
110
|
+
# verify that the multi file is derived from MultiFile
|
|
111
|
+
if not isinstance(multi_face, bfile.MultiFile):
|
|
112
|
+
return None, BifrostError(
|
|
113
|
+
"invalid multi file type: {}".format(type(multi_face)),
|
|
114
|
+
errors.ErrBadRequest,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# verify that the multi file has a valid files
|
|
118
|
+
if multi_face.files is None or len(multi_face.files) == 0:
|
|
119
|
+
return None, BifrostError("no files specified", errors.ErrBadRequest)
|
|
120
|
+
|
|
121
|
+
# verify the connection
|
|
122
|
+
if not self.is_connected():
|
|
123
|
+
return None, BifrostError(
|
|
124
|
+
"no active Amazon Simple Storage Service client", errors.ErrClientError
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# upload files to Amazon Simple Storage Service
|
|
128
|
+
uploaded_files = []
|
|
129
|
+
|
|
130
|
+
for file_face in multi_face.files:
|
|
131
|
+
# verify that the multi file has a valid global options
|
|
132
|
+
if multi_face.global_options is not None:
|
|
133
|
+
# for each option in the global option dataclass, if it is not present in the file option dataclass, then add it to the file option dataclass
|
|
134
|
+
for option in multi_face.global_options.__dataclass_fields__:
|
|
135
|
+
if not hasattr(file_face.options, option):
|
|
136
|
+
setattr(
|
|
137
|
+
file_face.options,
|
|
138
|
+
option,
|
|
139
|
+
getattr(multi_face.global_options, option),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
uploaded_file, err = self.upload_file(file_face)
|
|
143
|
+
|
|
144
|
+
if err is not None:
|
|
145
|
+
if self.enable_debug:
|
|
146
|
+
loga.error(
|
|
147
|
+
"Upload for file at path %s failed with err: %s",
|
|
148
|
+
file_face.path,
|
|
149
|
+
err.message,
|
|
150
|
+
)
|
|
151
|
+
uploaded_files.append(
|
|
152
|
+
bfile.UploadedFile(
|
|
153
|
+
error=err, name=file_face.filename, path=file_face.path
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
continue
|
|
157
|
+
uploaded_files.append(uploaded_file)
|
|
158
|
+
return uploaded_files, None
|
|
159
|
+
|
|
160
|
+
def disconnect(self) -> BifrostError:
|
|
161
|
+
"""disconnect closes the Amazon Simple Storage Service client connection and returns an error if one occurs."""
|
|
162
|
+
if self.is_connected():
|
|
163
|
+
self.client = None
|
|
164
|
+
|
|
165
|
+
def config(self) -> bridge.BridgeConfig:
|
|
166
|
+
"""config returns the provider configuration."""
|
|
167
|
+
return bridge.BridgeConfig(
|
|
168
|
+
provider=self.provider,
|
|
169
|
+
region=self.region,
|
|
170
|
+
default_bucket=self.default_bucket,
|
|
171
|
+
access_key=self.access_key,
|
|
172
|
+
secret_key=self.secret_key,
|
|
173
|
+
default_timeout=self.default_timeout,
|
|
174
|
+
enable_debug=self.enable_debug,
|
|
175
|
+
public_read=self.public_read,
|
|
176
|
+
use_async=self.use_async,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def is_connected(self) -> bool:
|
|
180
|
+
"""is_connected returns true if there is an active connection to the provider."""
|
|
181
|
+
return self.client is not None
|
|
182
|
+
|
|
183
|
+
def upload_folder(
|
|
184
|
+
self,
|
|
185
|
+
fold_face: bfile.MultiFile,
|
|
186
|
+
) -> Tuple[List[bfile.UploadedFile], BifrostError]:
|
|
187
|
+
"""upload_folder uploads a folder to the Simple Storage Service and returns an error if one occurs."""
|
|
188
|
+
...
|
byfrost/s3/s3.pyi
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
The RainbowBridge interface for Google Cloud Storage
|
|
4
|
+
|
|
5
|
+
All providers must implement this interface completely
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ..shared.types.dataclass import file as bfile
|
|
9
|
+
from ..shared.types.dataclass import bridge
|
|
10
|
+
from typing import List, Tuple
|
|
11
|
+
from ..shared.errors.interface import ByfrostError
|
|
12
|
+
import boto3
|
|
13
|
+
|
|
14
|
+
class SimpleStorageService:
|
|
15
|
+
"""Simple Storage Service is the Google Cloud Storage class"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, bc: bridge.BridgeConfig):
|
|
18
|
+
"""__init__ initializes the Simple Storage Service class."""
|
|
19
|
+
...
|
|
20
|
+
def upload_file(
|
|
21
|
+
self, file_face: bfile.File
|
|
22
|
+
) -> Tuple[bfile.UploadedFile, ByfrostError]:
|
|
23
|
+
"""
|
|
24
|
+
upload_file uploads a file to the provider storage and returns an error if one occurs.
|
|
25
|
+
|
|
26
|
+
Note: for some providers, upload_file requires that a default bucket be set in bifrosbfile.BridgeConfig.
|
|
27
|
+
"""
|
|
28
|
+
...
|
|
29
|
+
def upload_multi_file(
|
|
30
|
+
self,
|
|
31
|
+
multi_face: bfile.MultiFile,
|
|
32
|
+
) -> Tuple[List[bfile.UploadedFile], ByfrostError]:
|
|
33
|
+
"""
|
|
34
|
+
upload_multi_file uploads mutliple files to the provider storage and returns an error if one occurs. If any of the uploads fail, the error is appended to the []UploadedFile.Error and also logged when debug is enabled while the rest of the uploads continue.
|
|
35
|
+
|
|
36
|
+
Note: for some providers, UploadMultiFile requires that a default bucket be set in bifrosbfile.BridgeConfig.
|
|
37
|
+
"""
|
|
38
|
+
...
|
|
39
|
+
def disconnect(self) -> ByfrostError:
|
|
40
|
+
"""
|
|
41
|
+
disconnect closes the provider client connection and returns an error if one occurs.
|
|
42
|
+
|
|
43
|
+
Disconnect should only be called when the connection is no longer needed.
|
|
44
|
+
"""
|
|
45
|
+
...
|
|
46
|
+
def config(self) -> bridge.BridgeConfig:
|
|
47
|
+
"""
|
|
48
|
+
config returns the provider configuration.
|
|
49
|
+
"""
|
|
50
|
+
...
|
|
51
|
+
def is_connected(self) -> bool:
|
|
52
|
+
"""
|
|
53
|
+
is_connected returns true if there is an active connection to the provider.
|
|
54
|
+
"""
|
|
55
|
+
...
|
|
56
|
+
def upload_folder(
|
|
57
|
+
self,
|
|
58
|
+
fold_face: bfile.MultiFile,
|
|
59
|
+
) -> Tuple[List[bfile.UploadedFile], ByfrostError]:
|
|
60
|
+
"""
|
|
61
|
+
upload_folder uploads a folder to the provider storage and returns an error if one occurs.
|
|
62
|
+
|
|
63
|
+
Note: for some providers, upload_folder requires that a default bucket be set in bifrosbfile.BridgeConfig.
|
|
64
|
+
"""
|
|
65
|
+
...
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from .option import (
|
|
4
|
+
OptACL,
|
|
5
|
+
ACLPublicRead,
|
|
6
|
+
ACLPrivate,
|
|
7
|
+
OptContentType,
|
|
8
|
+
OptMetadata,
|
|
9
|
+
OptPinata,
|
|
10
|
+
)
|
|
11
|
+
from .provider import PinataCloud, SimpleStorageService, GoogleCloudStorage, providers
|
|
12
|
+
from .request import (
|
|
13
|
+
ReqAuth,
|
|
14
|
+
ReqBearer,
|
|
15
|
+
ReqContentType,
|
|
16
|
+
MethodGet,
|
|
17
|
+
MethodPost,
|
|
18
|
+
MethodPut,
|
|
19
|
+
MethodDelete,
|
|
20
|
+
)
|
|
21
|
+
from .url import (
|
|
22
|
+
URLPinataPinFile,
|
|
23
|
+
URLPinataPinJSON,
|
|
24
|
+
URLPinataPinCID,
|
|
25
|
+
URLPinataAuth,
|
|
26
|
+
URLPinataGateway,
|
|
27
|
+
URLGoogleCloudStorage,
|
|
28
|
+
URLSimpleStorageService,
|
|
29
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from .option import (
|
|
4
|
+
OptACL,
|
|
5
|
+
ACLPublicRead,
|
|
6
|
+
ACLPrivate,
|
|
7
|
+
OptContentType,
|
|
8
|
+
OptMetadata,
|
|
9
|
+
OptPinata,
|
|
10
|
+
)
|
|
11
|
+
from .provider import PinataCloud, SimpleStorageService, GoogleCloudStorage, providers
|
|
12
|
+
from .request import (
|
|
13
|
+
ReqAuth,
|
|
14
|
+
ReqBearer,
|
|
15
|
+
ReqContentType,
|
|
16
|
+
MethodGet,
|
|
17
|
+
MethodPost,
|
|
18
|
+
MethodPut,
|
|
19
|
+
MethodDelete,
|
|
20
|
+
)
|
|
21
|
+
from .url import (
|
|
22
|
+
URLPinataPinFile,
|
|
23
|
+
URLPinataPinJSON,
|
|
24
|
+
URLPinataPinCID,
|
|
25
|
+
URLPinataAuth,
|
|
26
|
+
URLPinataGateway,
|
|
27
|
+
URLGoogleCloudStorage,
|
|
28
|
+
URLSimpleStorageService,
|
|
29
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Options constants."""
|
|
4
|
+
|
|
5
|
+
OptACL = "acl"
|
|
6
|
+
"""ACL is the option to set the ACL of the file"""
|
|
7
|
+
|
|
8
|
+
ACLPublicRead = "public-read"
|
|
9
|
+
"""PublicRead is the option to set the ACL of the file to public read"""
|
|
10
|
+
|
|
11
|
+
ACLPrivate = "private"
|
|
12
|
+
"""Private is the option to set the ACL of the file to private"""
|
|
13
|
+
|
|
14
|
+
OptContentType = "content-type"
|
|
15
|
+
"""ContentType is the option to set the content type of the file"""
|
|
16
|
+
|
|
17
|
+
OptMetadata = "metadata"
|
|
18
|
+
"""Metadata is the option to set the metadata of the file"""
|
|
19
|
+
|
|
20
|
+
OptPinata = "pinataOptions"
|
|
21
|
+
"""OptPinata is the option to set the pinataOptions."""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Provider constants"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
PinataCloud = "pinata"
|
|
8
|
+
"""PinataCloud is the identifier of the Pinata Cloud storage."""
|
|
9
|
+
|
|
10
|
+
SimpleStorageService = "s3"
|
|
11
|
+
"""SimpleStorageService is the identifier of the S3 provider."""
|
|
12
|
+
|
|
13
|
+
GoogleCloudStorage = "gcs"
|
|
14
|
+
"""GoogleCloudStorage is the identifier of the Google Cloud Storage provider."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
providers: Dict[str, str] = {
|
|
18
|
+
"pinata": "Pinata Cloud Storage",
|
|
19
|
+
"s3": "Simple Storage Service",
|
|
20
|
+
"gcs": "Google Cloud Storage",
|
|
21
|
+
}
|
|
22
|
+
"""providers is a map of the supported providers"""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Request constants"""
|
|
4
|
+
|
|
5
|
+
ReqAuth = "Authorization"
|
|
6
|
+
"""ReqAuth is the authorization header identifier."""
|
|
7
|
+
|
|
8
|
+
ReqBearer = "Bearer %s"
|
|
9
|
+
"""ReqBearer is the bearer token identifier."""
|
|
10
|
+
|
|
11
|
+
ReqContentType = "Content-Type"
|
|
12
|
+
"""ReqContentType is the content type header identifier."""
|
|
13
|
+
|
|
14
|
+
MethodGet = "GET"
|
|
15
|
+
"""MethodGet is the HTTP method for GET requests."""
|
|
16
|
+
|
|
17
|
+
MethodPost = "POST"
|
|
18
|
+
"""MethodPost is the HTTP method for POST requests."""
|
|
19
|
+
|
|
20
|
+
MethodPut = "PUT"
|
|
21
|
+
"""MethodPut is the HTTP method for PUT requests."""
|
|
22
|
+
|
|
23
|
+
MethodDelete = "DELETE"
|
|
24
|
+
"""MethodDelete is the HTTP method for DELETE requests."""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""URL constants."""
|
|
4
|
+
|
|
5
|
+
URLPinataPinFile = "https://api.pinata.cloud/pinning/pinFileToIPFS"
|
|
6
|
+
"""URLPinataPinFile is the endpoint for pinning files/folders to Pinata cloud."""
|
|
7
|
+
|
|
8
|
+
URLPinataPinJSON = "https://api.pinata.cloud/pinning/pinJSONToIPFS"
|
|
9
|
+
"""URLPinataPinJSON is the endpoint for pinning JSON objects to Pinata cloud."""
|
|
10
|
+
|
|
11
|
+
URLPinataPinCID = "https://api.pinata.cloud/pinning/pinByHash"
|
|
12
|
+
"""URLPinataPinCID is the endpoint for pinning CIDs to Pinata cloud."""
|
|
13
|
+
|
|
14
|
+
URLPinataAuth = "https://api.pinata.cloud/data/testAuthentication"
|
|
15
|
+
"""URLPinataAuth is the endpoint for testing authentication against provided Pinata credentials."""
|
|
16
|
+
|
|
17
|
+
URLPinataGateway = "https://gateway.pinata.cloud/ipfs/{}"
|
|
18
|
+
"""URLPinataGateway is the public gateway for Pinata cloud."""
|
|
19
|
+
|
|
20
|
+
URLGoogleCloudStorage = "https://storage.googleapis.com/{}/{}"
|
|
21
|
+
"""URLGoogleCloudStorage is the public gateway for Google Cloud Storage."""
|
|
22
|
+
|
|
23
|
+
URLSimpleStorageService = "https://{}.s3.{}.amazonaws.com/{}"
|
|
24
|
+
"""URLSimpleStorageService is the public gateway for Simple Storage Service."""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from .color import WARN, DEBUG, INFO, ERROR, RESET
|
|
4
|
+
from .constant import (
|
|
5
|
+
ErrBadRequest,
|
|
6
|
+
ErrClientError,
|
|
7
|
+
ErrFileOperationFailed,
|
|
8
|
+
ErrIncompleteMultiFileUpload,
|
|
9
|
+
ErrInvalidBucket,
|
|
10
|
+
ErrInvalidConfig,
|
|
11
|
+
ErrInvalidCredentials,
|
|
12
|
+
ErrInvalidProvider,
|
|
13
|
+
ErrUnauthorized,
|
|
14
|
+
)
|
|
15
|
+
from .interface import BifrostError
|
|
16
|
+
from .loga import warn, error, info, debug
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from .color import WARN, DEBUG, INFO, ERROR, RESET
|
|
4
|
+
from .constant import (
|
|
5
|
+
ErrBadRequest,
|
|
6
|
+
ErrClientError,
|
|
7
|
+
ErrFileOperationFailed,
|
|
8
|
+
ErrIncompleteMultiFileUpload,
|
|
9
|
+
ErrInvalidBucket,
|
|
10
|
+
ErrInvalidConfig,
|
|
11
|
+
ErrInvalidCredentials,
|
|
12
|
+
ErrInvalidProvider,
|
|
13
|
+
ErrUnauthorized,
|
|
14
|
+
)
|
|
15
|
+
from .interface import BifrostError
|
|
16
|
+
from .loga import warn, error, info, debug
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Error color codes"""
|
|
4
|
+
|
|
5
|
+
WARN = "\033[0;33m"
|
|
6
|
+
"""Yellow color code"""
|
|
7
|
+
|
|
8
|
+
ERROR = "\033[0;31m"
|
|
9
|
+
"""Red color code"""
|
|
10
|
+
|
|
11
|
+
INFO = "\033[0;32m"
|
|
12
|
+
"""Green color code"""
|
|
13
|
+
|
|
14
|
+
DEBUG = "\033[0;34m"
|
|
15
|
+
"""Blue color code"""
|
|
16
|
+
|
|
17
|
+
RESET = "\033[0m"
|
|
18
|
+
"""Reset color code"""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Error constants for Bifrost."""
|
|
4
|
+
|
|
5
|
+
ErrBadRequest = "bad request"
|
|
6
|
+
"""ErrBadRequest is returned when something fails due to client error."""
|
|
7
|
+
|
|
8
|
+
ErrUnauthorized = "unauthorized"
|
|
9
|
+
"""ErrUnauthorized is returned when something fails due to client not being authorized."""
|
|
10
|
+
|
|
11
|
+
ErrInvalidConfig = "invalid config"
|
|
12
|
+
"""ErrInvalidConfig is returned when the config is invalid."""
|
|
13
|
+
|
|
14
|
+
ErrInvalidBucket = "invalid bucket"
|
|
15
|
+
"""ErrInvalidBucket is returned when the bucket is invalid."""
|
|
16
|
+
|
|
17
|
+
ErrInvalidProvider = "invalid provider"
|
|
18
|
+
"""ErrInvalidProvider is returned when the provider is invalid."""
|
|
19
|
+
|
|
20
|
+
ErrInvalidCredentials = "invalid credentials"
|
|
21
|
+
"""ErrInvalidCredentials is returned when the authentication credentials are invalid."""
|
|
22
|
+
|
|
23
|
+
ErrFileOperationFailed = "file operation failed"
|
|
24
|
+
"""ErrFileOperationFailed is returned when a file operation fails."""
|
|
25
|
+
|
|
26
|
+
ErrIncompleteMultiFileUpload = "incomplete files upload"
|
|
27
|
+
"""ErrIncompleteMultiFileUpload is returned when a multifile upload fails on some files."""
|
|
28
|
+
|
|
29
|
+
ErrClientError = "client error"
|
|
30
|
+
"""ErrClientError is returned when the client returns an error."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BifrostError(Exception):
|
|
7
|
+
"""Bifrost Error Class is the class for all Bifrost errors."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, error: str = "", code: str = ""):
|
|
10
|
+
super().__init__(error)
|
|
11
|
+
self.error = error
|
|
12
|
+
self.code = code
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
return f"{self.code} :: {self.error}"
|
|
16
|
+
|
|
17
|
+
def nil(self) -> bool:
|
|
18
|
+
return self.error == "" and self.code == ""
|
|
19
|
+
|
|
20
|
+
def code(self) -> str:
|
|
21
|
+
return self.code
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
class BifrostError(Exception):
|
|
6
|
+
"""BifrostError is the class for all bifrost errors."""
|
|
7
|
+
|
|
8
|
+
error: str
|
|
9
|
+
"""error is the error message."""
|
|
10
|
+
code: str
|
|
11
|
+
"""code is the error code."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, error: str = "", code: str = ""): ...
|
|
14
|
+
def __str__(self) -> str: ...
|
|
15
|
+
"""__str__ returns the error message."""
|
|
16
|
+
|
|
17
|
+
def nil(self) -> bool: ...
|
|
18
|
+
"""nil returns true if the error is nil."""
|
|
19
|
+
|
|
20
|
+
def code(self) -> str: ...
|
|
21
|
+
"""code returns the error code."""
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from .color import WARN, DEBUG, INFO, ERROR, RESET
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _get_formatted_date_time() -> str:
|
|
8
|
+
return datetime.now().isoformat(sep=" ", timespec="seconds")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_logger(level: str) -> str:
|
|
12
|
+
return _get_formatted_date_time() + " " + level + "{}" + RESET
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def warn(message: str) -> None:
|
|
16
|
+
print(_get_logger(WARN).format(message))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def error(message: str) -> None:
|
|
20
|
+
print(_get_logger(ERROR).format(message))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def info(message: str) -> None:
|
|
24
|
+
print(_get_logger(INFO).format(message))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def debug(message: str) -> None:
|
|
28
|
+
print(_get_logger(DEBUG).format(message))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
def _get_logger(level: str) -> str:
|
|
4
|
+
"""_get_logger returns a logger with the given level"""
|
|
5
|
+
...
|
|
6
|
+
|
|
7
|
+
def warn(message: str) -> None:
|
|
8
|
+
"""warn logs a warning message with color yellow"""
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
def error(message: str) -> None:
|
|
12
|
+
"""error logs an error message with color red"""
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
def info(message: str) -> None:
|
|
16
|
+
"""info logs an info message with color green"""
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
def debug(message: str) -> None:
|
|
20
|
+
"""debug logs a debug message with color blue"""
|
|
21
|
+
...
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .builder import Client, new_client
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .builder import Client, new_client
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from ..types.dataclass import file as bfile
|
|
3
|
+
from requests_toolbelt import MultipartEncoder
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Client:
|
|
8
|
+
def __init__(self, session: requests.Session, url: str):
|
|
9
|
+
self.session = session
|
|
10
|
+
self.url = url
|
|
11
|
+
|
|
12
|
+
def post_form(self, url: str, params: bfile.Param) -> (Any, Exception):
|
|
13
|
+
multipart_fields = {}
|
|
14
|
+
if params.files:
|
|
15
|
+
for pf in params.files:
|
|
16
|
+
multipart_fields[pf.key] = (
|
|
17
|
+
pf.name,
|
|
18
|
+
open(pf.path, "rb"),
|
|
19
|
+
"multipart/form-data",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if params.data:
|
|
23
|
+
for pd in params.data:
|
|
24
|
+
multipart_fields[pd.key] = pd.value
|
|
25
|
+
|
|
26
|
+
encoder = MultipartEncoder(fields=multipart_fields)
|
|
27
|
+
headers = {"Content-Type": encoder.content_type}
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
response = self.session.post(url, data=encoder, headers=headers)
|
|
31
|
+
response.raise_for_status()
|
|
32
|
+
return response.json(), None
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return None, e
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def new_client(url, token, timeout):
|
|
38
|
+
try:
|
|
39
|
+
# Create a session object with a bearer token in the headers
|
|
40
|
+
session = requests.Session()
|
|
41
|
+
headers = {"authorization": f"Bearer {token}"}
|
|
42
|
+
session.headers.update(headers)
|
|
43
|
+
|
|
44
|
+
# Set timeout if it is greater than 0
|
|
45
|
+
if timeout > 0:
|
|
46
|
+
session.timeout = timeout
|
|
47
|
+
|
|
48
|
+
return Client(session, url)
|
|
49
|
+
except Exception as e:
|
|
50
|
+
# Handle any exceptions during client initialization
|
|
51
|
+
return f"Error during client initialization:", {e}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from byfrost.shared.errors.interface import BifrostError
|