Skip to content
This repository has been archived by the owner on Apr 26, 2018. It is now read-only.

First big project commit as pull request #4

Merged
merged 1 commit into from Jan 26, 2017

Conversation

jaredhirsch
Copy link
Member

@jaredhirsch jaredhirsch commented Jan 24, 2017

I've added the whole team as admins on this repo. Comments and questions very welcome.

Take a look at my branch to see the formatted README and API docs: https://github.com/6a68/testpilot-metrics/tree/test

I'll plan to land this EOD Tuesday, unless there are major concerns :-)

- make it a mobile app, not a website
- go into 'real-time', then 'events', to see pings trickle in as you click the toolbar button:

![GA dashboard screenshot](https://www.dropbox.com/s/2i0e206q2qwcpx1/Screen%20Shot%202017-01-22%20at%209.35.39%20PM.png?dl=0&raw=true)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add this image to this repo instead of pointing out to Dropboxen?

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

"main": "index.js",
"scripts": {
"test": "./node_modules/.bin/mocha test/*",
"build-docs": "./node_modules/.bin/documentation build testpilot-metrics.js -f md -o API.md"
Copy link
Contributor

Choose a reason for hiding this comment

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

Techically, ./node_modules/.bin/ should be added to the $PATH when using npm run ..., so you should be able to simplify these to mocha test/* and documentation build ... (without the node_modules prefixes).

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

@@ -0,0 +1,15 @@
const TRACKING_ID = 'UA-XXXXXXXX-YY';

const { sendEvent } = new Metrics({
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not 100% familiar w/ web extension background scripts, but do we need to import Metrics class/object before using it here? Or do we automatically get it in-scope since it's defined in the manifest.json's background.scripts[] array?

Copy link
Member Author

Choose a reason for hiding this comment

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

Great question. WebExtensions background scripts seem to just get loaded into the auto-generated background page, script tag style (implicit global binding). Trying to require() the file threw an error. ES6 modules might work, but I didn't try that: I didn't want to lock experiment authors into dealing with ES6 in their build scripts.

Copy link
Contributor

@lmorchard lmorchard Jan 24, 2017

Choose a reason for hiding this comment

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

Yeah, AFAIK, there is no module system built into WebExtensions. We're using rollup & babel to build Snooze Tabs - but it is good not to assume that will be the case.

@meandavejustice
Copy link
Contributor

Some experiments (min-vid for example) send extra fields to metrics. We should add another parameter to handle this data. example data

in progress branch to integrate testpilot-metrics on min-vid


const { sendEvent } = new Metrics({
id: '@my-addon',
cid: 'some-non-PII-user-ID',
Copy link
Contributor

Choose a reason for hiding this comment

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

It may be nice to add an example of how to get this cid, in the testpilot addon we are currently setting this in a simple-storage store. and sending it along with each metric event.

initialization in exports.main

// set our unique identifier for metrics
if (!store.clientUUID) {
   store.clientUUID = require('sdk/util/uuid').uuid().toString().slice(1, -1);
}

sendEvent call

const { sendEvent } = new Metrics({
  id: '@'+manifest.name,
  cid: store.clientUUID
});

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 Thanks for the suggestion. I've gone back and forth on this point; not having node-style module support in WebExtensions makes it really, really hard to find an off-the-shelf library that's easy to use. But I guess I could roll my own, using window.crypto to get pseudorandom data, and resort to the hidden window trick to get the crypto API for SDK/bootstrapped add-ons. I'll add that now

Copy link
Member Author

Choose a reason for hiding this comment

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

Rather than add a uid generator, I'll just file a bug to document suggestions for generating the user id.

@jaredhirsch
Copy link
Member Author

Some experiments (min-vid for example) send extra fields to metrics. We should add another parameter to handle this data.

Good point. The simplest solution here seems to be: let experiments pass extra key/value pairs in the object. This works fine for Firefox client metrics, but GA doesn't support random extra named parameters.

GA does support custom dimensions, though they are just named cd1 up to cd200. This seems like a rather fancy use case, compared with "does event/object demonstrate DAU growth".

Maybe the best thing is to let fancy experiment authors solve their own problems: I'll add support for a transform function parameter that is called to transform the ping object into a set of GA parameters ready to be encoded and sent. Adding transform would solve a related issue where testpilot-metrics, currently, only uses the GA event type, while some experiments might want to use other GA hit types. I'll add that tonight.

@meandavejustice
Copy link
Contributor

@6a68 That sounds like a good compromise, thanks Jared

};
if (study && variant) {
data.xid = study;
data.xvar = variant;
Copy link

Choose a reason for hiding this comment

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

You may want to use custom dimensions here as these seem like environment variables which maps well to dimensions I think (technical docs, and more information on dimensions and metrics).

FWIW I think the measurement protocol is definitely the way to go, and it's worked fine in the Test Pilot implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call. I didn't realize how involved GA Content Experiments were: https://support.google.com/analytics/answer/1745154?hl=en&ref_topic=1745208. I don't think that's what we want for our A/B tests.


## installation

- Edit `background.js`, set the value of the `TRACKING_ID` const to your GA tracking ID
Copy link

Choose a reason for hiding this comment

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

RE: Mozilla ID vs. personal ID

  • If we're making Firefox-y decisions based on this, maybe Mozilla ID
  • Mozilla ID adds another dependency on the analytics team (filing a bug)
  • Personal ID has bus factor/SPOF risks
  • Personal ID could also reduce overhead on graduation (e.g. don't have to transfer things, although transferring ownership doesn't seem like a huge deal)

Maybe we suggest starting up with personal for quickness, then require a transfer to Mozilla before launch?

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 I'll add this to the top-level README docs

Copy link
Member Author

Choose a reason for hiding this comment

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

@ckprice Actually, Wil's metrics PR suggested leaving the author as owner throughout, but granting Mozilla access while it's in Test Pilot

_initTransports: function() {
if (this.type === 'webextension') {
try {
this._channel = new BroadcastChannel(this.topic);
Copy link
Contributor

@lmorchard lmorchard Jan 25, 2017

Choose a reason for hiding this comment

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

Potential problem in WebExtension land here: The BroadcastChannel has to be named testpilot-telemetry, but it looks like it will be named just testpilot because this.topic gets set to that.

Copy link
Member Author

Choose a reason for hiding this comment

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

doh. great catch, thank you

// pings to Telemetry. If the add-on ID matches the test pilot add-on, use
// the special nsIObserver topic. Otherwise, use the topic shared by all
// experiments.
this.topic = (id === '@testpilot-addon') ? 'testpilot' : 'testpilottest';
Copy link
Contributor

@lmorchard lmorchard Jan 25, 2017

Choose a reason for hiding this comment

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

Maybe change this to:

if (type === 'webextension') {
  this.topic = 'testpilot-telemetry'
} else if (id === '@testpilot-addon') {
  this.topic = 'testpilot';
} else {
  this.topic = 'testpilottest';
}

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

@lmorchard
Copy link
Contributor

lmorchard commented Jan 25, 2017

I'll add support for a transform function parameter that is called to transform the ping object into a set of GA parameters ready to be encoded and sent.

Does that mean, conversely, that whatever object gets passed in to sendEvent() will be sent as-is to Telemetry? (i.e. rather than stripped down to id, event, & object?) If so then 👍

(Also guessing a transform function would resolve my confusion about the GA params too, if I'm assembling them in my own code.)

cid: this.cid,
t: 'event',
ec: category,
ea: object,
Copy link
Contributor

@lmorchard lmorchard Jan 25, 2017

Choose a reason for hiding this comment

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

This mapping seems a little confusing. In the GA docs and in the realtime view, this ea param is called "Event Action" - but it's mapped here from object. And the el is "Event Label" - mapped here from event.

Confused me a little bit, because I was trying to send events like this:

{ event: 'panel-opened', object: 'snooze-tabs' }
{ event: 'snoozed', object: 'snooze-tabs' }
{ event: 'woke', object: 'snooze-tabs' }

And all I was getting were snooze-tabs under "Event Action" - and "Event Label" is not even listed on the realtime table.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. I've switched the code to make the event the 'event action'. That real-time event graph is the place to be.

@lmorchard
Copy link
Contributor

lmorchard commented Jan 25, 2017

FWIW, I've got a hacky start at integrating this into Snooze Tabs. The parameters seem a little confusing to me, but I was able to get some data flowing to GA in the realtime view:

screenshot 2017-01-25 17 09 50

screenshot 2017-01-25 17 17 32

@jaredhirsch
Copy link
Member Author

Does that mean, conversely, that whatever object gets passed in to sendEvent() will be sent as-is to Telemetry?

Yup! That's the idea...excluding the transform function, of course ;-)

@jaredhirsch
Copy link
Member Author

Made some changes, just about to squash, push, and merge. Just FYI for those who reviewed:

  • Fixed all the little review comments, thanks for those 👍
  • Send the entire object to Telemetry
  • Bunch of annoying little parameter changes:
    • Renamed 'cid' to 'uid', after realizing that 'cid' is always a uuidv4, but 'uid' can be whatever
    • Removed 'study', just made 'variant' the lone A/B testing parameter, with a new convention of using the first GA Custom Dimension for it. As ckprice pointed out, the GA experiment stuff is really complex and not what we want.
    • Add a mandatory 'version' (addon version) parameter to the constructor, and pass it to GA as the app version parameter. This assumes we are using the app GA view, not the web GA view. Documented this assumption in the GA setup steps in the README (which are new).
    • Rearrange the GA values corresponding to 'event' and 'object'. Event should be the action, and object the label, not the other way around.
    • Added the transform sendEvent parameter, and documented its use.
    • Updated the example docs and tests to use the new parameters.
  • The GA docs specifically mention using %20, not +, to encode spaces, even though the Measurement Protocol uses the x-www-formurlencodedcontent-type, so, switched back to using%20` for spaces.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants