Skip to content

Commit

Permalink
Merge pull request #116 from uploadcare/copy_params
Browse files Browse the repository at this point in the history
Added new params to file copy
  • Loading branch information
dmitry-mukhin committed Apr 6, 2017
2 parents 6b2e8bd + d55e241 commit 9849fe6
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 31 deletions.
9 changes: 6 additions & 3 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ History
x.x.x
~~~~~

- Update CDN link to the widget
- Add file.create_local_copy and file.create_remote_copy methods.
- Add new ``make_public`` and ``pattern`` parameters to file.create_remote_copy method.
- Add new ``store`` parameter to file.create_local_copy methods.
- Update CDN link to the widget.
- Use wildcard ``2.x`` to always get the latest
patch or minor version of the ``2`` major version of the widget,
see `widget changelog`_ now and then :)
patch or minor version of widget version ``2``.
See `widget changelog` (see `widget changelog`_).

2.1.0
~~~~~
Expand Down
132 changes: 107 additions & 25 deletions pyuploadcare/api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@

from . import conf
from .api import rest_request, uploading_request
from .exceptions import (InvalidParamError, InvalidRequestError, APIError,
UploadError, TimeoutError)
from .exceptions import InvalidParamError, APIError, UploadError, TimeoutError


logger = logging.getLogger("pyuploadcare")
Expand Down Expand Up @@ -68,6 +67,13 @@ def __repr__(self):
def __str__(self):
return self.cdn_url

def _build_effects(self, effects=''):
if self.default_effects is not None:
fmt = '{head}-/{tail}' if effects else '{head}'
effects = fmt.format(head=self.default_effects,
tail=effects)
return effects

@property
def uuid(self):
return self._uuid
Expand Down Expand Up @@ -232,23 +238,98 @@ def store(self):
self._info_cache = rest_request('PUT', self._api_storage_uri)

def copy(self, effects=None, target=None):
"""Creates File copy
"""Creates a File Copy on Uploadcare or Custom Storage.
File.copy method is deprecated and will be removed in 4.0.0.
Please use `create_local_copy` and `create_remote_copy` instead.
Args:
- effects:
Adds CDN image effects. If ``self.default_effects`` property
is set effects will be combined with default effects.
- target:
Name of a custom storage connected to your project.
Uploadcare storage is used if target is absent.
If ``target`` is ``None``, copy file to Uploadcare storage otherwise
copy to target associated with project.
Add ``effects`` to ``self.default_effects`` if any.
"""
effects = effects or ''
if self.default_effects is not None:
fmt = '{head}-/{tail}' if effects else '{head}'
effects = fmt.format(head=self.default_effects,
tail=effects)
warning = """File.copy method is deprecated and will be
removed in 4.0.0.
Please use `create_local_copy`
and `create_remote_copy` instead.
"""
logger.warn('API Warning: {0}'.format(warning))

if target is not None:
return self.create_remote_copy(target, effects)
else:
return self.create_local_copy(effects)

def create_local_copy(self, effects=None, store=None):
"""Creates a Local File Copy on Uploadcare Storage.
Args:
- effects:
Adds CDN image effects. If ``self.default_effects`` property
is set effects will be combined with default effects.
- store:
If ``store`` option is set to False the copy of your file will
be deleted in 24 hour period after the upload.
Works only if `autostore` is enabled in the project.
"""
effects = self._build_effects(effects)
store = store or ''
data = {
'source': self.cdn_path(effects)
}
if target is not None:
data['target'] = target
if store:
data['store'] = store
return rest_request('POST', 'files/', data=data)

def create_remote_copy(self, target, effects=None, make_public=None,
pattern=None):
"""Creates file copy in remote storage.
Args:
- target:
Name of a custom storage connected to the project.
- effects:
Adds CDN image effects to ``self.default_effects`` if any.
- make_public:
To forbid public from accessing your files on the storage set
``make_public`` option to be False.
Default value is None. Files have public access by default.
- pattern:
Specify ``pattern`` option to set S3 object key name.
Takes precedence over pattern set in project settings.
If neither is specified defaults to
`${uuid}/${filename}${effects}${ext}`.
For more information on each of the options above please refer to
REST API docs https://uploadcare.com/documentation/rest/#file.
Following example copies a file to custom storage named ``samplefs``:
>>> file = File('e8ebfe20-8c11-4a94-9b40-52ecad7d8d1a')
>>> file.create_remote_copy(target='samplefs',
>>> make_public=True,
>>> pattern='${uuid}/${filename}${ext}')
Now custom storage ``samplefs`` contains publicly available file
with original filename billmurray.jpg in
in the directory named ``e8ebfe20-8c11-4a94-9b40-52ecad7d8d1a``.
"""
effects = self._build_effects(effects)
data = {
'source': self.cdn_path(effects),
'target': target
}

if make_public is not None:
data['make_public'] = make_public
if pattern is not None:
data['pattern'] = pattern
return rest_request('POST', 'files/', data=data)

def delete(self):
Expand Down Expand Up @@ -281,8 +362,8 @@ def upload(cls, file_obj, store=None):
"""Uploads a file and returns ``File`` instance.
Args:
file_obj: file object to upload to
store (Optional[bool]): Should the file be automatically stored
- file_obj: file object to upload to
- store (Optional[bool]): Should the file be automatically stored
upon upload. Defaults to None.
- False - do not store file
- True - store file (can result in error if autostore
Expand All @@ -291,6 +372,7 @@ def upload(cls, file_obj, store=None):
Returns:
``File`` instance
"""
if store is None:
store = 'auto'
Expand All @@ -313,21 +395,21 @@ def upload_from_url(cls, url, store=None, filename=None):
"""Uploads file from given url and returns ``FileFromUrl`` instance.
Args:
url (str): URL of file to upload to
store (Optional[bool]): Should the file be automatically stored
- url (str): URL of file to upload to
- store (Optional[bool]): Should the file be automatically stored
upon upload. Defaults to None.
- False - do not store file
- True - store file (can result in error if autostore
is disabled for project)
- None - use project settings
filename (Optional[str]): Name of the uploaded file. If this not
- filename (Optional[str]): Name of the uploaded file. If this not
specified the filename will be obtained from response headers
or source URL. Defaults to None.
Returns:
``FileFromUrl`` instance
"""
"""
if store is None:
store = 'auto'
elif store:
Expand Down Expand Up @@ -357,21 +439,21 @@ def upload_from_url_sync(cls, url, timeout=30, interval=0.3,
"""Uploads file from given url and returns ``File`` instance.
Args:
url (str): URL of file to upload to
store (Optional[bool]): Should the file be automatically stored
- url (str): URL of file to upload to
- store (Optional[bool]): Should the file be automatically stored
upon upload. Defaults to None.
- False - do not store file
- True - store file (can result in error if autostore
is disabled for project)
- None - use project settings
filename (Optional[str]): Name of the uploaded file. If this not
- filename (Optional[str]): Name of the uploaded file. If this not
specified the filename will be obtained from response headers
or source URL. Defaults to None.
timeout (Optional[int]): seconds to wait for successful upload.
- timeout (Optional[int]): seconds to wait for successful upload.
Defaults to 30.
interval (Optional[float]): interval between upload status checks.
- interval (Optional[float]): interval between upload status checks.
Defaults to 0.3.
until_ready (Optional[bool]): should we wait until file is
- until_ready (Optional[bool]): should we wait until file is
available via CDN. Defaults to False.
Returns:
Expand Down
48 changes: 48 additions & 0 deletions tests/functional/test_api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,54 @@ def test_remote_copy_source(self, request):
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/-/flip/",
"target": "tgt"})

@patch('pyuploadcare.api_resources.rest_request', autospec=True)
def test_create_remote_copy(self, request):
request.return_value = {}

# uuid with no effects
f = File('a771f854-c2cb-408a-8c36-71af77811f3b')
f.create_remote_copy(target='tgt')
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/",
"target": "tgt"})

# uuid with effects
f = File('a771f854-c2cb-408a-8c36-71af77811f3b')
f.create_remote_copy(target='tgt', effects='resize/1x1/')
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/1x1/",
"target": "tgt"})

# cdn url with no effects
f = File('a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/2x2/')
f.create_remote_copy(target='tgt')
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/2x2/",
"target": "tgt"})

# cdn url with effects
f = File('a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/')
f.create_remote_copy(target='tgt', effects='flip/')
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/-/flip/",
"target": "tgt"})

#cdn url with effects, set permissions to public
f = File('a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/')
f.create_remote_copy(target='tgt', effects='flip/', make_public=True)
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/-/flip/",
"target": "tgt",
"make_public": True})

#cdn url with effects, naming pattern
f = File('a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/')
f.create_remote_copy(target='tgt', effects='flip/', pattern='${uuid}')
request.assert_called_with('POST', 'files/', data={
"source": "a771f854-c2cb-408a-8c36-71af77811f3b/-/resize/3x3/-/flip/",
"target": "tgt",
"pattern": "${uuid}"})


class FileGroupAsContainerTypeTest(unittest.TestCase):

Expand Down
19 changes: 16 additions & 3 deletions tests/integration/test_api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,27 @@ def tearDownClass(cls):
cls.f.delete()
super(FileCopyTest, cls).tearDownClass()


def test_local_copy(self):
response = self.f.copy()
response = self.f.copy(effects='resize/50x/')
self.assertEqual('file', response['type'])

response = self.f.copy(effects='resize/50x/')
def test_create_local_copy(self):
response = self.f.create_local_copy()
self.assertEqual('file', response['type'])

response = self.f.create_local_copy(effects='resize/50x/')
self.assertEqual('file', response['type'])

response = self.f.create_local_copy(effects='resize/50x/', store=True)
file = File(response['result']['uuid'])
time.sleep(2)
self.assertEqual(file.is_stored(), True)

response = self.f.create_local_copy(store=False)
file = File(response['result']['uuid'])
time.sleep(2)
self.assertEqual(file.is_stored(), False)


class FileListIterationTest(RestAPITestCase):
def test_iteration_over_all_files(self):
Expand Down

0 comments on commit 9849fe6

Please sign in to comment.