Updates URL parsing, updates API error code handling

This commit is contained in:
Richard O'Dwyer 2020-06-21 16:18:22 +01:00
parent 631ca606ff
commit aa89af0324
4 changed files with 101 additions and 55 deletions

View file

@ -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

View file

@ -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'
)

View file

@ -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,

View file

@ -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={})