Skip to content

Conversation

@NHNguyen94
Copy link

Tell us what you do here

  • Provide the fix to support authentication in Matomo 5 (via POST, not GET as in Matomo 4)

Short description

Related Issues

Additional Context

@anuunchin anuunchin self-assigned this Oct 30, 2025
Copy link
Collaborator

@anuunchin anuunchin left a comment

Choose a reason for hiding this comment

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

Pretty cool! Left a few small questions/comments here and there. Did you write local tests for the matomo 5? Feel free to add them to the PR as well :)

"""

def __init__(self, api_token: str, url: str) -> None:
def __init__(self, api_token: str, url: str, call_method: str = "GET") -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Literal["GET", "POST"] = "GET" might be a better (narrower) option

Copy link
Author

Choose a reason for hiding this comment

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

Fixed


self.base_url = url
self.auth_token = api_token
if call_method.upper() not in ["GET", "POST"]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we use Literal, we can drop the .upper() part

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

Args:
params (DictStrAny): Parameters for the API request.
base_params (DictStrAny): Parameters for the API request.
detailed_params (DictStrAny): Detailed parameters for the API request.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would make sense to explain why we separate the two here (i.e. Matomo 5 requires detailed params)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm thinking that detailed_params (and variables named the same way) is a bit vague, would there be a better option?

Copy link
Author

Choose a reason for hiding this comment

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

Already added the explanation, naming convention is not really my expertise... actually I felt something off with this name too, but could not think of any better name, please feel free to recommend one

)
else:
headers = {"Content-type": "application/json"}
final_params = base_params.copy()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wondering if there's a reason to copy the base params, maybe it's cleaner to simply

params = {**base_params, **detailed_params}
response = client.get(url=url, headers=headers, params=params)

Copy link
Author

Choose a reason for hiding this comment

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

Fixed

url (str): URL of the Matomo website.
call_method (str): HTTP method for API calls, related to authentication,
either "GET" or "POST". Default is "GET" to continue the support for Matomo 4 and below,
for Matomo 5, "POST" is recommended
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would be great if the thing with Only allow secure requests is mentioned here

Copy link
Author

Choose a reason for hiding this comment

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

Added further explanation

Comment on lines +146 to +162

while True:
detailed_params = {
"urls[0]": f"method={method}&idSite={site_id}&filter_limit={rows_per_page}&filter_offset={filter_offset}",
"token_auth": self.auth_token,
}
detailed_params.update(extra_params)
response_data = self._request(
base_params=base_params, detailed_params=detailed_params
)
if not response_data or not isinstance(response_data, list):
break
batch = response_data[0] if len(response_data) > 0 else []
if not batch:
break
yield batch
filter_offset += len(batch)
Copy link
Collaborator

Choose a reason for hiding this comment

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

wondering if keeping the previous flow an option here, since it was lightly easier to read

Copy link
Author

Choose a reason for hiding this comment

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

During my test, the old flow did not work

@NHNguyen94
Copy link
Author

Pushed the new changes, already run "make lint", all passed

For the local tests, actually you must have a Matomo account, and in my case, I integrate these changes into my full pipeline, so short answer: I do not have any single unit test to test this single PR

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.

Support for Matomo 5 authentication

3 participants