-
Notifications
You must be signed in to change notification settings - Fork 293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add GET:report datapoint for GA4. #6379
Add GET:report datapoint for GA4. #6379
Conversation
The metric `name` is required. If `expression` is provided, `name` works as an alias. Otherwise `name` must be a valid metric name.
Instead these will be enabled on a per-item basis as they are used in the plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @techanvil ! I really appreciate your thorough approach on this one, especially around the tests 👍 I left a few comments, mostly around the tests as well. Overall, everything is quite solid, there are a few points that are important to address and several comments are related to the same thing just given in-place for context.
Great work!
public function test_report__metric_validation() { | ||
// Enable some metrics. | ||
add_filter( | ||
'googlesitekit_shareable_analytics_4_metrics', | ||
function() { | ||
return array( | ||
'sessions', | ||
'totalUsers', | ||
); | ||
} | ||
); | ||
|
||
$property_id = '123456789'; | ||
|
||
$request_handler_calls = array(); | ||
|
||
$analytics = $this->setup_report( $property_id, $request_handler_calls, true ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also test the relevant behavior for authenticated admins around validation –that non-shareable metrics+dimensions can be requested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following our discussion I've taken an approach using a @dataProvider
to provide an access token (or not) for each test, with a corresponding call to authenticate the user; I am not entirely sure about it, please take a look and see what you think...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @techanvil – there were a few more things I noticed to address here but this is looking great. Almost there I think 👍
includes/Modules/Analytics_4.php
Outdated
} | ||
|
||
$option = $this->get_settings()->get(); | ||
$property_id = self::normalize_property_id( $option['propertyID'] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If
propertyID
is empty, we should probably return an error here; as it would be guaranteed to fail - The "normalized version" of the property ID is API specific, so we should probably apply that right before the API call, or even inline it otherwise it might be used somewhere else in the method assuming it is the setting value (which would be a reasonable assumption to make)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I've updated to return an error when propertyID
is empty and moved the normalization to the point of use.
I added a test for the empty propertyID
scenario. I've also refactored the validation tests slightly as I realised they don't actually need the fake HTTP client as they don't get as far as making a request. As a result I noticed another test scenario to cover and added a test for insufficient permissions.
includes/Modules/Analytics_4.php
Outdated
} else { | ||
$date_range = $data['dateRange'] ?: 'last-28-days'; | ||
$date_ranges[] = $this->parse_date_range( $date_range, $data['compareDateRanges'] ? 2 : 1 ); | ||
|
||
// When using multiple date ranges, it changes the structure of the response: | ||
// Aggregate properties (minimum, maximum, totals) will have an entry per date range. | ||
// The rows property will have additional row entries for each date range. | ||
if ( ! empty( $data['multiDateRange'] ) ) { | ||
$date_ranges[] = $this->parse_date_range( $date_range, 1, 1, true ); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK we don't use dateRange
, compareDateRanges
, or multiDateRange
report args anymore – these predate explicit start/end and comparison date range dates which we should be using exclusively for quite a while now. I see these still exist in other modules but given that they're legacy/deprecated, we shouldn't introduce them here. There's probably no harm in leaving these in the main Analytics module for now since the whole thing is going way eventually anyways, but we should open an issue to remove these from the other modules if we don't already have one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I've removed dateRange
, compareDateRanges
, and multiDateRange
.
I have, though, kept the default case where no date params are provided at all, in which case it defaults to the last 28 days. I've also added a test case as this scenario was missed in the coverage. If you'd prefer to remove this default entirely, please let me know.
Have also created a followup issue to remove these legacy params from the codebase: #6395
protected function days_ago_date_string( $days_ago ) { | ||
return gmdate( 'Y-m-d', strtotime( $days_ago . ' days ago' ) ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably isn't needed as we shouldn't need to do any date range ID -> dates conversion (as we have in some other modules) as mentioned before. This also makes the test slightly fragile to dates computing differently if the test were to run in a way that the date changed during the run that could cause a failure. Unlikely, but we have seen this kind of thing before in other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've ended up leaving this in, due to the new default date range test case mentioned above.
I started looking for solutions for mocking the date in PHPUnit but didn't want to go too far down a rabbit hole.
Given the low probability of hitting the edge case you mentioned on a given test run, I'd suggest leaving it in for now and following up with a separate issue to provide a date mocking solution and rewrite the test, as this would also provide useful functionality for other tests. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that sounds good to me. It's very unlikely that the test would fail for this reason, but I agree we should preserve the default date range 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, cool. I've created the issue #6407 to follow up on mocking the date in PHPUnit tests.
1140866
to
93a6b27
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patience on this @techanvil – this is very close, just a few final details to touch up.
includes/Modules/Analytics_4.php
Outdated
$option = $this->get_settings()->get(); | ||
|
||
if ( empty( $option['propertyID'] ) ) { | ||
return new WP_Error( 'missing_required_setting', __( 'No connected Google Analytics 4 property ID.', 'google-site-kit' ), array( 'status' => 400 ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit of a nitpick here but a 400 response would indicate that the request was malformed in some way that it should not be retried. Since this condition is based on internal state, I think a 500 error would be more appropriate, and this probably makes sense to go after the 4xx errors (i.e. once we determine the request was made correctly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot! I've done this, and also updated the metric/dimension validation errors to return a 400 rather than the current default of 500. Also updated the tests to check for the status code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! As mentioned in #6379 (comment) I don't think we should change the status of the errors for the DS-related validation because that is also dependent on internal state rather than an improperly made request. I've gone ahead and reverted this part.
protected function days_ago_date_string( $days_ago ) { | ||
return gmdate( 'Y-m-d', strtotime( $days_ago . ' days ago' ) ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that sounds good to me. It's very unlikely that the test would fail for this reason, but I agree we should preserve the default date range 👍
Verify status codes in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @techanvil ! Just one thing related to one of the extra changes you made. I'll update this myself to avoid another round here as it should be a trivial change to make, but great job here 👏
includes/Modules/Analytics_4.php
Outdated
@@ -926,7 +922,8 @@ function ( $dimension_def ) { | |||
} catch ( Invalid_Report_Dimensions_Exception $exception ) { | |||
return new WP_Error( | |||
'invalid_analytics_4_report_dimensions', | |||
$exception->getMessage() | |||
$exception->getMessage(), | |||
array( 'status' => 400 ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see why you used the status here as well but let's not do that because it would imply we should make the same changes in the other modules that also implement this check. Aside from that, I think the default status is okay here because the client is not making an invalid request – the validation is applied due to internal state/conditions related to dashboard sharing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries and thanks for changing it back 👍
); | ||
|
||
$request = new Google_Service_AnalyticsData_RunReportRequest(); | ||
$request->setProperty( self::normalize_property_id( $property_id ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be unnecessary but we can look at it in a separate issue as I don't think it's hurting anything here. The property ID is provided as a path parameter in the API request, but the object we're building here is essentially the body of that request so it's being duplicated. It's also not mentioned in the schema for the request body, but is specifiable in the API explorer 😄 🤷♂️
Perhaps this is a leftover detail from an earlier version of the API or maybe there for consistency with the batch method (although I think a batch is still limited to the same property). See https://github.com/googleapis/google-api-php-client-services/blob/ff7e67045c666d292e07e0898ce63be458592745/src/AnalyticsData/Resource/Properties.php#L199-L205
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, possibly unnecessary, worth taking another look at 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Thanks @techanvil 💪
Summary
Addresses issue:
GET:report
datapoint for GA4 #6172Relevant technical choices
Rather than adding test cases for the new protected methods
create_analytics_site_data_request
andparse_reporting_orderby
, which are protected and therefore not part of the public interface and should ideally not be tested directly in a unit test, I have added tests which get the report and check the report parameters have been correctly generated. These are more complete integration-style tests that provides better coverage than isolating these protected methods.PR Author Checklist
Do not alter or remove anything below. The following sections will be managed by moderators only.
Code Reviewer Checklist
Merge Reviewer Checklist