Skip to content

Commit

Permalink
Merge pull request #12 from k0t3n/feature/min-max-amounts
Browse files Browse the repository at this point in the history
Feature/min max amounts
  • Loading branch information
Alexey Kotenko authored Mar 26, 2020
2 parents 72a869c + 2e57943 commit 72583a4
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 17 deletions.
26 changes: 19 additions & 7 deletions changelly_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@

import requests

from changelly_api.conf import *
from changelly_api.exceptions import *


class ChangellyAPI:
def __init__(self, api_key, secret, url='https://api.changelly.com'):
def __init__(self, api_key, api_secret, url=API_ROOT_URL):
self._api_key = api_key
self._secret = secret
self._api_secret = api_secret
self._url = url

def _generate_sign(self, json_data):
return hmac.new(self._secret.encode('utf-8'), json_data.encode('utf-8'), hashlib.sha512).hexdigest()
return hmac.new(self._api_secret.encode('utf-8'), json_data.encode('utf-8'), hashlib.sha512).hexdigest()

def _parse_response(self, response):
if response.status_code == 401:
Expand All @@ -30,6 +31,15 @@ def _parse_response(self, response):
raise JSONResponseParseError('Error parsing JSON response')

if data.get('error'):

if data['error']['code'] == INVALID_AMOUNT_ERROR_CODE:
value = float(data['error']['message'][34:36])

if 'maximal' in data['error']['message']:
raise AmountGreaterThanMaximum(value)
elif 'minimal' in data['error']['message']:
raise AmountLessThanMinimum(value)

raise ChangellyAPIError(data['error'])

return data.get('result')
Expand All @@ -44,10 +54,12 @@ def _make_request(self, method, params=None):

serialized_data = json.dumps(message)

sign = hmac.new(self._secret.encode('utf-8'), serialized_data.encode('utf-8'), hashlib.sha512).hexdigest()

headers = {'api-key': self._api_key, 'sign': sign, 'Content-type': 'application/json'}
response = requests.post(self._url, headers=headers, data=serialized_data)
headers = {'api-key': self._api_key, 'sign': self._generate_sign(serialized_data),
'Content-type': 'application/json'}
try:
response = requests.post(self._url, headers=headers, data=serialized_data)
except requests.RequestException as error:
raise ChangellyAPIError(f'Unknown error occurred during request: {error}')

return self._parse_response(response)

Expand Down
3 changes: 3 additions & 0 deletions changelly_api/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
API_ROOT_URL = 'https://api.changelly.com'

INVALID_AMOUNT_ERROR_CODE = -32600
13 changes: 13 additions & 0 deletions changelly_api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ class ChangellyAPIError(Exception):
pass


class InvalidAmount(ChangellyAPIError):
def __init__(self, threshold_value: float):
self.threshold_value = threshold_value


class AmountGreaterThanMaximum(InvalidAmount):
pass


class AmountLessThanMinimum(InvalidAmount):
pass


class JSONResponseParseError(ChangellyAPIError):
pass

Expand Down
31 changes: 31 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest

from changelly_api import ChangellyAPI


@pytest.fixture(scope='module')
def api():
return ChangellyAPI('key', 'secret')


@pytest.fixture()
def get_fix_rate_for_amount_data():
return {
'response': {
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "test",
"from": "btc",
"to": "eth",
"result": "48.520938768637152700759353642",
"amountFrom": "1",
"amountTo": "48.520938768637152700"
}
},
'request': {
'from': 'btc',
'to': 'eth',
'amountFrom': 1,
}
}
9 changes: 0 additions & 9 deletions tests/generate_sign_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import json

import pytest

from changelly_api import ChangellyAPI


@pytest.fixture(scope='module')
def api():
return ChangellyAPI('key', 'secret')


def test_generate_sign(api):
data = {
Expand Down
50 changes: 50 additions & 0 deletions tests/get_fix_rate_for_amount_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest
import requests_mock

from changelly_api.conf import API_ROOT_URL
from changelly_api.exceptions import AmountGreaterThanMaximum, AmountLessThanMinimum


@requests_mock.Mocker(kw='requests_mock')
def test(api, get_fix_rate_for_amount_data, **kwargs):
r_mock = kwargs['requests_mock']
r_mock.post(API_ROOT_URL, json=get_fix_rate_for_amount_data['response'])
response = api.get_fix_rate_for_amount(get_fix_rate_for_amount_data['request'])

assert response == get_fix_rate_for_amount_data['response']['result']


@requests_mock.Mocker(kw='requests_mock')
def test_invalid_minimum_amount(api, get_fix_rate_for_amount_data, **kwargs):
minimum_amount = 10
r_mock = kwargs['requests_mock']
data = {
'error': {
'code': -32600,
'message': f'invalid amount: minimal amount is {minimum_amount}'
}
}
r_mock.post(API_ROOT_URL, json=data)

with pytest.raises(AmountLessThanMinimum) as error:
api.get_fix_rate_for_amount(get_fix_rate_for_amount_data['request'])

assert error.value.threshold_value == minimum_amount


@requests_mock.Mocker(kw='requests_mock')
def test_invalid_maximum_amount(api, get_fix_rate_for_amount_data, **kwargs):
maximum_amount = 10
r_mock = kwargs['requests_mock']
response = {
'error': {
'code': -32600,
'message': f'invalid amount: maximal amount is {maximum_amount}'
}
}
r_mock.post(API_ROOT_URL, json=response)

with pytest.raises(AmountGreaterThanMaximum) as error:
api.get_fix_rate_for_amount(get_fix_rate_for_amount_data['request'])

assert error.value.threshold_value == maximum_amount
4 changes: 3 additions & 1 deletion tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pytest-cov
codecov
pytest
pytest
mock
requests_mock

0 comments on commit 72583a4

Please sign in to comment.