justin_fung
11/15/2023, 12:01 AM412 invalid_code Will be returned if the code: has expired, has been used, is invalid.
I am having trouble finding where to re-authenticator my auth function. Can anyone help me?
Currently in my client.py
@property
@cached
def authenticator(self) -> afterpayAuthenticator:
"""Return a new authenticator object."""
return afterpayAuthenticator.create_for_stream(self)
christoph
11/15/2023, 12:37 AM412
somewhere in the response body and you need to handle the situation?
Are you writing a custom Authenticator class with the Meltano SDK?justin_fung
11/15/2023, 1:30 AMchristoph
11/15/2023, 2:26 AMjustin_fung
11/15/2023, 4:48 PMjustin_fung
11/15/2023, 6:47 PMedgar_ramirez_mondragon
11/15/2023, 7:11 PMexpires_in
value in the auth response? Meaning, even if expires_in
is still far in the future with a value like 86399, after 1000 requests you always have to re-authenticate?justin_fung
11/15/2023, 7:13 PMedgar_ramirez_mondragon
11/15/2023, 7:14 PMThe fieldI think it's in seconds: https://developers.afterpay.com/docs/api/online-api%2Fapi-architecture%2Fauthentication#access-token-responseis the time (in seconds) that the token is valid from when its generated. The token is not usable after this time elapses.expires_in
justin_fung
11/15/2023, 7:16 PM19:14:50
to 19:15:31
and got a total of 3100.justin_fung
11/15/2023, 7:17 PMjustin_fung
11/15/2023, 7:17 PMedgar_ramirez_mondragon
11/15/2023, 7:29 PMjustin_fung
11/15/2023, 7:30 PMjustin_fung
11/15/2023, 7:30 PMjustin_fung
11/15/2023, 7:31 PM2023-11-15T19:15:30.534958Z [info ] 2023-11-15 12:15:30,533 | INFO | singer_sdk.metrics | METRIC: {"type": "timer", "metric": "http_request_duration", "value": 0.140062, "tags": {"stream": "payments", "endpoint": "/v2/payments", "http_status_code": 412, "status": "failed"}} cmd_type=elb consumer=False name=tap-afterpay producer=True stdio=stderr string_id=tap-afterpay
2023-11-15T19:15:30.537971Z [info ] 2023-11-15 12:15:30,534 | INFO | singer_sdk.metrics | METRIC: {"type": "counter", "metric": "http_request_count", "value": 155, "tags": {"stream": "payments", "endpoint": "/v2/payments"}} cmd_type=elb consumer=False name=tap-afterpay producer=True stdio=stderr string_id=tap-afterpay
edgar_ramirez_mondragon
11/15/2023, 8:03 PMjustin_fung
11/15/2023, 8:15 PM"""afterpay Authentication."""
from __future__ import annotations
from singer_sdk.authenticators import OAuthAuthenticator, SingletonMeta
# The SingletonMeta metaclass makes your streams reuse the same authenticator instance.
# If this behaviour interferes with your use-case, you can remove the metaclass.
class afterpayAuthenticator(OAuthAuthenticator, metaclass=SingletonMeta):
"""Authenticator class for AfterPay."""
@property
def oauth_request_body(self) -> dict:
"""Define the OAuth request body for the AfterPay API."""
return {
"client_id": self.config["client_id"],
"client_secret": self.config["client_secret"],
"scope": "merchant_api_v2",
"grant_type": "client_credentials",
}
@classmethod
def create_for_stream(cls, stream) -> afterpayAuthenticator:
"""Instantiate an authenticator for a specific Singer stream."""
return cls(
stream=stream,
auth_endpoint="<https://merchant-auth.afterpay.com/v2/oauth2/token>",
)
edgar_ramirez_mondragon
11/15/2023, 8:37 PMfrom http import HTTPStatus
from singer_sdk.exceptions import RetriableAPIError
class MyStreamClass(...):
extra_retry_statuses = [HTTPStatus.PRECONDITION_FAILED, HTTPStatus.TOO_MANY_REQUESTS]
def validate_response(self, response):
# Requires re-authentication
if respone.status_code == HTTPStatus.PRECONDITION_FAILED:
self.authenticator = afterpayAuthenticator.create_for_stream(self)
super().validate_response(response)
justin_fung
11/15/2023, 9:25 PMedgar_ramirez_mondragon
11/15/2023, 9:25 PMjustin_fung
11/15/2023, 9:29 PM2023-11-15T21:28:35.076002Z [info ] AttributeError: can't set attribute 'authenticator' cmd_type=elb consumer=False name=tap-afterpay producer=True stdio=stderr string_id=tap-afterpay
justin_fung
11/15/2023, 9:29 PMjustin_fung
11/15/2023, 9:30 PMedgar_ramirez_mondragon
11/15/2023, 9:46 PM@cached
is ok but it kinda gets in the way. Can you use functools.cached_property
instead?
from http import HTTPStatus
from functools import cached_property
from singer_sdk.exceptions import RetriableAPIError
class MyStreamClass(...):
extra_retry_statuses = [HTTPStatus.PRECONDITION_FAILED, HTTPStatus.TOO_MANY_REQUESTS]
@cached_property
def authenticator(self) -> afterpayAuthenticator:
"""Return a new authenticator object."""
return afterpayAuthenticator.create_for_stream(self)
def validate_response(self, response):
# Requires re-authentication
if respone.status_code == HTTPStatus.PRECONDITION_FAILED:
self.authenticator = afterpayAuthenticator.create_for_stream(self)
super().validate_response(response)
Note that this will require Python 3.8+justin_fung
11/15/2023, 9:49 PMjustin_fung
11/15/2023, 9:50 PM2023-11-15T21:48:48.717633Z [info ] 2023-11-15 14:48:48,717 | ERROR | root | Backing off 16.35 seconds after 4 tries calling function <bound method RESTStream._request of <tap_afterpay.streams.PaymentsStream object at 0x0000019A92D0DA80>> with args (<PreparedRequest [GET]>, None) and kwargs {} cmd_type=elb consumer=False name=tap-afterpay producer=True stdio=stderr string_id=tap-afterpay
But went back to normal.justin_fung
11/15/2023, 9:56 PMjustin_fung
11/15/2023, 10:02 PM2023-11-15T21:48:48.717633Z [info ] 2023-11-15 14:48:48,717 | ERROR | root | Backing off 16.35 seconds after 4 tries calling function <bound method RESTStream._request of <tap_afterpay.streams.PaymentsStream object at 0x0000019A92D0DA80>> with args (<PreparedRequest [GET]>, None) and kwargs {} cmd_type=elb consumer=False name=tap-afterpay producer=True stdio=stderr string_id=tap-afterpay
edgar_ramirez_mondragon
11/15/2023, 10:03 PMjustin_fung
11/15/2023, 10:20 PMjustin_fung
11/15/2023, 10:32 PMjustin_fung
11/15/2023, 10:37 PMedgar_ramirez_mondragon
11/16/2023, 12:00 AM412
is encountered.justin_fung
11/24/2023, 2:58 PMedgar_ramirez_mondragon
11/24/2023, 8:31 PMjustin_fung
11/27/2023, 6:40 AMjustin_fung
11/27/2023, 6:40 AMedgar_ramirez_mondragon
11/28/2023, 12:31 AMjustin_fung
11/28/2023, 12:39 AM