[MAJC-123] Disambiguate invalid_url_error by short circuiting callback code paths for fallback ads#34
Conversation
0c04aed to
1bf5409
Compare
luc-lisi
left a comment
There was a problem hiding this comment.
Requesting on change to also include this no-op on fallback clicks as I think that might also be a source of some of our noisy logs, but otherwise looks awesome! Thank you for cleaning this up 🎉
| public observe(placement: MozAdsPlacementWithContent) { | ||
| // No need to observe fallback ads -- they are meant for an offline experience so they don't | ||
| // have dynamic impression urls from MARS, so we can't register an impression for them. | ||
| if (isFallback(placement)) return |
There was a problem hiding this comment.
I think we should also include this no-op in clicks.ts when a recordClick occurs.
The approach is great though!
There was a problem hiding this comment.
Oh great point, thank you. I will make that update
There was a problem hiding this comment.
|
@mashalifshin I don't have a ton of context into this area of the code but I've been trying to follow the verification steps outlined. I did have one question around distinguishing what the valid scenarios for which we send the report an ad url, click callback url, mpression callback url |
| placementId: placement.placementId, | ||
| }) | ||
| return | ||
| } |
There was a problem hiding this comment.
This un-nests the if-else. Before, we had the invalid_url_error handled in the else, now we check for fallback, then url validity, up front and error and exit before getting to the happy path.
| logger.info(`Impression occurred for placement: ${placementId}`, { | ||
| type: 'impressionObserver.recordImpression.viewed', | ||
| placementId: placementId, | ||
| if (!impressionUrl || !URL.canParse(impressionUrl)) { |
There was a problem hiding this comment.
Same pattern as for recordClick, this un-nests the if-else (where the invalid_url_error used to live on the else branch) and instead we'll first check for fallback, then url validity, and if we don't error and exit, proceed along the happy path.
| 'recordClick.success' | | ||
| 'recordClick.callbackResponseError' | | ||
| 'recordClick.callbackNotFoundError' | | ||
| 'recordClick.invalidCallbackError' | |
There was a problem hiding this comment.
Making this a bit more general now, and should now only be fired if we get to the time of recording the click or impression and find that the URL is not valid.
| click: 'http://example.com/click', | ||
| impression: 'http://example.com/impression', | ||
| report: 'http://example.com/report', | ||
| describe('recordClick', () => { |
There was a problem hiding this comment.
I did some refactoring in this test to unit-test-ify it more. It now just spies on the logger object and makes sure the expected logging functions are called, as opposed to "reaching into" the implementation of the logger and checking that it used the fetch/console log properly, which makes these tests a lot easier to work with.
And then in addition to this, I think there should be a logger test that verifies the logger emits the expected requests, and logs the expected things to the console, but I didn't write that as part of this PR.
Maybe we can tackle in a subsequent PR, I need to understand the expected behavior/how the logger is configured to do that.
There was a problem hiding this comment.
Had a conversation with Luc and we actually do have most of those tests already, in instrument.test.ts. So the scope of a new test here is really small, just making sure the toggle for emitting logs is working, so I added that in as part of this PR
| click: 'http://example.com/click', | ||
| impression: 'http://example.com/impression', | ||
| report: 'http://example.com/report', | ||
| click: 'https://example.com/click', |
There was a problem hiding this comment.
No substantial changes in this file, just doing http -> https find-replace for consistency/sanity.
There was a problem hiding this comment.
No longer true, we now have some new tests for the reporting flow down at the bottom
| const TEST_DEBUG_MESSAGE = 'Test debug message' | ||
|
|
||
| describe('core/logger.ts', () => { | ||
| describe('core/logger.ts DefaultLogger', () => { |
There was a problem hiding this comment.
Also just renames in this file Mock -> Spy for clarity. This is where I would have started the emitLogs testing but I backed off when I realized I was going too far down the rabbit hole.
There was a problem hiding this comment.
emitLogs testing is already in instrument.test.ts actually, hurray 🙌
| @@ -77,11 +77,11 @@ describe('iife/display.ts', () => { | |||
| format: 'billboard', | |||
| url: 'https://getpocket.com/', | |||
| callbacks: { | |||
There was a problem hiding this comment.
Just http -> https changes in this file
| @@ -38,11 +38,11 @@ describe('react/components/MozAdsPlacement.tsx', () => { | |||
| format: 'billboard', | |||
There was a problem hiding this comment.
Just http -> https changes in this file as well
| @@ -35,11 +35,11 @@ describe('react/hooks/useMozAdsPlacement.ts', () => { | |||
| format: 'billboard', | |||
There was a problem hiding this comment.
And one more, just http -> https changes in this file
@cyptm Thanks for bearing with my slow replying! Have been figuring out the tests here ... Yes, you have totally understood it correctly. After this change, we should only be sending the invalid url error if any one of those callback urls are invalid. So this change special cases the fallback ads, which are static and don't come from MARS and have empty callback urls, so now those should no longer be sending the The reporting an ad flow didn't require changes for this, since it already attempts to validate the url explicitly by constructing it, and will catch if that constructor Edited to Add: Oh actually, now that you mention it, I don't think we're skipping fallback ads in the reporting flow!! Thank you so for mentioning that! Will push up another round of changes soon |
…d urls before proceeding with callback fetch calls
…tp->https in examples
9f077e9 to
5fb174d
Compare
luc-lisi
left a comment
There was a problem hiding this comment.
Made a quick comment about the onReport callback not being called during the fallback no-op but I don't think it's being used actively on Pocket, so it is non-blocking.
Otherwise this is awesome! Thank you for all this cleanup :)
| placementId: placement.placementId, | ||
| }) | ||
| return | ||
| } |
packages/core/src/display.ts
Outdated
|
|
||
| const reason = reportSelect.value | ||
|
|
||
| if (isFallback(placement)) return |
There was a problem hiding this comment.
One thing to note is that by adding this no-op, we will not call the onReport callback passed by a user of the SDK.
I'm not entirely sure if is what we want for fallbacks though as the user might have behavior they always want to see after a report is sent from the frontend?
There was a problem hiding this comment.
Oh great callout! Is there any reason not to pull the onReport call up to right before we do the isFallback check? It's really meant only for the call to the reporting URL, not to stop the builder's callback from firing...
Looking at this code more closely, I also see some more error disambiguation that could be good here, like explicitly checking the report url for validity. And we could have different errors for when our call to the report URL fails versus when the builder's onReport callback throws ... I think I'm going to take one more iteration here before merge if that's okay
There was a problem hiding this comment.
Yeah I don't see why not to move it up!
There was a problem hiding this comment.
Okay, one more round of revisions is ready for 👀
So I actually prioritize our internal report sending work before the builder's callback, but otherwise I did essentially as described above, and added some tests.
…mbiguate errors more
luc-lisi
left a comment
There was a problem hiding this comment.
I cant remember if you were still waiting on re-approval but this is approved!
|
Thanks for all these update 😄 |
…k code paths for fallback ads (mozilla-services#34) * Don't observe fallback ads * Add isFallback guard for recording clicks, and first check for invalid urls before proceeding with callback fetch calls * Refactor click.test and add a new test for fallback early exit * WIP Logger test that verifies fetch behavior * Update impression observer tests for new behavior, and organize test cases * Organize click test a bit more * Properly mock URL functions, add some happy path impression tests, http->https in examples * Exit early and dont send report for fallback ads as well * Add a lil test to make sure we only emit logs to MARS when they have an event label * In reporting flow, call the builders callback for fallbacks, and disambiguate errors more
References
MAJC-123
Problem Statement
Currently, there are a couple situations when we send an
invalid_url_errorto MARS for our majc telemetry. We send it if there is a problem with:This works great except that we also register an
invalid_url_errorfor our fallback ads, which don't have live urls from MARS since they are a static part of the bundle.This means we can't distinguish real
invalid_url_errors from the expected behavior around fallback ads, and our code should be a bit smarter and fail out before we send this error for those ads.Proposed Changes
MozAdsPlacementWithContentas fallback ads if they have ablobimage_url as opposed to a live image url from MARS.There are a couple ways we could have handled this, definitely open to other ideas. We could still observe fallback ads but fail out before we try to make the network call. But it seemed like there wasn't much point in observing them at all if we can never report on it ... a little more efficient perhaps. So short-circuiting for fallbacks and actual invalid urls is the approach throughout.
Also, I chose not to define a first-order property on
MozAdsPlacementWithContentand rely on the fact that fallback ads are going to haveblobimage urls. Seemed like more of an internal detail and I don't want to clutter up the already beefy type.Open to reviewer thoughts on different approaches.
Verification Steps
A step by step guide of how the PR reviewer can verify that changes are working correctly.
mainbranchhttps://ads.allizom.org/v1/ads