Skip to content

Commit 0fcd71f

Browse files
feat(api): manual updates
1 parent 0d38dfa commit 0fcd71f

File tree

13 files changed

+147
-116
lines changed

13 files changed

+147
-116
lines changed

.stats.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 15
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-100e7052e74644026f594642a424e04ab306d44e6c73a1f4761cf8a7d7ee0d8f.yml
3-
openapi_spec_hash: 3437145a74c032f2319a235bf40baa88
4-
config_hash: 36b26d0d29548d4aa575fc337915ad42
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-f48e33c7d90ed6418a852f8d4d951d07b09f4f3f939feb395dc2aa03f522d81e.yml
3+
openapi_spec_hash: c516120ecf51bb8425b3b9ed76c6423a
4+
config_hash: c5ac9bd5889d27aa168f06d6d0fef0b3

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ The REST API documentation can be found on [developers.beeper.com](https://devel
1414
## Installation
1515

1616
```sh
17-
# install from PyPI
18-
pip install beeper_desktop_api
17+
# install from the production repo
18+
pip install git+ssh://git@github.com/beeper/desktop-api-python.git
1919
```
2020

21+
> [!NOTE]
22+
> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install beeper_desktop_api`
23+
2124
## Usage
2225

2326
The full API of this library can be found in [api.md](api.md).
@@ -78,8 +81,8 @@ By default, the async client uses `httpx` for HTTP requests. However, for improv
7881
You can enable this by installing `aiohttp`:
7982

8083
```sh
81-
# install from PyPI
82-
pip install beeper_desktop_api[aiohttp]
84+
# install from the production repo
85+
pip install 'beeper_desktop_api[aiohttp] @ git+ssh://git@github.com/beeper/desktop-api-python.git'
8386
```
8487

8588
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:

api.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ from beeper_desktop_api.types import ContactSearchResponse
4040

4141
Methods:
4242

43-
- <code title="get /v1/contacts/search">client.contacts.<a href="./src/beeper_desktop_api/resources/contacts.py">search</a>(\*\*<a href="src/beeper_desktop_api/types/contact_search_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/contact_search_response.py">ContactSearchResponse</a></code>
43+
- <code title="get /v1/accounts/{accountID}/contacts/search">client.contacts.<a href="./src/beeper_desktop_api/resources/contacts.py">search</a>(account_id, \*\*<a href="src/beeper_desktop_api/types/contact_search_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/contact_search_response.py">ContactSearchResponse</a></code>
4444

4545
# Chats
4646

@@ -70,11 +70,11 @@ Methods:
7070
Types:
7171

7272
```python
73-
from beeper_desktop_api.types import MessageListResponse, MessageSendResponse
73+
from beeper_desktop_api.types import MessageSendResponse
7474
```
7575

7676
Methods:
7777

78-
- <code title="get /v1/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">list</a>(\*\*<a href="src/beeper_desktop_api/types/message_list_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_list_response.py">MessageListResponse</a></code>
78+
- <code title="get /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">list</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_list_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorList[Message]</a></code>
7979
- <code title="get /v1/messages/search">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">search</a>(\*\*<a href="src/beeper_desktop_api/types/message_search_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorSearch[Message]</a></code>
80-
- <code title="post /v1/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">send</a>(\*\*<a href="src/beeper_desktop_api/types/message_send_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_send_response.py">MessageSendResponse</a></code>
80+
- <code title="post /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">send</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_send_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_send_response.py">MessageSendResponse</a></code>

src/beeper_desktop_api/pagination.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

3-
from typing import List, Generic, TypeVar, Optional
4-
from typing_extensions import override
3+
from typing import Any, List, Generic, TypeVar, Optional, cast
4+
from typing_extensions import Protocol, override, runtime_checkable
55

66
from pydantic import Field as FieldInfo
77

@@ -12,6 +12,11 @@
1212
_T = TypeVar("_T")
1313

1414

15+
@runtime_checkable
16+
class CursorListItem(Protocol):
17+
sort_key: Optional[str]
18+
19+
1520
class SyncCursorSearch(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
1621
items: List[_T]
1722
has_more: Optional[bool] = FieldInfo(alias="hasMore", default=None)
@@ -75,8 +80,6 @@ def next_page_info(self) -> Optional[PageInfo]:
7580
class SyncCursorList(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
7681
items: List[_T]
7782
has_more: Optional[bool] = FieldInfo(alias="hasMore", default=None)
78-
oldest_cursor: Optional[str] = FieldInfo(alias="oldestCursor", default=None)
79-
newest_cursor: Optional[str] = FieldInfo(alias="newestCursor", default=None)
8083

8184
@override
8285
def _get_page_items(self) -> List[_T]:
@@ -95,18 +98,21 @@ def has_next_page(self) -> bool:
9598

9699
@override
97100
def next_page_info(self) -> Optional[PageInfo]:
98-
oldest_cursor = self.oldest_cursor
99-
if not oldest_cursor:
101+
items = self.items
102+
if not items:
100103
return None
101104

102-
return PageInfo(params={"cursor": oldest_cursor})
105+
item = cast(Any, items[-1])
106+
if not isinstance(item, CursorListItem) or item.sort_key is None:
107+
# TODO emit warning log
108+
return None
109+
110+
return PageInfo(params={"cursor": item.sort_key})
103111

104112

105113
class AsyncCursorList(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
106114
items: List[_T]
107115
has_more: Optional[bool] = FieldInfo(alias="hasMore", default=None)
108-
oldest_cursor: Optional[str] = FieldInfo(alias="oldestCursor", default=None)
109-
newest_cursor: Optional[str] = FieldInfo(alias="newestCursor", default=None)
110116

111117
@override
112118
def _get_page_items(self) -> List[_T]:
@@ -125,8 +131,13 @@ def has_next_page(self) -> bool:
125131

126132
@override
127133
def next_page_info(self) -> Optional[PageInfo]:
128-
oldest_cursor = self.oldest_cursor
129-
if not oldest_cursor:
134+
items = self.items
135+
if not items:
130136
return None
131137

132-
return PageInfo(params={"cursor": oldest_cursor})
138+
item = cast(Any, items[-1])
139+
if not isinstance(item, CursorListItem) or item.sort_key is None:
140+
# TODO emit warning log
141+
return None
142+
143+
return PageInfo(params={"cursor": item.sort_key})

src/beeper_desktop_api/resources/contacts.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ def with_streaming_response(self) -> ContactsResourceWithStreamingResponse:
4545

4646
def search(
4747
self,
48-
*,
4948
account_id: str,
49+
*,
5050
query: str,
5151
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
5252
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -72,20 +72,16 @@ def search(
7272
7373
timeout: Override the client-level default timeout for this request, in seconds
7474
"""
75+
if not account_id:
76+
raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
7577
return self._get(
76-
"/v1/contacts/search",
78+
f"/v1/accounts/{account_id}/contacts/search",
7779
options=make_request_options(
7880
extra_headers=extra_headers,
7981
extra_query=extra_query,
8082
extra_body=extra_body,
8183
timeout=timeout,
82-
query=maybe_transform(
83-
{
84-
"account_id": account_id,
85-
"query": query,
86-
},
87-
contact_search_params.ContactSearchParams,
88-
),
84+
query=maybe_transform({"query": query}, contact_search_params.ContactSearchParams),
8985
),
9086
cast_to=ContactSearchResponse,
9187
)
@@ -115,8 +111,8 @@ def with_streaming_response(self) -> AsyncContactsResourceWithStreamingResponse:
115111

116112
async def search(
117113
self,
118-
*,
119114
account_id: str,
115+
*,
120116
query: str,
121117
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
122118
# The extra values given here take precedence over values defined on the client or passed to this method.
@@ -142,20 +138,16 @@ async def search(
142138
143139
timeout: Override the client-level default timeout for this request, in seconds
144140
"""
141+
if not account_id:
142+
raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
145143
return await self._get(
146-
"/v1/contacts/search",
144+
f"/v1/accounts/{account_id}/contacts/search",
147145
options=make_request_options(
148146
extra_headers=extra_headers,
149147
extra_query=extra_query,
150148
extra_body=extra_body,
151149
timeout=timeout,
152-
query=await async_maybe_transform(
153-
{
154-
"account_id": account_id,
155-
"query": query,
156-
},
157-
contact_search_params.ContactSearchParams,
158-
),
150+
query=await async_maybe_transform({"query": query}, contact_search_params.ContactSearchParams),
159151
),
160152
cast_to=ContactSearchResponse,
161153
)

src/beeper_desktop_api/resources/messages.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@
1919
async_to_raw_response_wrapper,
2020
async_to_streamed_response_wrapper,
2121
)
22-
from ..pagination import SyncCursorSearch, AsyncCursorSearch
22+
from ..pagination import SyncCursorList, AsyncCursorList, SyncCursorSearch, AsyncCursorSearch
2323
from .._base_client import AsyncPaginator, make_request_options
2424
from ..types.shared.message import Message
25-
from ..types.message_list_response import MessageListResponse
2625
from ..types.message_send_response import MessageSendResponse
2726

2827
__all__ = ["MessagesResource", "AsyncMessagesResource"]
@@ -52,8 +51,8 @@ def with_streaming_response(self) -> MessagesResourceWithStreamingResponse:
5251

5352
def list(
5453
self,
55-
*,
5654
chat_id: str,
55+
*,
5756
cursor: str | Omit = omit,
5857
direction: Literal["after", "before"] | Omit = omit,
5958
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -62,7 +61,7 @@ def list(
6261
extra_query: Query | None = None,
6362
extra_body: Body | None = None,
6463
timeout: float | httpx.Timeout | None | NotGiven = not_given,
65-
) -> MessageListResponse:
64+
) -> SyncCursorList[Message]:
6665
"""List all messages in a chat with cursor-based pagination.
6766
6867
Sorted by timestamp.
@@ -84,23 +83,25 @@ def list(
8483
8584
timeout: Override the client-level default timeout for this request, in seconds
8685
"""
87-
return self._get(
88-
"/v1/messages",
86+
if not chat_id:
87+
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
88+
return self._get_api_list(
89+
f"/v1/chats/{chat_id}/messages",
90+
page=SyncCursorList[Message],
8991
options=make_request_options(
9092
extra_headers=extra_headers,
9193
extra_query=extra_query,
9294
extra_body=extra_body,
9395
timeout=timeout,
9496
query=maybe_transform(
9597
{
96-
"chat_id": chat_id,
9798
"cursor": cursor,
9899
"direction": direction,
99100
},
100101
message_list_params.MessageListParams,
101102
),
102103
),
103-
cast_to=MessageListResponse,
104+
model=Message,
104105
)
105106

106107
def search(
@@ -206,8 +207,8 @@ def search(
206207

207208
def send(
208209
self,
210+
chat_id: str,
209211
*,
210-
chat_id: str | Omit = omit,
211212
reply_to_message_id: str | Omit = omit,
212213
text: str | Omit = omit,
213214
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -237,11 +238,12 @@ def send(
237238
238239
timeout: Override the client-level default timeout for this request, in seconds
239240
"""
241+
if not chat_id:
242+
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
240243
return self._post(
241-
"/v1/messages",
244+
f"/v1/chats/{chat_id}/messages",
242245
body=maybe_transform(
243246
{
244-
"chat_id": chat_id,
245247
"reply_to_message_id": reply_to_message_id,
246248
"text": text,
247249
},
@@ -276,10 +278,10 @@ def with_streaming_response(self) -> AsyncMessagesResourceWithStreamingResponse:
276278
"""
277279
return AsyncMessagesResourceWithStreamingResponse(self)
278280

279-
async def list(
281+
def list(
280282
self,
281-
*,
282283
chat_id: str,
284+
*,
283285
cursor: str | Omit = omit,
284286
direction: Literal["after", "before"] | Omit = omit,
285287
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -288,7 +290,7 @@ async def list(
288290
extra_query: Query | None = None,
289291
extra_body: Body | None = None,
290292
timeout: float | httpx.Timeout | None | NotGiven = not_given,
291-
) -> MessageListResponse:
293+
) -> AsyncPaginator[Message, AsyncCursorList[Message]]:
292294
"""List all messages in a chat with cursor-based pagination.
293295
294296
Sorted by timestamp.
@@ -310,23 +312,25 @@ async def list(
310312
311313
timeout: Override the client-level default timeout for this request, in seconds
312314
"""
313-
return await self._get(
314-
"/v1/messages",
315+
if not chat_id:
316+
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
317+
return self._get_api_list(
318+
f"/v1/chats/{chat_id}/messages",
319+
page=AsyncCursorList[Message],
315320
options=make_request_options(
316321
extra_headers=extra_headers,
317322
extra_query=extra_query,
318323
extra_body=extra_body,
319324
timeout=timeout,
320-
query=await async_maybe_transform(
325+
query=maybe_transform(
321326
{
322-
"chat_id": chat_id,
323327
"cursor": cursor,
324328
"direction": direction,
325329
},
326330
message_list_params.MessageListParams,
327331
),
328332
),
329-
cast_to=MessageListResponse,
333+
model=Message,
330334
)
331335

332336
def search(
@@ -432,8 +436,8 @@ def search(
432436

433437
async def send(
434438
self,
439+
chat_id: str,
435440
*,
436-
chat_id: str | Omit = omit,
437441
reply_to_message_id: str | Omit = omit,
438442
text: str | Omit = omit,
439443
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -463,11 +467,12 @@ async def send(
463467
464468
timeout: Override the client-level default timeout for this request, in seconds
465469
"""
470+
if not chat_id:
471+
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
466472
return await self._post(
467-
"/v1/messages",
473+
f"/v1/chats/{chat_id}/messages",
468474
body=await async_maybe_transform(
469475
{
470-
"chat_id": chat_id,
471476
"reply_to_message_id": reply_to_message_id,
472477
"text": text,
473478
},

src/beeper_desktop_api/types/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from .client_search_params import ClientSearchParams as ClientSearchParams
2828
from .account_list_response import AccountListResponse as AccountListResponse
2929
from .contact_search_params import ContactSearchParams as ContactSearchParams
30-
from .message_list_response import MessageListResponse as MessageListResponse
3130
from .message_search_params import MessageSearchParams as MessageSearchParams
3231
from .message_send_response import MessageSendResponse as MessageSendResponse
3332
from .contact_search_response import ContactSearchResponse as ContactSearchResponse

src/beeper_desktop_api/types/contact_search_params.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22

33
from __future__ import annotations
44

5-
from typing_extensions import Required, Annotated, TypedDict
6-
7-
from .._utils import PropertyInfo
5+
from typing_extensions import Required, TypedDict
86

97
__all__ = ["ContactSearchParams"]
108

119

1210
class ContactSearchParams(TypedDict, total=False):
13-
account_id: Required[Annotated[str, PropertyInfo(alias="accountID")]]
14-
"""Account ID this resource belongs to."""
15-
1611
query: Required[str]
1712
"""Text to search users by. Network-specific behavior."""

0 commit comments

Comments
 (0)