Skip to content

feat (audience match types): Update audience evaluator and project config for new audience match types #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

mjc1283
Copy link
Contributor

@mjc1283 mjc1283 commented Oct 5, 2018

Summary

This updates project config and the audience evaluator to finish the implementation of typed audience evaluation:

  • In audience_evaluator, allow evaluation to continue when no user attributes are passed (previously we were immediately returning false when no attributes were passed)
  • Update project_config: in createProjectConfig, set up a by-id object for accessing audiences from both audiences and typedAudiences. Also, update getAudiencesForExperiment to first look in projectConfig.typedAudiences, and only look in projectConfig.audiences as a fallback.
  • Added unit tests checking that all top-level methods that accept user attributes can bucket users into experiments or rollouts that use typed audiences.

Test Plan

  • New & existing unit tests
  • Ran compatibility suite tests

Matt Carroll added 3 commits October 5, 2018 11:48
projectConfig.typedAudiences first, then fall back to projectConfig.audiences
Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

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

I haven't yet looked at the test cases. I'll try to get to them soon. In the meantime, here is some feedback.

@@ -30,9 +31,8 @@ module.exports = {
return true;
}

// if no user attributes specified, return false
if (!userAttributes) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be fine because we do validate that the userAttributes is either an object or null and thus falsy values don't make it here

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I don't think it needs to be the job of this module to handle other types of falsy values. Gracefully overwriting all falsy values seems reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mikeng13 @nchilada I'm not sure if you're agreeing with my change, or requesting that this be left the way it was.

Copy link
Contributor

@nchilada nchilada Oct 8, 2018

Choose a reason for hiding this comment

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

I think we're both agreeing! At least I am. Sorry for the confusion.

@@ -546,6 +598,73 @@ var configWithFeatures = {
'id': '594017',
'name': 'test_audience',
'conditions': '["and", ["or", ["or", {"type": "custom_attribute", "name": "test_attribute", "value": "test_value"}]]]'
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a thought... but at some point maybe we can consolidate the test datafiles that we use across unit and compat tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree. The test datafiles have become large and unwieldy, and this PR is making it worse. Given @nchilada's other test-related comment also, I'm rethinking some of these tests.

@@ -20,8 +20,9 @@ module.exports = {
* Determine if the given user attributes satisfy the given audience conditions
* @param {Object[]} audiences Audiences to match the user attributes against
* @param {Object[]} audiences.conditions Audience conditions to match the user attributes against
* @param {Object} userAttributes Hash representing user attributes which will be used in determining if
* the audience conditions are met
* @param {Object} [userAttributes] Hash representing user attributes which will be used in
Copy link
Contributor

Choose a reason for hiding this comment

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

Please also update the license header in this file and others you have touched that haven't had their license headers updated to this year.

fns.forEach(projectConfig.typedAudiences, function(audience) {
audience.conditions = JSON.parse(audience.conditions);
});
projectConfig.typedAudienceIdMap = fns.keyBy(projectConfig.typedAudiences, 'id');
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason to maintain a separate map? Any conflicts in ID's would get overwritten with the typed audience, which is what we want.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, a single map would be nice.

And maybe call it audiencesById since it's storing audiences and not IDs? But maybe I'm just parsing it weirdly

var audiencesInExperiment = fns.filter(projectConfig.audiences, function(audience) {
return audienceIds.indexOf(audience.id) !== -1;
fns.forEach(audienceIds, function(audienceId) {
var typedAudience = projectConfig.typedAudienceIdMap[audienceId];
Copy link
Contributor

Choose a reason for hiding this comment

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

Haha I dunno why we did the lookup the other way around in the first place. This is so much better :)

audiencesInExperiment.push(typedAudience);
return;
}
var audience = projectConfig.audienceIdMap[audienceId];
Copy link
Contributor

Choose a reason for hiding this comment

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

If we have just one consolidated map, we'd just have to look in one place.

Copy link
Contributor

@nchilada nchilada left a comment

Choose a reason for hiding this comment

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

I'm not sure if we need to test each match type with each API. We just need to make sure that each API does some audience targeting, so we could probably get away with ~2 test cases for each API, one where an experiment's audienceIds collectively pass and one where they collectively fail.

That said, I'm not going to complain too much about thorough tests 😄 LGTM!

P.S. I would also err on the side of whatever's simplest for the moment (either in terms of the smallest change to the codebase or the smallest additional change to this PR): when we tackle audience combinations there'll be another layer of complexity that we'll suddenly have to care about.

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

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

lgtm

@mikeproeng37
Copy link
Contributor

build

@mjc1283 mjc1283 merged commit 0f19fbe into mcarroll/audience-match-types Oct 9, 2018
@mjc1283 mjc1283 deleted the mcarroll/audience-match-types-project-config branch October 9, 2018 17:43
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.

3 participants