Updates URL parsing, updates API error code handling
This commit is contained in:
parent
631ca606ff
commit
aa89af0324
4 changed files with 101 additions and 55 deletions
|
@ -1,17 +1,20 @@
|
|||
-r requirements.txt
|
||||
pytest
|
||||
ipdb
|
||||
flake8
|
||||
pep8-naming
|
||||
autoflake
|
||||
mccabe
|
||||
yapf
|
||||
tox
|
||||
coverage
|
||||
pytest-cov
|
||||
zest.releaser
|
||||
setuptools
|
||||
twine
|
||||
wheel
|
||||
rope
|
||||
pytest-mock
|
||||
pytest==5.4.3
|
||||
ipdb==0.13.2
|
||||
flake8==3.8.3
|
||||
pep8-naming==0.11.1
|
||||
autoflake==1.3.1
|
||||
mccabe==0.6.1
|
||||
yapf==0.30.0
|
||||
tox==3.15.2
|
||||
coverage==5.1
|
||||
pytest-cov==2.10.0
|
||||
zest.releaser==6.20.1
|
||||
setuptools==47.3.1
|
||||
twine==3.1.1
|
||||
wheel==0.34.2
|
||||
rope==0.17.0
|
||||
pytest-mock==3.1.1
|
||||
brunette==0.1.5
|
||||
lock-requirements==0.1.1
|
||||
requests-mock
|
||||
|
|
|
@ -6,17 +6,16 @@ class ValidationError(Exception):
|
|||
|
||||
|
||||
_CODE_TO_DESCRIPTIONS = {
|
||||
0: ('UNKNOWN', 'API Returned 0'),
|
||||
-1: (
|
||||
'EINTERNAL',
|
||||
(
|
||||
'EINTERNAL', (
|
||||
'An internal error has occurred. Please submit a bug report, '
|
||||
'detailing the exact circumstances in which this error occurred'
|
||||
)
|
||||
),
|
||||
-2: ('EARGS', 'You have passed invalid arguments to this command'),
|
||||
-3: (
|
||||
'EAGAIN',
|
||||
(
|
||||
'EAGAIN', (
|
||||
'(always at the request level) A temporary congestion or server '
|
||||
'malfunction prevented your request from being processed. '
|
||||
'No data was altered. Retry. Retries must be spaced with '
|
||||
|
@ -24,8 +23,7 @@ _CODE_TO_DESCRIPTIONS = {
|
|||
)
|
||||
),
|
||||
-4: (
|
||||
'ERATELIMIT',
|
||||
(
|
||||
'ERATELIMIT', (
|
||||
'You have exceeded your command weight per time quota. Please '
|
||||
'wait a few seconds, then try again (this should never happen '
|
||||
'in sane real-life applications)'
|
||||
|
@ -37,15 +35,13 @@ _CODE_TO_DESCRIPTIONS = {
|
|||
'Too many concurrent IP addresses are accessing this upload target URL'
|
||||
),
|
||||
-7: (
|
||||
'ERANGE',
|
||||
(
|
||||
'ERANGE', (
|
||||
'The upload file packet is out of range or not starting and '
|
||||
'ending on a chunk boundary'
|
||||
)
|
||||
),
|
||||
-8: (
|
||||
'EEXPIRED',
|
||||
(
|
||||
'EEXPIRED', (
|
||||
'The upload target URL you are trying to access has expired. '
|
||||
'Please request a fresh one'
|
||||
)
|
||||
|
|
|
@ -174,24 +174,41 @@ class Mega:
|
|||
data = [data]
|
||||
|
||||
url = f'{self.schema}://g.api.{self.domain}/cs'
|
||||
req = requests.post(
|
||||
response = requests.post(
|
||||
url,
|
||||
params=params,
|
||||
data=json.dumps(data),
|
||||
timeout=self.timeout,
|
||||
)
|
||||
json_resp = json.loads(req.text)
|
||||
if isinstance(json_resp, int):
|
||||
if json_resp == -3:
|
||||
json_resp = json.loads(response.text)
|
||||
try:
|
||||
if isinstance(json_resp, list):
|
||||
int_resp = json_resp[0] if isinstance(
|
||||
json_resp[0], int
|
||||
) else None
|
||||
elif isinstance(json_resp, int):
|
||||
int_resp = json_resp
|
||||
except IndexError:
|
||||
int_resp = None
|
||||
if int_resp is not None:
|
||||
if int_resp == -3:
|
||||
msg = 'Request failed, retrying'
|
||||
logger.info(msg)
|
||||
raise RuntimeError(msg)
|
||||
raise RequestError(json_resp)
|
||||
raise RequestError(int_resp)
|
||||
return json_resp[0]
|
||||
|
||||
def _parse_url(self, url):
|
||||
# parse file id and key from url
|
||||
if '!' in url:
|
||||
"""Parse file id and key from url."""
|
||||
if '/file/' in url:
|
||||
# V2 URL structure
|
||||
url = url.replace(' ', '')
|
||||
file_id = re.findall(r'\W\w\w\w\w\w\w\w\w\W', url)[0][1:-1]
|
||||
id_index = re.search(file_id, url).end()
|
||||
key = url[id_index + 1:]
|
||||
return f'{file_id}!{key}'
|
||||
elif '!' in url:
|
||||
# V1 URL structure
|
||||
match = re.findall(r'/#!(.*)', url)
|
||||
path = match[0]
|
||||
return path
|
||||
|
@ -874,9 +891,12 @@ class Mega:
|
|||
# update attributes
|
||||
data = self._api_request(
|
||||
{
|
||||
'a': 'p',
|
||||
't': dest,
|
||||
'i': self.request_id,
|
||||
'a':
|
||||
'p',
|
||||
't':
|
||||
dest,
|
||||
'i':
|
||||
self.request_id,
|
||||
'n': [
|
||||
{
|
||||
'h': completion_file_handle,
|
||||
|
@ -902,8 +922,10 @@ class Mega:
|
|||
# update attributes
|
||||
data = self._api_request(
|
||||
{
|
||||
'a': 'p',
|
||||
't': parent_node_id,
|
||||
'a':
|
||||
'p',
|
||||
't':
|
||||
parent_node_id,
|
||||
'n': [
|
||||
{
|
||||
'h': 'xxxxxxxx',
|
||||
|
@ -912,7 +934,8 @@ class Mega:
|
|||
'k': encrypted_key
|
||||
}
|
||||
],
|
||||
'i': self.request_id
|
||||
'i':
|
||||
self.request_id
|
||||
}
|
||||
)
|
||||
return data
|
||||
|
@ -1102,8 +1125,10 @@ class Mega:
|
|||
encrypted_name = base64_url_encode(encrypt_attr({'n': dest_name}, k))
|
||||
return self._api_request(
|
||||
{
|
||||
'a': 'p',
|
||||
't': dest_node['h'],
|
||||
'a':
|
||||
'p',
|
||||
't':
|
||||
dest_node['h'],
|
||||
'n': [
|
||||
{
|
||||
'ph': file_handle,
|
||||
|
|
|
@ -2,6 +2,7 @@ import random
|
|||
from pathlib import Path
|
||||
import os
|
||||
|
||||
import requests_mock
|
||||
import pytest
|
||||
|
||||
from mega import Mega
|
||||
|
@ -11,6 +12,7 @@ TEST_PUBLIC_URL = (
|
|||
'https://mega.nz/#!hYVmXKqL!r0d0-WRnFwulR_shhuEDwrY1Vo103-am1MyUy8oV6Ps'
|
||||
)
|
||||
TEST_FILE = os.path.basename(__file__)
|
||||
MODULE = 'mega.mega'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -32,9 +34,7 @@ def mega(folder_name):
|
|||
def uploaded_file(mega, folder_name):
|
||||
folder = mega.find(folder_name)
|
||||
dest_node_id = folder[1]['h']
|
||||
mega.upload(
|
||||
__file__, dest=dest_node_id, dest_filename='test.py'
|
||||
)
|
||||
mega.upload(__file__, dest=dest_node_id, dest_filename='test.py')
|
||||
path = f'{folder_name}/test.py'
|
||||
return mega.find(path)
|
||||
|
||||
|
@ -73,7 +73,6 @@ def test_get_link(mega, uploaded_file):
|
|||
|
||||
|
||||
class TestExport:
|
||||
|
||||
def test_export_folder(self, mega, folder_name):
|
||||
public_url = None
|
||||
for _ in range(2):
|
||||
|
@ -103,9 +102,7 @@ class TestExport:
|
|||
# Upload a single file into a folder
|
||||
folder = mega.find(folder_name)
|
||||
dest_node_id = folder[1]['h']
|
||||
mega.upload(
|
||||
__file__, dest=dest_node_id, dest_filename='test.py'
|
||||
)
|
||||
mega.upload(__file__, dest=dest_node_id, dest_filename='test.py')
|
||||
path = f'{folder_name}/test.py'
|
||||
assert mega.find(path)
|
||||
|
||||
|
@ -143,13 +140,10 @@ class TestCreateFolder:
|
|||
|
||||
|
||||
class TestFind:
|
||||
|
||||
def test_find_file(self, mega, folder_name):
|
||||
folder = mega.find(folder_name)
|
||||
dest_node_id = folder[1]['h']
|
||||
mega.upload(
|
||||
__file__, dest=dest_node_id, dest_filename='test.py'
|
||||
)
|
||||
mega.upload(__file__, dest=dest_node_id, dest_filename='test.py')
|
||||
path = f'{folder_name}/test.py'
|
||||
|
||||
assert mega.find(path)
|
||||
|
@ -194,9 +188,7 @@ def test_download(mega, tmpdir, folder_name):
|
|||
# Upload a single file into a folder
|
||||
folder = mega.find(folder_name)
|
||||
dest_node_id = folder[1]['h']
|
||||
mega.upload(
|
||||
__file__, dest=dest_node_id, dest_filename='test.py'
|
||||
)
|
||||
mega.upload(__file__, dest=dest_node_id, dest_filename='test.py')
|
||||
path = f'{folder_name}/test.py'
|
||||
file = mega.find(path)
|
||||
|
||||
|
@ -222,3 +214,33 @@ def test_add_contact(mega):
|
|||
def test_remove_contact(mega):
|
||||
resp = mega.remove_contact(TEST_CONTACT)
|
||||
assert isinstance(resp, int)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'url, expected_file_id_and_key', [
|
||||
(
|
||||
'https://mega.nz/#!Ue5VRSIQ!kC2E4a4JwfWWCWYNJovGFHlbz8F'
|
||||
'N-ISsBAGPzvTjT6k',
|
||||
'Ue5VRSIQ!kC2E4a4JwfWWCWYNJovGFHlbz8FN-ISsBAGPzvTjT6k'
|
||||
),
|
||||
(
|
||||
'https://mega.nz/file/cH51DYDR#qH7QOfRcM-7N9riZWdSjsRq'
|
||||
'5VDTLfIhThx1capgVA30',
|
||||
'cH51DYDR!qH7QOfRcM-7N9riZWdSjsRq5VDTLfIhThx1capgVA30'
|
||||
),
|
||||
]
|
||||
)
|
||||
def test_parse_url(url, expected_file_id_and_key, mega):
|
||||
assert mega._parse_url(url) == expected_file_id_and_key
|
||||
|
||||
|
||||
class TestAPIRequest:
|
||||
@pytest.mark.parametrize('response_text', ['-3', '-9'])
|
||||
def test_when_api_returns_int_raises_exception(
|
||||
self, mega, response_text,
|
||||
):
|
||||
with requests_mock.Mocker() as m:
|
||||
m.post(
|
||||
f'{mega.schema}://g.api.{mega.domain}/cs', text=response_text
|
||||
)
|
||||
mega._api_request(data={})
|
||||
|
|
Loading…
Reference in a new issue