Skip to content
This repository has been archived by the owner. It is now read-only.

feat(metrics): add flow.attempt_signin, flow.engage, flow.attempt_signup #4150

Merged
merged 4 commits into from Sep 20, 2016

Conversation

@vladikoff
Copy link
Contributor

@vladikoff vladikoff commented Sep 14, 2016

Fixes #4119

TODO: tests

@philbooth feedback?

Copy link
Contributor Author

@vladikoff vladikoff left a comment

added questions

@@ -90,7 +90,7 @@ define(function (require, exports, module) {

_logPromptExperimentEvent: function (eventNameSuffix) {
const eventName = 'experiment.pw_prompt.' + eventNameSuffix.toLowerCase();
this.logViewEvent(eventName);
this.logEventOnce(eventName);

This comment has been minimized.

@vladikoff

vladikoff Sep 14, 2016
Author Contributor

to avoid spamming pw_strength focus

engageForm() {
// user has engaged with the signin or sign up form
// log 'flow.engage' from both forms because both forms can login.
this.logEventOnce('flow.engage');

This comment has been minimized.

@vladikoff

vladikoff Sep 14, 2016
Author Contributor

only log it once per page reload, which should match new flow.begin events right?

flow_id: metrics.flowId, //eslint-disable-line camelcase
flow_time: 0, //eslint-disable-line camelcase
time: metrics.flowBeginTime
}, req);
}
});

This comment has been minimized.

@vladikoff

vladikoff Sep 14, 2016
Author Contributor

do we want to differentiate flow.begin from other events?

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

Not sure I understand the question?

This comment has been minimized.

@vladikoff

vladikoff Sep 15, 2016
Author Contributor

I just was not sure if all flow events should be logged the same away, seems like we should just log all of them the same way (like flow.begin already was).

@vladikoff
Copy link
Contributor Author

@vladikoff vladikoff commented Sep 15, 2016

This also fixes #4022

@vladikoff
Copy link
Contributor Author

@vladikoff vladikoff commented Sep 15, 2016

cc @rfk ^

Copy link
Member

@rfk rfk left a comment

love it, this will make for some really great analysis opportunities

* @param {String} eventName
*/
logEventOnce: function (eventName) {
if (eventMemory.indexOf(eventName) === -1) {

This comment has been minimized.

@rfk

rfk Sep 15, 2016
Member

Could use a map or set here for constant-time lookup, this lookup will grow with O(number of events).

This comment has been minimized.

@vladikoff

vladikoff Sep 15, 2016
Author Contributor

We don't have Map or Set available. However I updated this to use underscore which should be more performant hopefully.

This comment has been minimized.

@rfk

rfk Sep 16, 2016
Member

That was bad vocabulary on my part, what I really meant was to use one of these: {}.

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

@rfk - You are thinking in Python terms, aren't you?

*/
logEventOnce: function (eventName) {
this.metrics.logEventOnce(eventName);
},

This comment has been minimized.

@rfk

rfk Sep 15, 2016
Member

I really like this helper, will reduce a lot of confusion when reviewing the metrics.

Copy link
Contributor

@philbooth philbooth left a comment

Looking good @vladikoff!

Some inline comments about event names, we need to get them consistent with the spec one way or another.

engageForm() {
// user has engaged with the signin or sign up form
// log 'flow.engage' from both forms because both forms can login.
this.logEventOnce('flow.engage');

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

Re: the name of this event, it is currently specced differently to this. Now the spec can change, of course, but also I want to make sure that the event we emit is the one that is most useful for analysis purposes.

For flows that end at this point, where the user never submits the form, it might be useful to know which form the user was engaged with when they abandoned the flow. How tricky is it to change this event name so that it is signup.engage or signin.engage? What about force_auth.engage?

This comment has been minimized.

@vladikoff

vladikoff Sep 15, 2016
Author Contributor

Yea makes sense, I think my main issue is the word login, we don't use that anywhere. might be a good idea to stay away from that

@@ -31,6 +31,8 @@ define(function (require, exports, module) {
*/
signUp: function (account, password) {
var self = this;
self.logEvent('flow.attempt_signup');

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

Does this correspond to signup.submit from the spec? Is there a reason to prefer this name over that one?

if (DISABLE_CLIENT_METRICS_STDERR) {
return;
}

var events = metrics.events || [];
var hasFlowBeginEvent = events.some(function (event) {
var flowEvents = _.filter(events, function(event) {
return event.type.indexOf('flow.') === 0;

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

...ahhh, and here I get my answer. The event names are changed because we use the flow. prefix as an indicator that it is a flow event.

Makes sense, but we still need to take into account the analysis of abandoned flows. Could we maybe change:

  • flow.engage to flow.signup.engage and flow.signin.engage?
  • flow.attempt_signup to flow.signup.submit?
  • flow.attempt_signin to flow.signin.submit?
@@ -77,20 +82,14 @@ function optionallyLogFlowBeginEvent (req, metrics, requestReceivedTime) {
/*eslint-enable sorting/sort-object-props*/
});
}

return true;

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

Weird, I wonder why I did that?

flow_id: metrics.flowId, //eslint-disable-line camelcase
flow_time: 0, //eslint-disable-line camelcase
time: metrics.flowBeginTime
}, req);
}
});

This comment has been minimized.

@philbooth

philbooth Sep 15, 2016
Contributor

Not sure I understand the question?

@@ -24,13 +31,15 @@ define(function (require, exports, module) {
* @return {object} promise
*/
signIn: function (account, password) {
var self = this;
self.logEvent(`flow.${this.signInSubmitContext}.submit`);

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

Is there a reason to use signInSubmitContext instead of this.viewName? Seems like the already existing name should suffice?

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

btw, all views have a viewName based off of their URL, if one is not already specified on the View's prototype.

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.

This comment has been minimized.

@philbooth

philbooth Sep 16, 2016
Contributor

When will client side created flow events be used instead of server side created events?

Whenever the thing represented by the event occurs client-side.

The point of these events is to help us identify where users might be dropping off during the sign-in flow. Having extra events on the client, in between the existing flow.begin (client) and account.login (server) flow events provides a finer granularity for analysing those drop-offs. For instance, if some imaginary future UI change caused people to stop submitting the form for some reason, these events would help us identify that.

@@ -121,6 +121,7 @@ define(function (require, exports, module) {

this._marketingImpressions = {};
this._activeExperiments = {};
this._eventMemory = [];

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

Like @rfk suggested, lookups could be a bit quicker by using an Object instead of an Array, though I don't imagine the list ever growing so long that lookups become a problem.

Another alternative is to skip storing a list of events locally and search this.events directly:

isEventLogged (eventName) {
  return _.some(this.events, (event) => event.type === eventName);
}

This comment has been minimized.

@vladikoff

vladikoff Sep 16, 2016
Author Contributor

Another alternative is to skip storing a list of events locally and search this.events directly:

I don't think we can do that because those get flushed so it won't be logged once.
Otherwise an Object makes sense 👍

*
* @param {String} eventName
*/
logEventOnce: function (eventName) {

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

One case to consider here is:

this.logEvent('eventName');
this.logEventOnce('eventName');

When logEventOnce is called, should it log an additional time? That seems a bit unexpected.

If you go with storing a list of events locally, I'd add the eventName to the list in logEvent and check whether it's already stored here.

* @param {String} eventName
*/
logEventOnce: function (eventName) {
if (_.indexOf(this._eventMemory, eventName) === -1) {

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

See the previous comment about searching this.events directly instead of storing a local list.

@@ -24,13 +31,15 @@ define(function (require, exports, module) {
* @return {object} promise
*/
signIn: function (account, password) {
var self = this;
self.logEvent(`flow.${this.signInSubmitContext}.submit`);

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

When will client side created flow events be used instead of server side created events? Up to this point, I thought all flow events were server side created.

Copy link
Member

@shane-tomlinson shane-tomlinson left a comment

Looks reasonable, though have a couple of questions/nits/suggestions.

@@ -31,6 +31,8 @@ define(function (require, exports, module) {
*/
signUp: function (account, password) {
var self = this;
self.logEvent('flow.signup.submit');

This comment has been minimized.

@shane-tomlinson

shane-tomlinson Sep 16, 2016
Member

How will users that sign-in from the /signup page affect this stat?

This comment has been minimized.

@vladikoff

vladikoff Sep 16, 2016
Author Contributor

i think we talked irc about that but then the flow would be:

begin -> signup.engage -> signin.submit -> account.login

This comment has been minimized.

@philbooth

philbooth Sep 16, 2016
Contributor

How will users that sign-in from the /signup page affect this stat?

Yep, just to confirm @vladikoff's answer, that is by design as per the feature doc.

@vladikoff
Copy link
Contributor Author

@vladikoff vladikoff commented Sep 20, 2016

@philbooth final r?

@philbooth
Copy link
Contributor

@philbooth philbooth commented Sep 20, 2016

@vladikoff, LGTM, r+

@vladikoff vladikoff merged commit e618104 into master Sep 20, 2016
4 checks passed
4 checks passed
ci/circleci Your tests passed on CircleCI!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
coverage/coveralls Coverage increased (+0.003%) to 97.229%
Details
@vladikoff vladikoff deleted the i4119 branch Sep 20, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants