diff --git a/omise/__init__.py b/omise/__init__.py index c318116..0f43d6a 100644 --- a/omise/__init__.py +++ b/omise/__init__.py @@ -30,6 +30,7 @@ def iteritems(d, **kw): 'Balance', 'BankAccount', 'Card', + 'Chain', 'Charge', 'Collection', 'Customer', @@ -65,6 +66,7 @@ def _get_class_for(type): 'balance': Balance, 'bank_account': BankAccount, 'card': Card, + 'chain': Chain, 'charge': Charge, 'customer': Customer, 'dispute': Dispute, @@ -538,6 +540,82 @@ def destroyed(self): return self._attributes.get('deleted', False) +class Chain(_MainResource, Base): + """API class representing chain details. + + This API class is used for retrieving and revoking chains. Chains represent + sub-merchant accounts which have authorized another account to create + charges and perform other actions on their behalf. + + Basic usage:: + + >>> import omise + >>> omise.api_secret = 'skey_test_4xs8breq3htbkj03d2x' + >>> chain = omise.Chain.retrieve('acch_test_5lals6ot3vlz6lsnfhn') + + >>> chain.email + 'john.doe@example.com' + """ + + @classmethod + def _collection_path(cls): + return 'chains' + + @classmethod + def _instance_path(cls, chain_id): + return ('chains', chain_id) + + @classmethod + def retrieve(cls, chain_id=None): + """Retrieve the sub-merchant chain details for the given + :param:`chain_id`. If :param:`chain_id` is not given, all sub-merchant + chains will be returned instead. + + :param chain_id: (optional) a chain id to retrieve. + :type chain_id: str + :rtype: Chain + """ + if chain_id: + return _as_object(cls._request('get', cls._instance_path(chain_id))) + return _as_object(cls._request('get', cls._collection_path())) + + @classmethod + def list(cls): + """Return a list of sub-merchant chains belonging to your account. + + :rtype: LazyCollection + """ + return LazyCollection(cls._collection_path()) + + def reload(self): + """Reload the sub-merchant chain details. + + :rtype: Chain + """ + return self._reload_data( + self._request('get', + self._instance_path(self._attributes['id']))) + + def revoke(self): + """Revoke the sub-merchant chain. + + Basic usage:: + + >>> import omise + >>> omise.api_secret = 'skey_test_4xs8breq3htbkj03d2x' + >>> chain = omise.Chain.retrieve('acch_test_5lals6ot3vlz6lsnfhn') + >>> chain.revoke() + + >>> chain.revoked + True + + :rtype: Chain + """ + + path = self._instance_path(self._attributes['id']) + ('revoke',) + return self._reload_data(self._request('post', path)) + + class Charge(_MainResource, Base): """API class representing a charge. diff --git a/omise/test/test_chain.py b/omise/test/test_chain.py new file mode 100644 index 0000000..e7d4e74 --- /dev/null +++ b/omise/test/test_chain.py @@ -0,0 +1,144 @@ +import mock +import unittest + +from .helper import _ResourceMixin + + +class ChainTest(_ResourceMixin, unittest.TestCase): + + def _getTargetClass(self): + from .. import Chain + return Chain + + def _getCollectionClass(self): + from .. import Collection + return Collection + + def _makeOne(self): + return self._getTargetClass().from_data({ + 'object': 'chain', + 'id': 'acch_test', + 'livemode': False, + 'location': '/chains/acch_test', + 'revoked': False, + 'email': 'john.doe@example.com', + 'key': 'ckey_test', + 'created': '2021-02-02T03:12:43Z' + }) + + @mock.patch('requests.get') + def test_retrieve(self, api_call): + class_ = self._getTargetClass() + self.mockResponse(api_call, """{ + "object": "chain", + "id": "acch_test", + "livemode": false, + "location": "/chains/acch_test", + "revoked": false, + "email": "john.doe@example.com", + "key": "ckey_test", + "created": "2021-02-02T03:12:43Z" + }""") + + chain = class_.retrieve('acch_test') + self.assertTrue(isinstance(chain, class_)) + self.assertEqual(chain.id, 'acch_test') + self.assertEqual(chain.email, 'john.doe@example.com') + self.assertEqual(chain.key, 'ckey_test') + self.assertFalse(chain.revoked) + self.assertRequest(api_call, 'https://api.omise.co/chains/acch_test') + + @mock.patch('requests.get') + def test_retrieve_no_args(self, api_call): + class_ = self._getTargetClass() + collection_class_ = self._getCollectionClass() + self.mockResponse(api_call, """{ + "object": "list", + "data": [ + { + "object": "chain", + "id": "acch_test_1", + "livemode": false, + "location": "/chains/acch_test_1", + "revoked": true, + "email": "jenny.doe@example.com", + "key": "", + "created_at": "2020-09-22T06:08:38Z" + }, + { + "object": "chain", + "id": "acch_test_2", + "livemode": false, + "location": "/chains/acch_test_2", + "revoked": false, + "email": "john.doe@example.com", + "key": "ckey_test", + "created_at": "2021-02-02T03:12:43Z" + } + ], + "limit": 20, + "offset": 0, + "total": 2, + "location": null, + "order": "chronological", + "from": "1970-01-01T00:00:00Z", + "to": "2021-02-02T03:16:57Z" + }""") + + chains = class_.retrieve() + self.assertTrue(isinstance(chains, collection_class_)) + self.assertTrue(isinstance(chains[0], class_)) + self.assertTrue(chains[0].id, 'acch_test_1') + self.assertTrue(chains[0].email, 'jenny.doe@example.com') + self.assertTrue(chains[1].id, 'acch_test_2') + self.assertTrue(chains[1].email, 'john.doe@example.com') + self.assertRequest(api_call, 'https://api.omise.co/chains') + + @mock.patch('requests.get') + def test_reload(self, api_call): + chain = self._makeOne() + class_ = self._getTargetClass() + + self.assertTrue(isinstance(chain, class_)) + self.assertEqual(chain.id, 'acch_test') + self.assertFalse(chain.revoked) + + self.mockResponse(api_call, """{ + "object": "chain", + "id": "acch_test", + "livemode": false, + "location": "/chains/acch_test", + "revoked": true, + "email": "john.doe@example.com", + "key": "ckey_test", + "created": "2021-02-02T03:12:43Z" + }""") + + chain.reload() + self.assertEqual(chain.id, 'acch_test') + self.assertTrue(chain.revoked) + self.assertRequest( + api_call, + 'https://api.omise.co/chains/acch_test' + ) + + @mock.patch('requests.post') + def test_revoke(self, api_call): + chain = self._makeOne() + class_ = self._getTargetClass() + self.mockResponse(api_call, """{ + "object": "chain", + "id": "acch_test", + "livemode": false, + "revoked": true + }""") + + self.assertTrue(isinstance(chain, class_)) + self.assertEqual(chain.id, 'acch_test') + + chain.revoke() + self.assertTrue(chain.revoked) + self.assertRequest( + api_call, + 'https://api.omise.co/chains/acch_test/revoke' + ) \ No newline at end of file