-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat: Implements OAuth association API resource #286
Conversation
☂️ Python Coverage
Overall Coverage
New Files
Modified Files
|
] | ||
|
||
|
||
def update(self, integration_guid: None | 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.
@dbkegley @tdstein would love thoughts on the best way to represent this API interaction through the SDK. I don't think this is precisely what we want, but its a little ambiguous.
The actual API endpoint accepts a list of json objects - this is designed with future flexibility in mind, but for now, the only valid inputs are:
[{"oauth_integration_guid": }] to associate a content item with an integration
[] to dissociate the content item with all integrations
Given that currently you can only set one association per content item, I think it makes sense to support a function signature that accepts a single integration guid outside of a list. This breaks the semantics of PUTting an empty list to dissociate the content from integrations, though.
I put None
in as a placeholder for now to facilitate discussion, but I don't really like it all that much. I'm not sure what a better alternative would be.
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.
It feels like the correct expression is content.associations.create(integration_guid)
, which should return a simple object that maintains the integration_guid and provides a delete
method. The delete method would need to remove the association from the list.
class Association(Resource):
def delete(self) -> None:
# call `v1/content/{self.content_guid}/oauth/integrations/association` to remove the association.
fwiw, here is how I am thinking about the REST API implementation in my head, even though I know this doesn't represent the actual implementation.
Create an association
POST .../oauth/integrations/associations
{
'integration_guid': 'whatever..."
}
Response:
{
'guid': 'the-association-guid',
'integration_guid': 'whatever...',
'content_guid': 'the-content-guid'
}
Delete an association
DELETE .../the-content-guid/oauth/integrations/associations/the-association-guid
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.
in the current API implementation, you can't create / delete singular oauth associations - there aren't public CRUD operations that let you use an association in isolation. So I don't think your proposed pattern would be possible.
You can only PUT a list of oauth integration guids to the<content_guid>oauth/integrations/associations
endpoint - any PUTs completely wipe the current set of associations and replace it with a new set of associations.
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 see. When support for multiple associations is added, will the PUT operation still be an overwrite? If so, how should multiple people coordinate the modification when they each want to add a single association to the list simultaneously?
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.
its a good question. I don't know if we have an answer to this - currently, the asnwer is "whoever PUTs last wins."
Really it should only be publishers / admins who use this endpoint to interact with content that they own, so I wouldn't expect there to be many use cases where there's parallel requests coming from many clients. We really built the API with the expected client primarily being the dashboard.
src/posit/connect/content.py
Outdated
@@ -27,6 +29,13 @@ def __getitem__(self, key: Any) -> Any: | |||
return ContentItemOwner(params=self.params, **v) | |||
return v | |||
|
|||
@property | |||
def oauth_associations(self) -> ContentItemAssociations: |
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 this should be expressed as content.oauth.associations
?
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 like that alot better, yeah. Would the oauth property just be a passthrough class that we hang the associations resource off of?
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.
Yep, that sounds like the right approach.
Test NotesHere's a test script illustrating / manually validating the behavior of the associations resources:
|
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 looks great! I have a few minor nits on exception handling, but I'm ok with merging the pull request as is or with changes.
src/posit/connect/content.py
Outdated
if "guid" not in self: | ||
raise ValueError("ContentItemOAuth requires content guid") |
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 think you can remove this. If 'guid' is missing, we have bigger issues. And I believe the default KeyError thrown by self['guid'] if 'guid' is missing is sufficient.
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.
cool, yep - makes sense. I was getting type checking errors when we were using property-based fields because the guids were str|None
types, but the change to a self['guid']
lookup removes that issue entirely.
I'll make that change and then merge when CI passes.
if "guid" not in self: | ||
raise ValueError( | ||
"IntegrationAssociations requires integration guid" | ||
) |
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 as above.
Adds support for requesting and updating oauth associations through the Connect API.