Skip to content

Commit 710e59b

Browse files
authored
Merge pull request #7 from python-ellar/save_extra
Save extra for save method
2 parents 464a12d + e6174f2 commit 710e59b

6 files changed

Lines changed: 152 additions & 3 deletions

File tree

README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,128 @@ def upload_file(self, file: UploadFile, storage_service: Inject[StorageService])
168168

169169
See [Sample Project](https://github.com/python-ellar/ellar-storage/tree/master/samples)
170170

171+
## Some Quick Cloud Setup
172+
173+
### Google Cloud Storage
174+
175+
- For a service Account
176+
177+
```python
178+
from ellar.common import Module
179+
from ellar.core import ModuleBase
180+
from ellar_storage import StorageModule, get_driver, Provider
181+
182+
@Module(modules=[
183+
StorageModule.setup(
184+
files={
185+
# For a service Account
186+
"driver": get_driver(Provider.GOOGLE_STORAGE),
187+
"options": {
188+
"key": "client_email",
189+
"secret": "private_key",
190+
"...": "..."
191+
},
192+
},
193+
)
194+
])
195+
class ApplicationModule(ModuleBase):
196+
pass
197+
```
198+
- Installed Application
199+
```python
200+
from ellar.common import Module
201+
from ellar.core import ModuleBase
202+
from ellar_storage import StorageModule, get_driver, Provider
203+
204+
@Module(modules=[
205+
StorageModule.setup(
206+
files={
207+
# For a service Account
208+
"driver": get_driver(Provider.GOOGLE_STORAGE),
209+
"options": {
210+
"key": "client_id",
211+
"secret": "client_secret",
212+
"...": "..."
213+
},
214+
},
215+
)
216+
])
217+
class ApplicationModule(ModuleBase):
218+
pass
219+
```
220+
221+
- GCE instance
222+
```python
223+
from ellar.common import Module
224+
from ellar.core import ModuleBase
225+
from ellar_storage import StorageModule, get_driver, Provider
226+
227+
@Module(modules=[
228+
StorageModule.setup(
229+
files={
230+
# For a service Account
231+
"driver": get_driver(Provider.GOOGLE_STORAGE),
232+
"options": {
233+
"key": "GOOG0123456789ABCXYZ",
234+
"secret": "key_secret",
235+
"...": "..."
236+
},
237+
},
238+
)
239+
])
240+
class ApplicationModule(ModuleBase):
241+
pass
242+
```
243+
See [GCS](https://libcloud.readthedocs.io/en/stable/storage/drivers/google_storage.html?highlight=Google%20Cloud%20Storage#api-docs)
244+
245+
### AWS S3
246+
247+
```python
248+
from ellar.common import Module
249+
from ellar.core import ModuleBase
250+
from ellar_storage import StorageModule, get_driver, Provider
251+
252+
@Module(modules=[
253+
StorageModule.setup(
254+
files={
255+
"driver": get_driver(Provider.S3),
256+
"options": {
257+
"key": "api key",
258+
"secret": "api secret key"
259+
},
260+
},
261+
)
262+
])
263+
class ApplicationModule(ModuleBase):
264+
pass
265+
```
266+
267+
#### Specifying canned ACL when uploading an object
268+
If you want to specify custom ACL when uploading an object,
269+
you can do so by passing `extra` argument with the acl attribute to the `save` or `save_content` methods.
270+
271+
Valid values for this attribute are:
272+
273+
- private (default)
274+
- public-read
275+
- public-read-write
276+
- authenticated-read
277+
- bucket-owner-read
278+
- bucket-owner-full-control
279+
280+
For example
281+
```python
282+
from ellar.common import UploadFile, Inject, post
283+
from ellar_storage import StorageService
284+
285+
@post('/upload')
286+
def upload_file(self, file: UploadFile, storage_service: Inject[StorageService]):
287+
extra = {"content_type": "application/octet-stream", "acl": "public-read"}
288+
stored_file = storage_service.save(file=file, extra=extra)
289+
290+
return {"message": f"{stored_file.filename} saved"}
291+
```
292+
171293
## API Reference
172294

173295
### StorageService

ellar_storage/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Storage Module for Ellar"""
22

3-
__version__ = "0.1.1"
3+
__version__ = "0.1.2"
44

55
from .module import StorageModule
66
from .providers import Provider, get_driver

ellar_storage/module.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class _ContainerOptions(t.TypedDict):
1616

1717
class _StorageSetupKey(t.TypedDict):
1818
driver: t.Type[StorageDriver]
19-
options: _ContainerOptions
19+
options: t.Union[_ContainerOptions, t.Dict[str, t.Any]]
2020

2121

2222
@Module()

ellar_storage/schemas.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import typing as t
22

3-
from ellar.pydantic import model_validator
3+
from ellar.pydantic import field_validator, model_validator
44
from pydantic import BaseModel
55

66
from ellar_storage.storage import StorageDriver
@@ -10,6 +10,13 @@ class _StorageSetupItem(BaseModel):
1010
driver: t.Type[StorageDriver]
1111
options: t.Dict[str, t.Any] = {}
1212

13+
@field_validator("options", mode="before")
14+
def pre_options_validate(cls, value: t.Dict) -> t.Any:
15+
if "key" not in value:
16+
raise ValueError("Driver Options must have a `key` option ")
17+
18+
return value
19+
1320

1421
class StorageSetup(BaseModel):
1522
# default storage name that must exist in `storages`

ellar_storage/services.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,15 @@ def save(
6262
self,
6363
file: UploadFile,
6464
upload_storage: t.Optional[str] = None,
65+
extra: t.Optional[t.Dict[str, t.Any]] = None,
6566
) -> StoredFile:
67+
extra_ = extra or {}
68+
extra_.setdefault(
69+
"meta_data",
70+
{"content_type": file.content_type, "filename": file.filename},
71+
)
72+
extra_.setdefault("content_type", file.content_type)
73+
6674
return self.save_content(
6775
name=file.filename or str(uuid.uuid4())[10],
6876
content=file.file,

tests/test_schema.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,15 @@ def test_storage_fails_for_invalid_default_storage():
6868
},
6969
default="files",
7070
)
71+
72+
73+
def test_storage_fails_for_invalid_driver_option():
74+
with pytest.raises(ValueError, match="Driver Options must have a `key` option"):
75+
StorageSetup(
76+
storages={
77+
"cloud": {
78+
"driver": get_driver(Provider.GOOGLE_STORAGE),
79+
"options": {"secret": "key_secret"},
80+
}
81+
},
82+
)

0 commit comments

Comments
 (0)