-
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 typing and async support for SSO #291
Add typing and async support for SSO #291
Conversation
55dbdaf
to
d0130fe
Compare
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.
See comments inline. Creating an HTTP client to generate a URL seems like such a smell. Is there a better way?
@pytest.fixture | ||
def mock_profile(self): | ||
return { | ||
"object": "profile", |
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.
Kind of amazing how invalid our test fixtures were.
tests/test_sso.py
Outdated
connection_list = [MockConnection(id=str(i)).to_dict() for i in range(10)] | ||
|
||
return { | ||
"object": "list", |
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 did create a little list_response_of
helper to help with this repetition
tests/test_sso.py
Outdated
self.customer_domain = "workos.com" | ||
self.login_hint = "[email protected]" | ||
self.redirect_uri = "https://localhost/auth/callback" | ||
self.state = json.dumps({"things": "with_stuff"}) |
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.
Should this correspond to a Connection state
?
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.
Given tests are passing, it makes me wonder how (or if) this is used.
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 is actually the authorization state (values a dev would stuff into a authorization URL. I'll rename for clarity.
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.
Ah. Damn you state
and how general you are.
tests/test_sso.py
Outdated
connections = self.sso.list_connections() | ||
all_connections = [] | ||
|
||
def test_delete_connection(self, setup_with_client_id, mock_raw_request_method): | ||
mock_raw_request_method( | ||
"delete", | ||
"No Content", | ||
204, | ||
headers={"content-type": "text/plain; charset=utf-8"}, | ||
for connection in connections.auto_paging_iter(): | ||
all_connections.append(connection) |
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.
We repeat something like this for every auto paging test. I wonder if it's worth having some helper. (Not in this 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.
Yeah, worthy of a helper for sure.
tests/test_sso.py
Outdated
request_params = request_kwargs["params"] | ||
assert request_params["connection_type"] == "GenericSAML" |
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.
For tests like this I wonder if we should explicitly assert all the params, like assert {"connection_type": "GenericSaml", "limit": 10} == request_kwargs["params"]
or something similar. We know exactly how we're calling list_connections
. it seems like we should know exactly how that translates into the HTTP request.
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.
That's a great idea. Updating.
|
||
params = { | ||
"connection_type": connection_type.value if connection_type else None, | ||
"connection_type": connection_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.
We are not consistent about when we always add a key (even if it's None
) or only add the key to the params if it's specified. I'm pretty sure I'm guilty of this as well. We should pick a style and go with it.
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.
... and I realized, if we do want to omit missing keys, I wonder if it's easier to come full circle and define our params as BaseModel
s.
Then we could just....
params = ConnectionParams(connection_type=connection_type)
# when it's time to use the params
Request(params=ConnectionParams.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.
Utilizing BaseModel could work. Also worth noting the HTTP client has a helper that removes all None
values from query params before sending the request.
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. In that case I guess we don't need to care.
workos/sso.py
Outdated
return WorkOsListResource( | ||
list_method=self.list_connections, | ||
list_args=params, | ||
**ListPage[Connection](**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 explicitly added exclude_unset
to the events endpoint because I didn't want to serialize the before
key, which does not exist in the event list metadata.
Do you think we should extend this to all list endpoints? It might be moot, the API may not omit any fields.
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.
Oops, nope. Just a copy-pasta. Removing!
workos/sso.py
Outdated
"domain": domain, | ||
"organization_id": organization_id, | ||
"limit": limit, | ||
"limit": limit or DEFAULT_RESPONSE_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.
Same here, didn't this get a value above?
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.
Ah yep, now that I have Pylance working I can see this is unnecessary.
ConnectionType = Literal[ | ||
"ADFSSAML", | ||
"AdpOidc", | ||
"AppleOAuth", | ||
"Auth0SAML", | ||
"AzureSAML", | ||
"CasSAML", | ||
"CloudflareSAML", | ||
"ClassLinkSAML", | ||
"CyberArkSAML", | ||
"DuoSAML", | ||
"GenericOIDC", | ||
"GenericSAML", | ||
"GitHubOAuth", | ||
"GoogleOAuth", | ||
"GoogleSAML", | ||
"JumpCloudSAML", | ||
"KeycloakSAML", | ||
"LastPassSAML", | ||
"LoginGovOidc", | ||
"MagicLink", | ||
"MicrosoftOAuth", | ||
"MiniOrangeSAML", | ||
"NetIqSAML", | ||
"OktaSAML", | ||
"OneLoginSAML", | ||
"OracleSAML", | ||
"PingFederateSAML", | ||
"PingOneSAML", | ||
"RipplingSAML", | ||
"SalesforceSAML", | ||
"ShibbolethGenericSAML", | ||
"ShibbolethSAML", | ||
"SimpleSamlPhpSAML", | ||
"VMwareSAML", | ||
] |
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.
The API types also include pending
and Auth0Migration
types.
I think whoever owns SSO (is that us? LOL) could give us guidance around whether it's safe to omit these.
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.
Noticed that also. Let me ask.
workos/utils/http_client.py
Outdated
@@ -172,3 +172,26 @@ async def request( | |||
) | |||
response = await self._client.request(**prepared_request_parameters) | |||
return self._handle_response(response) | |||
|
|||
|
|||
class FakeHTTPClient(SyncHTTPClient): |
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.
🙈
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.
Description
Add typing and async support for SSO.
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.