Hanroux Vos
11/07/2024, 2:14 PM:{
"customers": [
{
"customerId": "123"
},
{
"customerId": "234"
}
],
"paging": {
"totalCount": 15,
"totalPages": 4,
"currentPage": 1
}
}
visch
11/07/2024, 3:50 PM/api?page=2
/api?page=3
/api?page=4
currentpage == totalPage , doneEdgar Ramírez (Arch.dev)
11/07/2024, 4:07 PMpage
key or similar in the return value of get_url_params
.Hanroux Vos
12/06/2024, 1:32 PMclass LoyaltyPaginator(BasePageNumberPaginator):
def get_next(self, response):
data = response.json()
totp = data['totalPages']
curp = data['currentPage']
modpg = curp%(totp + 1)
page = modpg + 1 if modpg < totp else None
return page
I am following https://sdk.meltano.com/en/v0.25.0/guides/pagination-classes.html, but I am getting an error:
TypeError: BaseAPIPaginator.__init__() missing 1 required positional argument: 'start_value'
any insight would be helpful thanks 🙏Reuben (Matatika)
12/06/2024, 2:37 PMLoyaltyPaginator
somewhere - you will need to pass the starting page number
client.py
def get_new_paginator(self):
return LoyaltyPaginator(0)
or implement __init__
for LoyaltyPaginator
class LoyaltyPaginator(BasePageNumberPaginator):
def __init__(self):
return super().__init__(0)
...
client.py
def get_new_paginator(self):
return LoyaltyPaginator()
Hanroux Vos
12/06/2024, 2:46 PMKeyError: 'totalPages'
This is how the paging body looks like:
:{
"customers": [
{
"customerId": "123"
},
{
"customerId": "234"
}
],
"paging": {
"totalCount": 15,
"totalPages": 4,
"currentPage": 1
}
}
Any idea on what I might be doing wrong here? 🤔Reuben (Matatika)
12/06/2024, 2:51 PMclass LoyaltyPaginator(BasePageNumberPaginator):
def get_next(self, response):
data = response.json()["paging"] # <---
totp = data['totalPages']
curp = data['currentPage']
modpg = curp%(totp + 1)
page = modpg + 1 if modpg < totp else None
return page
Hanroux Vos
12/06/2024, 3:15 PMRuntimeError: Loop detected in pagination. Pagination token 2 is identical to prior token.
,but when I test the paging algorithm it runs fine 🤔
thank you for all the help and sorry for all the questions 🙏Reuben (Matatika)
12/06/2024, 3:48 PMclass LoyaltyPaginator(BasePageNumberPaginator):
def get_next(self, response):
data = response.json()["paging"]
totp = data['totalPages']
curp = data['currentPage']
page = curp + 1 if curp < totp else None
return page
I think modpg = curp%(totp + 1)
was over-complicating things.Hanroux Vos
12/06/2024, 5:03 PMRuntimeError: Loop detected in pagination. Pagination token 2 is identical to prior token.
I've modified the paginator class to look like this:
class LoyaltyPaginator(BasePageNumberPaginator):
def __init__(self):
return super().__init__(0)
def get_next(self, response):
data = response.json()["paging"]
totp = data['totalPages']
curp = data['currentPage']
page = curp + 1 if curp < totp else None
return page
Edgar Ramírez (Arch.dev)
12/06/2024, 6:56 PMHanroux Vos
12/06/2024, 11:09 PM@cached_property
def authenticator(self):
return LoyaltyPaginator()
or is it fine otherwise as long as I added:
def __init__(self):
return super().__init__(0)
Into the paginator class like above?
This is me returning the paginator in get_new_paginator:
def get_new_paginator(self) -> LoyaltyPaginator:
return LoyaltyPaginator()
and def get_url_params is pretty much the same. I haven't made any changes. 🤔Edgar Ramírez (Arch.dev)
12/06/2024, 11:19 PMReuben (Matatika)
12/06/2024, 11:20 PMauthenticator
property implementation.Reuben (Matatika)
12/06/2024, 11:23 PMparams["page"] = next_page_token
in get_url_params
?Hanroux Vos
12/07/2024, 12:52 PMdef get_url_params(
self,
context: dict | None, # noqa: ARG002
next_page_token: Any | None, # noqa: ANN401
) -> dict[str, Any]:
params: dict = {}
if next_page_token:
params["page"] = next_page_token
if self.replication_key:
params["sort"] = "asc"
params["order_by"] = self.replication_key
starting_date = self.get_starting_timestamp(context)
if starting_date:
params["after"] = starting_date.isoformat()
if next_page_token is not None:
params["page"] = next_page_token
<http://self.logger.info|self.logger.info>("QUERY PARAMS: %s", params)
return params
yield from extract_jsonpath(self.records_jsonpath, input=response.json())
I haven't really made any changes in get_url_params. I didn't make any changes to the authenticator property. I only added the innit def to the paginator 👍 No errors or warnings leading up to the runtime error only errors I get is :
raise RuntimeError(msg)
RuntimeError: Loop detected in pagination. Pagination token 2 is identical to prior token.
Reuben (Matatika)
12/09/2024, 4:00 PMpage
is the correct param? I imagine this is what might be happening:
1. Request to /api
2. Response containing paging.currentPage
value of 1
3. next_page_token
resolved as 2
4. Request to /api?page=2
5. API doesn't respect page
param, so it is ignored - effectively the same request to /api
as in step 1
6. Response containing paging.currentPage
value of 1
7. next_page_token
resolved as 2
8. RuntimeError: Loop detected in pagination. Pagination token 2 is identical to prior token.
Hanroux Vos
12/11/2024, 11:11 AMHanroux Vos
12/11/2024, 11:15 AMHanroux Vos
12/11/2024, 11:28 AMReuben (Matatika)
12/12/2024, 12:33 AMdef validate_response(self, response):
<http://self.logger.info|self.logger.info>("Paging: %s", response.json()["paging"])
super().validate_response(response)
Failing that, you should try the same but directly in LoyaltyPaginator.get_next
- since there is no logger
available, you will have to configure one yourself, or you can do what I do sometimes:
def get_next(self, response):
data = response.json()["paging"]
import sys
print(data, file=sys.stderr)
totp = data['totalPages']
curp = data['currentPage']
page = curp + 1 if curp < totp else None
return page
Otherwise, look into a proper debug setup with breakpoints in your IDE: https://sdk.meltano.com/en/latest/dev_guide.html#ide-tips
For example: https://github.com/Matatika/tap-spotify/blob/a6da36f062b32d0f6847cd3eb01b300e35a19e59/.vscode/launch.jsonHanroux Vos
01/10/2025, 9:14 AMyield from extract_jsonpath(self.records_jsonpath, input=response.json()
Reuben (Matatika)
01/10/2025, 9:26 AM