Skip to content

Conversation

@aborem
Copy link
Collaborator

@aborem aborem commented Jul 25, 2025

Closes #33

I think this may be a case where we don't want to re-export these functions in the top-level init file because we'd want to keep the from pardner.tumblr import construct_authorization_url (in response to #34 (comment)). Either that or we could rename these to include the word "Tumblr", or put them in a dummy class or something!

from typing import Any, Iterable, Optional

from pardner.services import BaseTransferService
from pardner.stateless.tumblr import URLs
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might get confusing if stateless/ and services/ are importing form each other, but I think it's fine as long as only services/ is pulling from stateless/ and not vice-versa.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it that makes sense

@aborem aborem requested a review from lisad July 25, 2025 15:37
:returns: the authorization URL and state, respectively.
"""
return generic_construct_authorization_url(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do a lot of these services wouldn't it be clean to use class inheritance to automatically inherit the construct_authorization_url, and then wrap it and call super()... if the method even needs to be overridden?

This is just meant as food for thought, one often wants to do things 3 times before abstracting or generalizing... we might want to see how the data methods interact before rearchictecting

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you're suggesting is more or less how I set things up in the "stateful" model (e.g., BaseTransferService.fetch_token and TumblrTransferService.fetch_token).

Maybe it's not worth creating stateless versions of those methods after all and just use the classes + methods that already exists for the stateless case as well. If we are using classes even in the stateless mode, the work is pretty much already done (see below)! The reason I was hesitant to use classes for the stateless case is that the ___TransferService object would be used just one time to call a method on it. But now that I'm thinking about it, we could reuse that same object after getting back the token to make the transfer requests.

What I propose: revert #34, close this PR, and use the existing classes (BaseTransferService and TumblrTransferService) for the stateless use case. It reduces code duplication and achieves the same thing in a slightly different way as the functions in this PR and #34 .

UX with classes

initiate oauth2

from pardner.services import TumblrTransferService
tumblr = TumblrTransferService('client_id', 'client_secret', 'https://redirect.com', [Vertical.FeedPost])
auth_url, state = tumblr.authorization_url()
# forward user to auth_url

callback url

from pardner.services import TumblrTransferService
# need to create new instance because the other one is in a completely different scope
tumblr = TumblrTransferService('client_id', 'client_secret', 'https://redirect.com', [Vertical.FeedPost])
token = tumblr.authorization_url(code = '39040239402')

UX without classes

initiate oauth2

from pardner.stateless.tumblr import construct_authorization_url
auth_url, state = construct_authorization_url('client_id', 'https://redirect.com', scope = {'base'))
# forward user to auth_url

callback url

from pardner.stateless.tumblr import fetch_token
token = fetch_token('client_id', 'https://redirect.com', client_secret = 'client_secret', code = '39040239402')

Copy link
Member

@lisad lisad Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is very good and I love the thinking - but also I think about continuing it just a little further, and calling the methods to fetch comments, or fetch block list

Editing to pull the tumblr context out of storage since this gets triggered AFTER initial setup... and I realize I might not be thinking enough about what parts of this happen in different segments of the process separated by different triggers and server contexts. We might not want the OAuth setup that gets triggered by the first click of the "donate my data" button, to be the same object that gets triggered when the callback URL receives a go-ahead token.

tumblr = TumblrTransferService(token=TokenStorage.get(username=username))
tumblr.fetch_block_list()
tumblr.fetch_comments(since=timezone.now() - relativedelta(days=1))
tumblr.fetch_user_profile()

vs

TumblrTransferService.fetch_block_list(token, username, etc)
TumblrTransferService.fetch_comments(token, username, since=etc)
TumblrTransferService.fetch_user_profile(...)

Copy link
Collaborator Author

@aborem aborem Jul 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're on the same page about definiting the data-specific methods; what you've laid out is pretty much exactly what I was thinking of doing!

So in addition to defining traditional methods that can be called on instances of the TumblrTransferService class, I'll also define static class methods that do pretty much the same thing but without using any instance attributes. → I don't think this is necessary, it would involve some strange antipatterns (like writing the same method twice: as a classmethod and a normal method).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create stateless OAuth functions for Tumblr

3 participants