LG-7582: Add proofing components to IdV analytics events#7111
Conversation
2e18566 to
b0db03f
Compare
0f2ef93 to
30d72b7
Compare
spec/features/idv/analytics_spec.rb
Outdated
There was a problem hiding this comment.
This feels rather hacky, but I was finding that the specs were failing due to what appeared to be the user record being outdated (user.proofing_component vs. user.reload.proofing_component had different results when using binding.pry). I suspected it's because the stubbed user was being cached across requests, which is unlike how it would behave in the real-world. Interesting that it only happened when updating proofing component for gpo_letter (pretty late in the flow) and not other proofing components.
b33a721 to
1e428c2
Compare
14ba16f to
15be4f7
Compare
There was a problem hiding this comment.
I was observing some issues where the proofing components weren't being logged correctly. When debugging, it appeared that the user's proofing component was a new record with nil values, which I traced back to this usage of create_or_find_by. It could make sense if this logic would behave as an upsert without actually fetching the underlying record. At this point in the flow I would assume that a component should definitely exist, so the optimizations of create_or_find_by wouldn't seem to apply, or at least we definitely do want to fetch the record so that the logged proofing components are accurate.
Flagging since it's a bit more substantial a change than I would have expected to need to make here, and in case there's any drawbacks I'm not considering by swapping this to a find_or_create_by.
|
Marking this ready for review. There's been a handful of approvals already, but also quite a bit of revisions. I'll keep it open for the next day or so to allow time for any other comments and plan to merge sometime tomorrow morning. |
There was a problem hiding this comment.
so if we're calling .decorate in the ApplicationController... we're decorating it basically everywhere (like 95% of callsites), which tells me we should probably just add whatever behavior here to the base class
There was a problem hiding this comment.
so if we're calling
.decoratein the ApplicationController... we're decorating it basically everywhere (like 95% of callsites), which tells me we should probably just add whatever behavior here to the base class
Yeah, that's true. I tried to find a middle-ground option by baking in the behaviors as an included module of the base class, but I was running into some issues with how the methods are dynamically defined. I can't recall the specifics of the issue, but I can try that again, and if all else fails, I can bake it into the class itself.
There was a problem hiding this comment.
Got something working in 38235a73d.
There was a problem hiding this comment.
(I also had an alternative approach which avoided it being a "concern", but unsure how I feel about referencing the base event method class like this)
DECORATED_METHODS.each do |method_name|
define_method(method_name) do |**kwargs|
original_method = AnalyticsEvents.instance_method(method_name)
original_method.bind_call(self, **kwargs, **common_analytics_attributes)
end
end
app/services/analytics_events.rb
Outdated
There was a problem hiding this comment.
I have one more idea, if we implement as_json in the ProofingComponentLogging class, we don't have to remember to call .to_h at each callsite here?
def as_json(*)
to_h
end
then we can do:
| proofing_components: proofing_components.to_h, | |
| proofing_components: proofing_components, |
There was a problem hiding this comment.
I think that would be fine for the logging, but it may end up being a problem for how we're asserting expected calls to track_event, since it would then expect the instance and not the hash.
There was a problem hiding this comment.
Oh that's true. It looks like lots of the specs use kind_of(Hash) so for those at least it would be similar
There was a problem hiding this comment.
I gave this a shot between 958a793a3 and f58a2f390. It required me to change how we assert against expected analytics properties in the FakeAnalytics class, though this feels like it's in line with how the logging works in the real-world.
changelog: Internal, Analytics, Add common properties to identity verification events Handle absent user proofing components Refactor IdV analytics as decorator It behaves as one, and resolves naming collision of base analytics class Refactor IdV analytics initializer as concern more composable, prefer consistency of single application controller Symbolize analytics keys consistent analytics argument shape, simplified/consistent spec expected values Add idv_final to decorated analytics methods Add proofing_components as explicit parameter to analytics methods So they're documented, and so that it's enforced as required parameter Add idv_personal_key_visited to decorated analytics Add idv_personal_key_submitted to analytics decorator Add specs for AnalyticsDecorator Simplify decorator interface by making readers private Add extra parameter to IdV analytics methods Add idv_review_info_visited to decorated analytics methods Add idv_phone_confirmation_otp_submitted to decorated analytics methods Add idv_phone_confirmation_otp_visit to decorated analytics methods Sync expected methods Reference user via analytics instance Override base analytics method in Idv::AnalyticsConcern Add idv_phone_confirmation_otp_sent to decorated methods Add idv_phone_otp_delivery_selection_submitted to decorated methods Add analytics concern to OtpDeliveryMethodController So that analytics override occurs as expected Make FakeAnalytics user parameter optional Add idv_cancellation_visited as decorated event Implement decorated methods as method_missing Discussion: #7111 (comment) At least for now, to simplify including new methods, can be broken back out later Still explicitly lists every method, so may address concerns with magic? Add all cancellation events as decorated Add GPO come back later event as decorated Allow optional user for proofing_components Not really expected, but simplifies existing tests support Add forgot password events to decorated events Freeze decorated methods Try fixing memoized outdated user proofing components Previously, address_check: 'gpo_letter' was not being shown as included in the logs, but was present in proofing components only after `user.reload`. Operating theory is that because the user is sticky across all request analytics instances, it doesn't behave the same as in real world with per-request initialization Add GPO letter events to decorated methods Add personal key frontend events to decorated methods Not currently working (proofing_components is nil). Maybe user not correctly set? Refactor proofing components keys as struct More clarity in analytics method documentation Rework FrontendLogger to send on analytics instance Since otherwise the decorated events are not called, since the analytics_method is a reference to the base method in AnalyticsEvent class spec coverage tbd Remove analytics concern in favor of baking in decorated analytics Implement ProofingComponentsLogging custom to_h as sliced model Accuracy of documented parameter in AnalyticsEvents See: #7111 (comment) Add idv_in_person_ready_to_verify_visit as decorated method Add IdV phone events to decorated methods Add proofing components expectations for happy path Fix analytics decorator spec Revise analytics decorator spec to sample single method Since all methods should behave the same via define_method implementation, this helps speed up the test Make proofing_components nillable in analytics events Too many specs run in isolation without expectation of decorated analytics Add missing event documentation Update controller spec analytics assertions Add idv_setup_errors_visited to decorated methods Fix specs Update ApplicationController specs Handle anonymous analytics user in analytics decorator Add additional expected properties for IdV phone events Swap create_or_find_by to find_or_create_by 1. Likely the more optimal query, since at this point it should be expected that a proofing component would already exist 2. Fixes a behavior where user proofing record relation had nil values (proofing component record treated as new/unsaved?) Refactor analytics decorator as built-in to base Analytics class #7111 (comment) Remove lingering reference to decorate method Normalize asserted events as JSON in FakeAnalytics Match real-world logging behavior Log proofing components via as_json method See: #7111 (comment) Update specs for nil logged proofing_component These specs stub analytics without an associated user / proofing component, so proofing components would be expected to be empty Previously the nil value would have been converted to an empty hash via `to_h`. This is a more desirable result anyways
Odd that it suddenly starts failing?
The analytics method calls Hash#compact
ad2afa1 to
8051764
Compare
There was a problem hiding this comment.
LGTM
Also one question: we still haven't 100% migrated the DOC_AUTH event, should we manually add this instrumentation inside the FlowStateMachine?
identity-idp/app/services/flow/flow_state_machine.rb
Lines 177 to 183 in b0e1dba
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| DECORATED_METHODS.each do |method_name| | ||
| alias_method "original_#{method_name}", method_name | ||
| define_method(method_name) do |**kwargs| | ||
| send("original_#{method_name}", **kwargs, **common_analytics_attributes) | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
unsure if it would too "sneaky" but what if we used prepend?
so
class Analytics
include AnalyticsEvents
prepend Idv::AnalyticsEventsEnhancer
and then this becomes...
| extend ActiveSupport::Concern | |
| included do | |
| DECORATED_METHODS.each do |method_name| | |
| alias_method "original_#{method_name}", method_name | |
| define_method(method_name) do |**kwargs| | |
| send("original_#{method_name}", **kwargs, **common_analytics_attributes) | |
| end | |
| end | |
| end | |
| DECORATED_METHODS.each do |method_name| | |
| define_method(method_name) do |**kwargs| | |
| super(**kwargs, **common_analytics_attributes) | |
| end | |
| end |
There was a problem hiding this comment.
Hm, the specs for the enhancer class don't seem to like the change.
diff --git a/app/services/analytics.rb b/app/services/analytics.rb
index a70bdaddc..fee5bf485 100644
--- a/app/services/analytics.rb
+++ b/app/services/analytics.rb
@@ -2,7 +2,7 @@
class Analytics
include AnalyticsEvents
- include Idv::AnalyticsEventsEnhancer
+ prepend Idv::AnalyticsEventsEnhancer
attr_reader :user, :request, :sp, :ahoy
diff --git a/app/services/idv/analytics_events_enhancer.rb b/app/services/idv/analytics_events_enhancer.rb
index cd0460db1..398d8a7f1 100644
--- a/app/services/idv/analytics_events_enhancer.rb
+++ b/app/services/idv/analytics_events_enhancer.rb
@@ -35,14 +35,10 @@ module Idv
idv_start_over
].freeze
- extend ActiveSupport::Concern
-
- included do
- DECORATED_METHODS.each do |method_name|
- alias_method "original_#{method_name}", method_name
- define_method(method_name) do |**kwargs|
- send("original_#{method_name}", **kwargs, **common_analytics_attributes)
- end
+ DECORATED_METHODS.each do |method_name|
+ alias_method "original_#{method_name}", method_name
+ define_method(method_name) do |**kwargs|
+ send("original_#{method_name}", **kwargs, **common_analytics_attributes)
end
end
diff --git a/spec/services/idv/analytics_events_enhancer_spec.rb b/spec/services/idv/analytics_events_enhancer_spec.rb
index a2f6fe79e..f4ce17b0a 100644
--- a/spec/services/idv/analytics_events_enhancer_spec.rb
+++ b/spec/services/idv/analytics_events_enhancer_spec.rb
@@ -8,7 +8,7 @@ describe Idv::AnalyticsEventsEnhancer do
@called_kwargs = kwargs
end
- include Idv::AnalyticsEventsEnhancer
+ prepend Idv::AnalyticsEventsEnhancer
attr_reader :user, :called_kwargsFailure:
$ rspec spec/services/idv/analytics_events_enhancer_spec.rb
An error occurred while loading ./spec/services/idv/analytics_events_enhancer_spec.rb.
Failure/Error: alias_method "original_#{method_name}", method_name
NameError:
undefined method `idv_cancellation_confirmed' for module `Idv::AnalyticsEventsEnhancer'
There was a problem hiding this comment.
I'm happy to revisit this, but I won't consider it blocking since I'd like to get this merged today.
No longer decorating
Within the scope of the ticket, I'm not sure it's necessary, though I agree it'd probably make sense to be consistent with this, with the caveat that FlowStateMachine analytics probably needs a rethink anyways. Do we have an existing ticket for that? I could write one and/or add to the scope of that. |
|
🎫 Ticket
LG-7582
🛠 Summary of changes
To provide a means to disambiguate between in-person and unsupervised proofing events, it's proposed to add the user's proofing components to identity verification events, to allow for this and other similar filtering.
📜 Testing Plan
rspec spec/features/idv/analytics_spec.rb