Skip to content

LG-7582: Add proofing components to IdV analytics events#7111

Merged
aduth merged 5 commits intomainfrom
aduth-lg-7582-common-analytics
Oct 18, 2022
Merged

LG-7582: Add proofing components to IdV analytics events#7111
aduth merged 5 commits intomainfrom
aduth-lg-7582-common-analytics

Conversation

@aduth
Copy link
Contributor

@aduth aduth commented Oct 7, 2022

🎫 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.

# Find IdV completion events for in-person proofing
filter name = 'IdV: review complete' and properties.event_properties.proofing_components.document_check = 'usps'

📜 Testing Plan

  • Run updated analytics regression specs: rspec spec/features/idv/analytics_spec.rb

@aduth aduth force-pushed the aduth-lg-7582-common-analytics branch from 2e18566 to b0db03f Compare October 11, 2022 13:15
Copy link
Contributor

@sheldon-b sheldon-b left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@zachmargolis zachmargolis left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 137 to 145
Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

@aduth aduth force-pushed the aduth-lg-7582-common-analytics branch from b33a721 to 1e428c2 Compare October 13, 2022 16:07
@aduth aduth force-pushed the aduth-lg-7582-common-analytics branch from 14ba16f to 15be4f7 Compare October 17, 2022 14:07
Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

@aduth aduth marked this pull request as ready for review October 17, 2022 14:57
@aduth
Copy link
Contributor Author

aduth commented Oct 17, 2022

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got something working in 38235a73d.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(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

Copy link
Contributor

Choose a reason for hiding this comment

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

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:

Suggested change
proofing_components: proofing_components.to_h,
proofing_components: proofing_components,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh that's true. It looks like lots of the specs use kind_of(Hash) so for those at least it would be similar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

aduth added 2 commits October 18, 2022 12:45
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?
@aduth aduth force-pushed the aduth-lg-7582-common-analytics branch from ad2afa1 to 8051764 Compare October 18, 2022 17:04
Copy link
Contributor

@zachmargolis zachmargolis left a comment

Choose a reason for hiding this comment

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

LGTM

Also one question: we still haven't 100% migrated the DOC_AUTH event, should we manually add this instrumentation inside the FlowStateMachine?

def analytics_properties
{
flow_path: @flow.flow_path,
step: current_step,
step_count: current_flow_step_counts[current_step_name],
}
end

Comment on lines +38 to +47
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
Copy link
Contributor

Choose a reason for hiding this comment

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

unsure if it would too "sneaky" but what if we used prepend?

so

class Analytics
  include AnalyticsEvents
  prepend Idv::AnalyticsEventsEnhancer

and then this becomes...

Suggested change
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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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_kwargs

Failure:

$ 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'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to revisit this, but I won't consider it blocking since I'd like to get this merged today.

@aduth
Copy link
Contributor Author

aduth commented Oct 18, 2022

Also one question: we still haven't 100% migrated the DOC_AUTH event, should we manually add this instrumentation inside the FlowStateMachine?

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.

@aduth aduth merged commit 9bcd3cb into main Oct 18, 2022
@aduth aduth deleted the aduth-lg-7582-common-analytics branch October 18, 2022 19:04
@zachmargolis
Copy link
Contributor

Do we have an existing ticket for that?

LG-6748

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.

5 participants