1+ import json
12from typing import Any , Iterable , Optional , override
23
34from pardner .exceptions import UnsupportedRequestException
@@ -12,6 +13,7 @@ class TumblrTransferService(BaseTransferService):
1213 See API documentation: https://www.tumblr.com/docs/en/api/v2
1314 """
1415
16+ primary_blog_id : str | None = None
1517 _authorization_url = 'https://www.tumblr.com/oauth2/authorize'
1618 _base_url = 'https://api.tumblr.com/v2/'
1719 _token_url = 'https://api.tumblr.com/v2/oauth2/token'
@@ -23,7 +25,20 @@ def __init__(
2325 redirect_uri : str ,
2426 state : Optional [str ] = None ,
2527 verticals : set [Vertical ] = set (),
28+ primary_blog_id : str | None = None ,
2629 ) -> None :
30+ """
31+ Creates an instance of ``TumblrTransferService``.
32+
33+ :param client_id: Client identifier given by the OAuth provider upon registration.
34+ :param client_secret: The ``client_secret`` paired to the ``client_id``.
35+ :param redirect_uri: The registered callback URI.
36+ :param state: State string used to prevent CSRF and identify flow.
37+ :param verticals: The :class:`Vertical`s for which the transfer service has
38+ appropriate scope to fetch.
39+ :param primary_blog_id: Optionally, the primary blog ID of the data owner (the
40+ user being authorized).
41+ """
2742 super ().__init__ (
2843 service_name = 'Tumblr' ,
2944 client_id = client_id ,
@@ -33,6 +48,7 @@ def __init__(
3348 supported_verticals = {SocialPostingVertical },
3449 verticals = verticals ,
3550 )
51+ self .primary_blog_id = primary_blog_id
3652
3753 @override
3854 def scope_for_verticals (self , verticals : Iterable [Vertical ]) -> set [str ]:
@@ -48,6 +64,40 @@ def fetch_token(
4864 ) -> dict [str , Any ]:
4965 return super ().fetch_token (code , authorization_response , include_client_id )
5066
67+ def fetch_primary_blog_id (self ) -> str :
68+ """
69+ Fetches the primary blog ID from the data owner, which will be used as the
70+ ``data_owner_id`` in the vertical model objects. If the ``primary_blog_id``
71+ attribute on this class is already set, the method does not make a new request.
72+
73+ Note: "PrimaryBlogId" is not a vertical. This is used purely as a unique
74+ identifier for the user, since Tumblr doesn't provide one by default.
75+
76+ :returns: the primary blog id.
77+
78+ :raises: :class:`ValueError`: if the primary blog ID could not be extracted from
79+ the response.
80+ """
81+ if self .primary_blog_id :
82+ return self .primary_blog_id
83+ user_info = self ._get_resource_from_path ('user/info' ).json ().get ('response' , {})
84+ for blog_info in user_info .get ('user' , {}).get ('blogs' , []):
85+ if (
86+ isinstance (blog_info , dict )
87+ and blog_info .get ('primary' )
88+ and 'uuid' in blog_info
89+ and isinstance (blog_info ['uuid' ], str )
90+ ):
91+ self .primary_blog_id = blog_info ['uuid' ]
92+ return blog_info ['uuid' ]
93+
94+ raise ValueError (
95+ 'Failed to fetch primary blog id. Either manually set the _primary_blog_id '
96+ 'attribute or verify all the client credentials '
97+ 'and permissions are correct. Response from Tumblr: '
98+ f'{ json .dumps (user_info , indent = 2 )} '
99+ )
100+
51101 def fetch_social_posting_vertical (
52102 self ,
53103 request_params : dict [str , Any ] = {},
0 commit comments