Skip to content

Commit c264e50

Browse files
committed
Implemented EventsEndpoint
1 parent d4e0d26 commit c264e50

21 files changed

+555
-73
lines changed

open_sea_v1/endpoints/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
Import other modules at your own risk, as their location may change.
44
"""
55
from open_sea_v1.endpoints.endpoint_assets import _AssetsEndpoint as AssetsEndpoint
6-
from open_sea_v1.endpoints.endpoint_assets import _AssetsOrderBy as AssetsOrderBy
6+
from open_sea_v1.endpoints.endpoint_assets import _AssetsOrderBy as AssetsOrderBy
7+
8+
from open_sea_v1.endpoints.endpoint_events import _EventsEndpoint as EventsEndpoint
9+
from open_sea_v1.endpoints.endpoint_events import AuctionType
10+
from open_sea_v1.endpoints.endpoint_events import EventType

open_sea_v1/endpoints/endpoint_abc.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from abc import ABC, abstractmethod
2-
from typing import Optional
2+
from typing import Optional, Union
33

44
from requests import Response
55

6+
from open_sea_v1.responses.response__base import _OpenSeaAPIResponse
7+
68

79
class BaseOpenSeaEndpoint(ABC):
810

11+
@property
912
@abstractmethod
10-
def get_request(self, url: str, method: str = 'GET', **kwargs) -> Response:
11-
"""Call to super().get_request passing url and _request_params."""
13+
def __post_init__(self):
14+
"""Using post_init to run param validation"""
1215

1316
@property
1417
@abstractmethod
@@ -23,14 +26,24 @@ def url(self) -> str:
2326
@property
2427
@abstractmethod
2528
def _request_params(self) -> dict:
26-
"""Dictionnary of _request_params to pass into the get_request."""
29+
"""Dictionnary of _request_params to pass into the _get_request."""
2730

2831
@property
2932
@abstractmethod
30-
def validate_request_params(self) -> None:
33+
def _validate_request_params(self) -> None:
3134
""""""
3235

3336
@property
3437
@abstractmethod
35-
def response(self) -> list[dict]:
38+
def response(self) -> Union[list[_OpenSeaAPIResponse], _OpenSeaAPIResponse]:
3639
"""Parsed JSON dictionnary from HTTP Response."""
40+
41+
@property
42+
@abstractmethod
43+
def http_response(self) -> Optional[Response]:
44+
"""HTTP Response from Opensea API."""
45+
46+
@abstractmethod
47+
def get_request(self, url: str, method: str = 'GET', **kwargs) -> Response:
48+
"""Call to super()._get_request passing url and _request_params."""
49+

open_sea_v1/endpoints/endpoint_assets.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
from enum import Enum
33
from typing import Optional
44

5-
from requests import Response
6-
7-
from open_sea_v1.endpoints.endpoint_urls import OpenseaApiEndpoints
8-
from open_sea_v1.endpoints.endpoint_client import OpenSeaClient
95
from open_sea_v1.endpoints.endpoint_abc import BaseOpenSeaEndpoint
10-
from open_sea_v1.responses.asset_obj import _AssetResponse
6+
from open_sea_v1.endpoints.endpoint_client import OpenSeaClient
7+
from open_sea_v1.endpoints.endpoint_urls import OpenseaApiEndpoints
8+
from open_sea_v1.responses.response_asset import _AssetResponse
119

1210

1311
class _AssetsOrderBy(str, Enum):
@@ -28,8 +26,6 @@ class _AssetsEndpoint(OpenSeaClient, BaseOpenSeaEndpoint):
2826
2927
Parameters
3028
----------
31-
width:
32-
width of the snake
3329
3430
owner:
3531
The address of the owner of the assets
@@ -72,17 +68,11 @@ class _AssetsEndpoint(OpenSeaClient, BaseOpenSeaEndpoint):
7268
limit: int = 20
7369

7470
def __post_init__(self):
75-
self.validate_request_params()
76-
self._response: Optional[Response] = None
77-
78-
@property
79-
def http_response(self):
80-
self._validate_response_property()
81-
return self._response
71+
self._validate_request_params()
8272

8373
@property
8474
def response(self) -> list[_AssetResponse]:
85-
self._validate_response_property()
75+
self._assert_get_request_was_called_before_accessing_this_property()
8676
assets_json = self._response.json()['assets']
8777
assets = [_AssetResponse(asset_json) for asset_json in assets_json]
8878
return assets
@@ -92,7 +82,7 @@ def url(self):
9282
return OpenseaApiEndpoints.ASSETS.value
9383

9484
def get_request(self, *args, **kwargs):
95-
self._response = super().get_request(self.url, **self._request_params)
85+
self._response = self._get_request(self.url, **self._request_params)
9686

9787
@property
9888
def _request_params(self) -> dict[dict]:
@@ -103,17 +93,13 @@ def _request_params(self) -> dict[dict]:
10393
)
10494
return dict(api_key=self.api_key, params=params)
10595

106-
def validate_request_params(self) -> None:
96+
def _validate_request_params(self) -> None:
10797
self._validate_mandatory_params()
10898
self._validate_asset_contract_addresses()
10999
self._validate_order_direction()
110100
self._validate_order_by()
111101
self._validate_limit()
112102

113-
def _validate_response_property(self):
114-
if self._response is None:
115-
raise AttributeError('You must call self.request prior to accessing self.response')
116-
117103
def _validate_mandatory_params(self):
118104
mandatory = self.owner, self.token_ids, self.asset_contract_address, self.asset_contract_addresses, self.collection
119105
if all((a is None for a in mandatory)):
@@ -123,7 +109,7 @@ def _validate_mandatory_params(self):
123109
def _validate_asset_contract_addresses(self):
124110
if self.asset_contract_address and self.asset_contract_addresses:
125111
raise ValueError(
126-
"You cannot simultaneously get_request for a single contract_address and a list of contract_addresses."
112+
"You cannot simultaneously _get_request for a single contract_address and a list of contract_addresses."
127113
)
128114

129115
if self.token_ids and not (self.asset_contract_address or self.asset_contract_addresses):

open_sea_v1/endpoints/endpoint_client.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from typing import Optional
2+
13
from requests import Response, request
24

35

46
class OpenSeaClient:
57

68
api_key = None
9+
_response: Optional[Response] = None
710

811
@property
912
def http_headers(self) -> dict:
@@ -12,15 +15,24 @@ def http_headers(self) -> dict:
1215
{"X-API-Key" : self.api_key} if self.api_key else dict(),
1316
}
1417

15-
def get_request(self, url: str, method: str = 'GET', **kwargs) -> Response:
18+
def _get_request(self, url: str, method: str = 'GET', **kwargs) -> Response:
1619
"""
17-
Automatically passes in API key in HTTP get_request headers.
20+
Automatically passes in API key in HTTP _get_request headers.
1821
"""
1922
if 'api_key' in kwargs:
2023
self.api_key = kwargs.pop('api_key')
2124
updated_kwargs = kwargs | self.http_headers
2225
return request(method, url, **updated_kwargs)
2326

27+
@property
28+
def http_response(self):
29+
self._assert_get_request_was_called_before_accessing_this_property()
30+
return self._response
31+
32+
def _assert_get_request_was_called_before_accessing_this_property(self):
33+
if self._response is None:
34+
raise AttributeError('You must call self.request prior to accessing self.response')
35+
2436
# def collections(self, *, asset_owner: Optional[str] = None, offset: int, limit: int) -> OpenseaCollections:
2537
# """
2638
# Use this endpoint to fetch collections and dapps that OpenSea shows on opensea.io,
@@ -45,7 +57,7 @@ def get_request(self, url: str, method: str = 'GET', **kwargs) -> Response:
4557
# def _collections(self, **_request_params) -> Response:
4658
# """Returns HTTPResponse object."""
4759
# url = OpenseaApiEndpoints.COLLECTIONS.value
48-
# return self.get_request("GET", url, _request_params=_request_params)
60+
# return self._get_request("GET", url, _request_params=_request_params)
4961
#
5062
# def asset(self, asset_contract_address: str, token_id: str, account_address: Optional[str] = None) -> OpenseaAsset:
5163
# """
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
from dataclasses import dataclass
2+
from datetime import datetime
3+
from typing import Optional
4+
5+
from open_sea_v1.endpoints.endpoint_abc import BaseOpenSeaEndpoint
6+
from open_sea_v1.endpoints.endpoint_client import OpenSeaClient
7+
from open_sea_v1.endpoints.endpoint_urls import OpenseaApiEndpoints
8+
from open_sea_v1.helpers.extended_classes import ExtendedStrEnum
9+
from open_sea_v1.responses import EventResponse
10+
11+
12+
class EventType(ExtendedStrEnum):
13+
"""
14+
The event type to filter. Can be created for new auctions, successful for sales, cancelled, bid_entered, bid_withdrawn, transfer, or approve
15+
"""
16+
CREATED = 'created'
17+
SUCCESSFUL = 'successful'
18+
CANCELLED = 'cancelled'
19+
BID_ENTERED = 'bid_entered'
20+
BID_WITHDRAWN = 'bid_withdrawn'
21+
TRANSFER = 'transfer'
22+
APPROVE = 'approve'
23+
24+
25+
class AuctionType(ExtendedStrEnum):
26+
"""
27+
Filter by an auction type. Can be english for English Auctions, dutch for fixed-price and declining-price sell orders (Dutch Auctions), or min-price for CryptoPunks bidding auctions.
28+
"""
29+
ENGLISH = 'english'
30+
DUTCH = 'dutch'
31+
MIN_PRICE = 'min-price'
32+
33+
34+
@dataclass
35+
class _EventsEndpoint(OpenSeaClient, BaseOpenSeaEndpoint):
36+
"""
37+
Opensea API Events Endpoint
38+
39+
Parameters
40+
----------
41+
42+
offset:
43+
Offset for pagination
44+
45+
limit:
46+
Limit for pagination
47+
48+
asset_contract_address:
49+
The NFT contract address for the assets for which to show events
50+
51+
event_type:
52+
The event type to filter. Can be created for new auctions, successful for sales, cancelled, bid_entered, bid_withdrawn, transfer, or approve
53+
54+
only_opensea:
55+
Restrict to events on OpenSea auctions. Can be true or false
56+
57+
auction_type:
58+
Filter by an auction type. Can be english for English Auctions, dutch for fixed-price and declining-price sell orders (Dutch Auctions), or min-price for CryptoPunks bidding auctions.
59+
60+
occurred_before:
61+
Only show events listed before this datetime.
62+
63+
occurred_after:
64+
Only show events listed after this datetime.
65+
66+
api_key:
67+
Optional Opensea API key, if you have one.
68+
69+
:return: Parsed JSON
70+
"""
71+
offset: int
72+
limit: int
73+
asset_contract_address: str
74+
event_type: EventType
75+
only_opensea: bool
76+
collection_slug: Optional[str] = None
77+
token_id: Optional[str] = None
78+
account_address: Optional[str] = None
79+
auction_type: Optional[AuctionType] = None
80+
occurred_before: Optional[datetime] = None
81+
occurred_after: Optional[datetime] = None
82+
api_key: Optional[str] = None
83+
84+
def __post_init__(self):
85+
self._validate_request_params()
86+
87+
@property
88+
def url(self) -> str:
89+
return OpenseaApiEndpoints.EVENTS.value
90+
91+
@property
92+
def _request_params(self) -> dict:
93+
params = dict(offset=self.offset, limit=self.limit, asset_contract_address=self.asset_contract_address, event_type=self.event_type, only_opensea=self.only_opensea, collection_slug=self.collection_slug, token_id=self.token_id, account_address=self.account_address, auction_type=self.auction_type, occurred_before=self.occurred_before, occurred_after=self.occurred_after)
94+
return dict(api_key=self.api_key, params=params)
95+
96+
def get_request(self, **kwargs):
97+
self._response = self._get_request(self.url, **self._request_params)
98+
99+
@property
100+
def response(self) -> list[EventResponse]:
101+
self._assert_get_request_was_called_before_accessing_this_property()
102+
events_json = self._response.json()['asset_events']
103+
events = [EventResponse(event) for event in events_json]
104+
return events
105+
106+
def _validate_request_params(self) -> None:
107+
self._validate_param_auction_type()
108+
self._validate_param_event_type()
109+
self._validate_params_occurred_before_and_occurred_after()
110+
111+
def _validate_param_event_type(self) -> None:
112+
if not isinstance(self.event_type, (str, EventType)):
113+
raise TypeError('Invalid event_type type. Must be str or EventType Enum.', f"{self.event_type=}")
114+
115+
if self.event_type not in EventType.list():
116+
raise ValueError('Invalid event_type value. Must be str value from EventType Enum.', f"{self.event_type=}")
117+
118+
def _validate_param_auction_type(self) -> None:
119+
if self.auction_type is None:
120+
return
121+
122+
if not isinstance(self.auction_type, (str, AuctionType)):
123+
raise TypeError('Invalid auction_type type. Must be str or AuctionType Enum.', f"{self.auction_type=}")
124+
125+
if self.auction_type not in AuctionType.list():
126+
raise ValueError('Invalid auction_type value. Must be str value from AuctionType Enum.',
127+
f"{self.auction_type=}")
128+
129+
def _validate_params_occurred_before_and_occurred_after(self) -> None:
130+
self._validate_param_occurred_before()
131+
self._validate_param_occurred_after()
132+
if self.occurred_after and self.occurred_before:
133+
self._assert_param_occurred_before_after_cannot_be_same_value()
134+
self._assert_param_occurred_before_cannot_be_higher_than_occurred_after()
135+
136+
def _validate_param_occurred_before(self) -> None:
137+
if not isinstance(self.occurred_before, (type(None), datetime)):
138+
raise TypeError('Invalid occurred_before type. Must be instance of datetime.',
139+
f'{type(self.occurred_before)=}')
140+
141+
def _validate_param_occurred_after(self) -> None:
142+
if not isinstance(self.occurred_after, (type(None), datetime)):
143+
raise TypeError('Invalid occurred_after type. Must be instance of datetime.',
144+
f'{type(self.occurred_after)=}')
145+
146+
def _assert_param_occurred_before_after_cannot_be_same_value(self) -> None:
147+
if self.occurred_after == self.occurred_before:
148+
raise ValueError('Params occurred_after and occurred_before may not have the same value.',
149+
f"{self.occurred_before=}, {self.occurred_after=}")
150+
151+
def _assert_param_occurred_before_cannot_be_higher_than_occurred_after(self) -> None:
152+
if not self.occurred_after < self.occurred_before:
153+
raise ValueError('Param occurred_before cannot be higher than param occurred_after.',
154+
f"{self.occurred_before=}, {self.occurred_after=}")

open_sea_v1/endpoints/tests/test_assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from unittest import TestCase
33

44
from open_sea_v1.endpoints.endpoint_assets import _AssetsEndpoint, _AssetsOrderBy
5-
from open_sea_v1.responses.asset_obj import _AssetResponse
5+
from open_sea_v1.responses.response_asset import _AssetResponse
66

77

88
class TestAssetsRequest(TestCase):

0 commit comments

Comments
 (0)