Skip to content

Commit e485e45

Browse files
committed
Implemented OrdersEndpoint and OrderResponse.
Major updates: ____________ Implemented OrdersEndpoint and OrderResponse. Implemented get_request rate limiter to all endpoints. Minor updates: ____________ EventResponse: added missing is_private as an optional response attribute. Refactor: passed parsing of http response responsibility to the BaseClient ABC class. Hotfix: prevent json() from raising exception when http_response is empty Hotfix: removed token_id as a possible order_by parameter for Asset endpoint.
1 parent 14969f4 commit e485e45

File tree

13 files changed

+573
-11
lines changed

13 files changed

+573
-11
lines changed

open_sea_v1/endpoints/abc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import Optional, Union, Generator
2+
from typing import Union, Generator
33

44
from requests import Response
55

open_sea_v1/endpoints/assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22
from enum import Enum
3-
from typing import Optional, Generator
3+
from typing import Optional
44

55
from open_sea_v1.endpoints.abc import BaseEndpoint
66
from open_sea_v1.endpoints.client import BaseClient, ClientParams

open_sea_v1/endpoints/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from requests import Response
66

7-
from open_sea_v1.endpoints.client import BaseClient, ClientParams
87
from open_sea_v1.endpoints.abc import BaseEndpoint
8+
from open_sea_v1.endpoints.client import BaseClient, ClientParams
99
from open_sea_v1.endpoints.urls import EndpointURLS
1010
from open_sea_v1.helpers.extended_classes import ExtendedStrEnum
1111
from open_sea_v1.responses.event import EventResponse

open_sea_v1/endpoints/orders.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
from dataclasses import dataclass
2+
from datetime import datetime
3+
4+
from open_sea_v1.endpoints.abc import BaseEndpoint
5+
from open_sea_v1.endpoints.client import BaseClient, ClientParams
6+
from open_sea_v1.endpoints.urls import EndpointURLS
7+
from open_sea_v1.responses.collection import CollectionResponse
8+
from open_sea_v1.responses.order import OrderResponse
9+
10+
11+
@dataclass
12+
class OrdersEndpoint(BaseClient, BaseEndpoint):
13+
"""
14+
How to fetch orders from the OpenSea system.
15+
16+
Parameters
17+
----------
18+
client_params:
19+
ClientParams instance.
20+
21+
asset_contract_address: str
22+
Filter by smart contract address for the asset category.
23+
Needs to be defined together with token_id or token_ids.
24+
25+
payment_token_address: str
26+
Filter by the address of the smart contract of the payment
27+
token that is accepted or offered by the order
28+
29+
maker: str
30+
Filter by the order maker's wallet address
31+
32+
taker: str
33+
Filter by the order taker's wallet address.
34+
Orders open for any taker have the null address as their taker.
35+
36+
owner: str
37+
Filter by the asset owner's wallet address
38+
39+
is_english: bool
40+
When "true", only show English Auction sell orders, which wait for the highest bidder.
41+
When "false", exclude those.
42+
43+
bundled: bool
44+
Only show orders for bundles of assets
45+
46+
include_bundled: bool
47+
Include orders on bundles where all assets in the bundle share the address
48+
provided in asset_contract_address or where the bundle's maker is the address provided in owner.
49+
50+
include_invalid: bool
51+
Include orders marked invalid by the orderbook, typically due to makers
52+
not owning enough of the token/asset anymore.
53+
54+
listed_after: datetime
55+
Only show orders listed after this timestamp.
56+
57+
listed_before: datetime
58+
Only show orders listed before this timestamp.
59+
60+
token_id: str
61+
Filter by the token ID of the order's asset.
62+
Needs to be defined together with asset_contract_address.
63+
64+
token_ids: list[str]
65+
Filter by a list of token IDs for the order's asset.
66+
Needs to be defined together with asset_contract_address.
67+
68+
side: int
69+
Filter by the side of the order.
70+
0 for buy orders and 1 for sell orders.
71+
72+
sale_kind: int
73+
Filter by the kind of sell order.
74+
0 for fixed-price sales or min-bid auctions, and 1 for declining-price Dutch Auctions.
75+
NOTE=use only_english=true for filtering for only English Auctions
76+
77+
limit: int
78+
Number of orders to return (capped at 50).
79+
80+
offset: int
81+
Number of orders to offset by (for pagination)
82+
83+
order_by: str
84+
How to sort the orders. Can be created_date for when they were made,
85+
or eth_price to see the lowest-priced orders first (converted to their ETH values).
86+
eth_price is only supported when asset_contract_address and token_id are also defined.
87+
88+
order_direction: str
89+
Can be asc or desc for ascending or descending sort.
90+
For example, to see the cheapest orders, do order_direction asc and order_by eth_price.
91+
92+
:return=Parsed JSON
93+
"""
94+
client_params: ClientParams = None
95+
asset_contract_address: str = None
96+
payment_token_address: str = None
97+
maker: str = None
98+
taker: str = None
99+
owner: str = None
100+
is_english: bool = None
101+
bundled: bool = None
102+
include_bundled: bool = None
103+
include_invalid: bool = None
104+
listed_after: datetime = None
105+
listed_before: datetime = None
106+
token_id: str = None
107+
token_ids: list[str] = None
108+
side: int = None
109+
sale_kind: int = None
110+
order_by: str = 'created_date'
111+
order_direction: str = 'desc'
112+
113+
def __post_init__(self):
114+
self._validate_request_params()
115+
116+
@property
117+
def url(self):
118+
return EndpointURLS.ORDERS.value
119+
120+
@property
121+
def parsed_http_response(self) -> list[OrderResponse]:
122+
if self._http_response:
123+
orders_jsons = self._http_response.json()['orders']
124+
orders = [OrderResponse(order_json) for order_json in orders_jsons]
125+
return orders
126+
return list()
127+
128+
def _get_request(self, **kwargs):
129+
params = dict(
130+
asset_contract_address=self.asset_contract_address,
131+
payment_token_address=self.payment_token_address,
132+
maker=self.maker,
133+
taker=self.taker,
134+
owner=self.owner,
135+
is_english=self.is_english,
136+
bundled=self.bundled,
137+
include_bundled=self.include_bundled,
138+
include_invalid=self.include_invalid,
139+
listed_after=self.listed_after,
140+
listed_before=self.listed_before,
141+
token_id=self.token_id,
142+
token_ids=self.token_ids,
143+
side=self.side,
144+
sale_kind=self.sale_kind,
145+
limit=self.client_params.limit,
146+
offset=self.client_params.offset,
147+
order_by=self.order_by,
148+
order_direction=self.order_direction,
149+
)
150+
get_request_kwargs = dict(params=params)
151+
self._http_response = super()._get_request(**get_request_kwargs)
152+
return self._http_response
153+
154+
def _validate_request_params(self) -> None:
155+
self._validate_values(self.side, attr_name='side', valid_values=[None, 1, 0])
156+
self._validate_values(self.sale_kind, attr_name='sale_kind', valid_values=[None, 1, 0])
157+
self._validate_values(self.order_by, attr_name='order_by', valid_values=[None, 'created_date', 'eth_price'])
158+
self._validate_values(self.order_direction, attr_name='order_direction', valid_values=[None, 'asc', 'desc'])
159+
160+
self._validate_contract_address_defined_with_token_id_or_tokens_ids()
161+
self._validate_token_id_defined_with_contract_address()
162+
self._validate_token_ids_defined_with_contract_address()
163+
self._validate_token_id_and_token_ids_cannot_be_defined_together()
164+
self._validate_listed_after_and_listed_before_are_datetimes()
165+
self._validate_order_by_eth_price_is_defined_with_asset_contract_address_and_token_id_or_token_ids()
166+
167+
if self.include_bundled:
168+
self._validate_include_bundled_is_defined_with_contract_address_or_owner_address()
169+
self._validate_include_bundled_is_defined_with_token_id_or_token_ids()
170+
171+
def _validate_contract_address_defined_with_token_id_or_tokens_ids(self) -> None:
172+
if self.asset_contract_address is None:
173+
return
174+
if not any([self.token_id, self.token_ids]):
175+
raise AttributeError('Attribute asset_contract_address must be defined together with either token_id or token_ids.')
176+
177+
def _validate_token_id_defined_with_contract_address(self) -> None:
178+
if self.token_id is None:
179+
return
180+
if not self.asset_contract_address:
181+
raise AttributeError('Attribute token_id must be defined together with asset_contract_address')
182+
183+
def _validate_token_ids_defined_with_contract_address(self) -> None:
184+
if self.token_ids is None:
185+
return
186+
if not self.asset_contract_address:
187+
raise AttributeError('Attribute token_ids must be defined together with asset_contract_address')
188+
189+
def _validate_token_id_and_token_ids_cannot_be_defined_together(self) -> None:
190+
if self.token_ids and self.token_id:
191+
raise AttributeError('Attribute token_id and token_ids cannot be defined together.')
192+
193+
def _validate_include_bundled_is_defined_with_contract_address_or_owner_address(self) -> None:
194+
if not any([self.asset_contract_address, self.owner]):
195+
raise AttributeError('Attribute include_bundled must be defined together with asset_contract_address or owner')
196+
197+
def _validate_include_bundled_is_defined_with_token_id_or_token_ids(self):
198+
if not any([self.token_id, self.token_ids]):
199+
raise AttributeError(
200+
'Attribute include_bundled must be defined together with token_id or token_ios')
201+
202+
def _validate_listed_after_and_listed_before_are_datetimes(self):
203+
if not isinstance(self.listed_after, (type(None), datetime)):
204+
raise TypeError("Attribute 'listed_after' must be a datetime instance")
205+
206+
if not isinstance(self.listed_before, (type(None), datetime)):
207+
raise TypeError("Attribute 'listed_before' must be a datetime instance")
208+
209+
def _validate_order_by_eth_price_is_defined_with_asset_contract_address_and_token_id_or_token_ids(self):
210+
if not self.order_by:
211+
return
212+
213+
if not self.asset_contract_address and self.order_by == 'eth_price' and not any([self.token_id, self.token_ids]):
214+
raise AttributeError("When attribute 'order_by' is set to 'eth_price', you must also set the asset_contract_address and token_id or token_ids attributes.")
215+
216+
@staticmethod
217+
def _validate_values(attr, *, attr_name: str, valid_values: list):
218+
if not any(attr is v for v in valid_values):
219+
raise ValueError(f"attr {attr_name} must be a value among: {valid_values}")

open_sea_v1/endpoints/tests/test_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from open_sea_v1.endpoints.client import ClientParams
55
from open_sea_v1.endpoints.events import EventsEndpoint, EventType
66

7+
78
class TestClientParams(TestCase):
89

910
def test_max_pages_attr_is_automatically_decremented_by_1(self):

open_sea_v1/endpoints/tests/test_events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from unittest import TestCase
21
from datetime import datetime, timedelta
2+
from unittest import TestCase
33

4-
from open_sea_v1.endpoints.events import EventsEndpoint, EventType, AuctionType
54
from open_sea_v1.endpoints.client import ClientParams
5+
from open_sea_v1.endpoints.events import EventsEndpoint, EventType, AuctionType
66

77

88
class TestEventsEndpoint(TestCase):

0 commit comments

Comments
 (0)