-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Event types #289
Add Event types #289
Conversation
workos/events.py
Outdated
# TODO: This is a hack, and it's wrong. Events does not support before or order | ||
"before": None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit unclear - why do we need to define before
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our current list params typing forces it. We need to refactor but I'm kicking that can down the road to a future PR.
workos/events.py
Outdated
return WorkOsListResource( | ||
list_method=self.list_events, | ||
list_args=params, | ||
**ListPage[Event](**response).model_dump(), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh interesting. Does this work without issue for async? I may have to revisit this in my PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt it. We probably need to parametrize the list resource to use an async client. But I haven't gotten there yet
@mattgd Switching this back to draft. Need to fix some tests and there may be some real work to make the auto paging iter async |
@tribble Before you troubleshoot, I have an addition for that in my PR - let me know what you think: https://github.com/workos/workos-python/pull/288/files#diff-fd823a1e532828a74cb86c58012687832231229d0985952f4580c40ab33c3191R206-R225. |
@mattgd OK, PR ready for review. Events has enough quirks this required some changes. Will comment inline. |
@@ -153,7 +153,7 @@ def inner( | |||
http_client: Union[SyncHTTPClient, AsyncHTTPClient], | |||
data_list: list, | |||
status_code: int = 200, | |||
headers: Mapping[str, str] = None, | |||
headers: Optional[Mapping[str, str]] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should point mypy at more of our files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seemingly we should be able to open it up to all workos/
files. That's how I was running it locally. If we're having a mypy mismatch we should adjust settings to make sure it runs how we'd like in CI.
# We normalize this to 'active' in the SDK. This helper function | ||
# does this conversion to make make assertions easier. | ||
if directory["state"] == "linked": | ||
return {**directory, "state": "active"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I bit the bullet and just did this conversion instead of having two types.
@@ -73,7 +87,7 @@ def mock_group(self): | |||
|
|||
@pytest.fixture | |||
def mock_directories(self): | |||
directory_list = [MockDirectory(id=str(i)).to_dict() for i in range(20)] | |||
directory_list = [MockDirectory(id=str(i)).to_dict() for i in range(10)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our list pages default to 10 anyway
@@ -8,8 +8,9 @@ def __init__(self, id): | |||
self.object = "directory" | |||
self.id = id | |||
self.organization_id = "organization_id" | |||
self.domain = "crashlanding.com" | |||
self.name = "Ri Jeong Hyeok" | |||
self.external_key = "ext_123" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This external key is actually required. See our API ref (which isn't consistent, sigh) as well as our API JSON model and Event JSON model.
from workos.resources.base import WorkOSBaseResource | ||
|
||
|
||
class MockDirectoryActivatedPayload(WorkOSBaseResource): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was easier than mangling the existing MockDirectory
model
from workos.resources.base import WorkOSBaseResource | ||
|
||
|
||
class MockEvent(WorkOSBaseResource): | ||
def __init__(self, id): | ||
self.object = "event" | ||
self.id = id | ||
self.event = "dsync.user.created" | ||
self.data = {"id": "event_01234ABCD", "organization_id": "org_1234"} | ||
self.event = "dsync.activated" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to switch to an event that actually exists
|
||
|
||
def list_data_to_dicts(list_data: List): | ||
def list_data_to_dicts(list_data: Sequence): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use a more generic duck type?
@@ -4,7 +4,6 @@ | |||
from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient | |||
from workos.utils.pagination_order import PaginationOrder | |||
from workos.utils.request import REQUEST_METHOD_DELETE, REQUEST_METHOD_GET | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hates me rando newlines
"events": events, | ||
"limit": limit, | ||
"after": after, | ||
"organization_id": organization_id, | ||
"organization_id": organization, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now our other API methods just use organization
.
I'm conforming for consistency. Happy to have a cleanup PR sweep through and change all of those params to organization_id
return WorkOsListResource( | ||
list_method=self.list_events, | ||
list_args=params, | ||
**ListPage[Event](**response).model_dump(exclude_unset=True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to use exclude_unset=True
otherwise this would always serialize before
in the list_metadata
. I think it's because the resource can't tell the difference between ListAfterMetadata
and ListMetadata
.
The other option is to and a third generic parameter and explicitly pass in the ListMetadata
shape. But, since python type hint generics don't set support default values for generics, every list resource would have to pass in the list metadata type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized there is another option. We could have separate WorkOsForwardListResource
and WorkOsBidirectionalListResource
.
Not sure how much benefit that will bring. It would almost definitely be more code.
from workos.typing.literals import LiteralOrUntyped | ||
|
||
DirectoryState = Literal[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracted to a shared type which normalizes state
@@ -1,42 +0,0 @@ | |||
from workos.resources.base import WorkOSBaseResource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't used ¯\_(ツ)_/¯
fixed_pagination_params = cast( | ||
# Type hints consider this a mismatch because it assume the dictionary is dict[str, int] | ||
Dict[str, Union[int, str, None]], | ||
{ | ||
"limit": self.list_args["limit"], | ||
}, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type hints were a bit "too smart". Since self.list_args["limit"]
is an int
it considers the entire dictionary to be of type Dict[str, int]
.
"limit": self.list_args["limit"], | ||
}, | ||
) | ||
if "order" in self.list_args: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Order isn't a thing for events.
def convert_linked_to_active(value: Any, info: ValidationInfo) -> Any: | ||
if isinstance(value, str) and value == "linked": | ||
return "active" | ||
else: | ||
return value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙃
@@ -153,7 +153,7 @@ def inner( | |||
http_client: Union[SyncHTTPClient, AsyncHTTPClient], | |||
data_list: list, | |||
status_code: int = 200, | |||
headers: Mapping[str, str] = None, | |||
headers: Optional[Mapping[str, str]] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seemingly we should be able to open it up to all workos/
files. That's how I was running it locally. If we're having a mypy mismatch we should adjust settings to make sure it runs how we'd like in CI.
Description
Initial PR to add two event types. It's most crucial to get feedback on the approach, then we can add more types in following PRs.
Documentation
Does this require changes to the WorkOS Docs? E.g. the API Reference or code snippets need updates.
If yes, link a related docs PR and add a docs maintainer as a reviewer. Their approval is required.