From 2a1890dd0373b2615000debe1e879b2f1842d31d Mon Sep 17 00:00:00 2001 From: Adam Wootton Date: Tue, 9 Apr 2024 17:37:20 -0400 Subject: [PATCH] chore: remove audience _id field from config and update bucketing code (#802) --- .../assembly/bucketing/segmentation.ts | 9 +++-- .../assembly/testHelpers.ts | 8 ++-- .../assembly/types/configBody.ts | 8 ++-- .../assembly/types/target.ts | 18 +-------- .../src/data/largeConfig.ts | 11 +----- .../bucketing-test-data/src/data/testData.ts | 30 ++++++++++----- lib/shared/bucketing/src/bucketing.ts | 9 +++-- .../types/src/types/config/models/audience.ts | 37 +++++++++++++++++++ .../config/models/featureConfiguration.ts | 8 +++- 9 files changed, 85 insertions(+), 53 deletions(-) diff --git a/lib/shared/bucketing-assembly-script/assembly/bucketing/segmentation.ts b/lib/shared/bucketing-assembly-script/assembly/bucketing/segmentation.ts index 065dcd831..842f7486a 100644 --- a/lib/shared/bucketing-assembly-script/assembly/bucketing/segmentation.ts +++ b/lib/shared/bucketing-assembly-script/assembly/bucketing/segmentation.ts @@ -8,7 +8,8 @@ import { validSubTypes, CustomDataFilter, UserFilter, - NoIdAudience, AudienceMatchFilter + AudienceMatchFilter, + Audience } from '../types' import { JSON } from '@devcycle/assemblyscript-json/assembly' import { getF64FromJSONValue } from '../helpers/jsonHelpers' @@ -24,7 +25,7 @@ import { getF64FromJSONValue } from '../helpers/jsonHelpers' */ export function _evaluateOperator( operator: AudienceOperator, - audiences: Map, + audiences: Map, user: DVCPopulatedUser, clientCustomData: JSON.Obj ): bool { @@ -61,7 +62,7 @@ export function _evaluateOperator( function doesUserPassFilter( filter: AudienceFilter, - audiences: Map, + audiences: Map, user: DVCPopulatedUser, clientCustomData: JSON.Obj ): bool { @@ -96,7 +97,7 @@ function doesUserPassFilter( function filterForAudienceMatch( filter: AudienceMatchFilter, - configAudiences: Map, + configAudiences: Map, user: DVCPopulatedUser, clientCustomData: JSON.Obj ): bool { diff --git a/lib/shared/bucketing-assembly-script/assembly/testHelpers.ts b/lib/shared/bucketing-assembly-script/assembly/testHelpers.ts index f54a78c00..623fbc8f8 100644 --- a/lib/shared/bucketing-assembly-script/assembly/testHelpers.ts +++ b/lib/shared/bucketing-assembly-script/assembly/testHelpers.ts @@ -10,12 +10,12 @@ import { Rollout as PublicRollout, Target as PublicTarget, AudienceOperator, UserFilter, - NoIdAudience, decodeVariableForUserParams_PB, encodeVariableForUserParams_PB, decodeDVCUser_PB, decodeSDKVariable_PB, - encodeSDKVariable_PB + encodeSDKVariable_PB, + Audience } from './types' import { _checkCustomData, @@ -87,7 +87,7 @@ export function evaluateOperatorFromJSON( if (!operatorJSON.isObj) { throw new Error('evaluateOperatorFromJSON operatorStr or userStr param not a JSON Object') } - const audiences = new Map() + const audiences = new Map() if (audiencesStr !== '' && audiencesStr !== '{}') { const audiencesJSON = JSON.parse(audiencesStr) as JSON.Obj if (!audiencesJSON.isObj) { @@ -96,7 +96,7 @@ export function evaluateOperatorFromJSON( const keys = audiencesJSON.keys for (let i = 0; i < keys.length; i++) { const aud = audiencesJSON.get(keys[i]) as JSON.Obj - audiences.set(keys[i], new NoIdAudience(aud)) + audiences.set(keys[i], new Audience(aud)) } } diff --git a/lib/shared/bucketing-assembly-script/assembly/types/configBody.ts b/lib/shared/bucketing-assembly-script/assembly/types/configBody.ts index 6b591c97e..2b0fc2457 100644 --- a/lib/shared/bucketing-assembly-script/assembly/types/configBody.ts +++ b/lib/shared/bucketing-assembly-script/assembly/types/configBody.ts @@ -9,7 +9,7 @@ import { getJSONObjFromJSONOptional, } from '../helpers/jsonHelpers' import { Feature } from './feature' -import { NoIdAudience } from './target' +import { Audience } from './target' export class PublicProject extends JSON.Value { readonly _id: string @@ -79,7 +79,7 @@ export class Variable extends JSON.Value { export class ConfigBody { readonly project: PublicProject - readonly audiences: Map + readonly audiences: Map readonly environment: PublicEnvironment readonly features: Feature[] readonly variables: Variable[] @@ -170,13 +170,13 @@ export class ConfigBody { configJSONObj, 'audiences', ) - const audiences = new Map() + const audiences = new Map() if (audiencesJSON) { const audienceKeys = audiencesJSON.keys for (let i = 0; i < audienceKeys.length; i++) { const audience_id = audienceKeys[i] const aud = audiencesJSON.get(audience_id) - audiences.set(audience_id, new NoIdAudience(aud as JSON.Obj)) + audiences.set(audience_id, new Audience(aud as JSON.Obj)) } } this.audiences = audiences diff --git a/lib/shared/bucketing-assembly-script/assembly/types/target.ts b/lib/shared/bucketing-assembly-script/assembly/types/target.ts index 07dd8e90b..bf15d3639 100644 --- a/lib/shared/bucketing-assembly-script/assembly/types/target.ts +++ b/lib/shared/bucketing-assembly-script/assembly/types/target.ts @@ -145,7 +145,7 @@ export class AudienceOperator extends JSON.Value { } } -export class NoIdAudience extends JSON.Value { +export class Audience extends JSON.Value { readonly filters: AudienceOperator constructor(audience: JSON.Obj) { @@ -161,22 +161,6 @@ export class NoIdAudience extends JSON.Value { } } -export class Audience extends NoIdAudience { - readonly _id: string - - constructor(audience: JSON.Obj) { - super(audience) - this._id = getStringFromJSON(audience, '_id') - } - - stringify(): string { - const json = new JSON.Obj() - json.set('_id', this._id) - json.set('filters', this.filters) - return json.stringify() - } -} - const validAudienceOperators = ['and', 'or'] const validTypes = ['all', 'user', 'optIn', 'audienceMatch'] diff --git a/lib/shared/bucketing-test-data/src/data/largeConfig.ts b/lib/shared/bucketing-test-data/src/data/largeConfig.ts index 5dda0b945..d82bde2a5 100644 --- a/lib/shared/bucketing-test-data/src/data/largeConfig.ts +++ b/lib/shared/bucketing-test-data/src/data/largeConfig.ts @@ -1,3 +1,5 @@ +import { ConfigBody } from '@devcycle/types' + export const largeConfig = { project: { _id: '52979e6b353148fe8d54f1b7946578da', @@ -51,7 +53,6 @@ export const largeConfig = { targets: [ { _audience: { - _id: 'b8c6aca26fb540c78cea5560620d3fa5', filters: { filters: [ { @@ -78,7 +79,6 @@ export const largeConfig = { }, { _audience: { - _id: '59b2ecbd6d204f3ba8bd59ea17871844', filters: { filters: [ { @@ -140,7 +140,6 @@ export const largeConfig = { targets: [ { _audience: { - _id: '9a3c47be82ec469b836ef2aa63de3fdc', filters: { filters: [ { @@ -275,7 +274,6 @@ export const largeConfig = { targets: [ { _audience: { - _id: 'fa43ca76fc0943ba922051e144088065', filters: { filters: [ { @@ -313,7 +311,6 @@ export const largeConfig = { }, { _audience: { - _id: 'a0f11ee6388e4e7984667c779f423163', filters: { filters: [ { @@ -371,7 +368,6 @@ export const largeConfig = { targets: [ { _audience: { - _id: '19aec1d651d64ec7993df5c5a13ea33f', filters: { filters: [ { @@ -403,7 +399,6 @@ export const largeConfig = { }, { _audience: { - _id: '49b2698a5aa84622975f37d0582164e9', filters: { filters: [ { @@ -461,7 +456,6 @@ export const largeConfig = { targets: [ { _audience: { - _id: '09dce2cbd4594bf8bfc164769ec51415', filters: { filters: [ { @@ -507,7 +501,6 @@ export const largeConfig = { }, { _audience: { - _id: '86393f91545f4922a81b5567ac3ec901', filters: { filters: [ { diff --git a/lib/shared/bucketing-test-data/src/data/testData.ts b/lib/shared/bucketing-test-data/src/data/testData.ts index 5ad63df05..cbf88d8f8 100644 --- a/lib/shared/bucketing-test-data/src/data/testData.ts +++ b/lib/shared/bucketing-test-data/src/data/testData.ts @@ -10,6 +10,7 @@ import { PublicProject, PublicVariable, PublicVariation, + TargetAudience, UserSubType, VariableType, } from '@devcycle/types' @@ -32,7 +33,7 @@ export const environment: PublicEnvironment = { key: 'test-environment', } -export const audiences: PublicAudience[] = [ +export const reusableAudiences: PublicAudience[] = [ { _id: '614ef6ea475929459060721a', filters: { @@ -47,8 +48,23 @@ export const audiences: PublicAudience[] = [ operator: AudienceOperator.and, }, }, +] + +export const audiences: TargetAudience[] = [ + { + filters: { + filters: [ + { + type: FilterType.user, + subType: UserSubType.email, + comparator: FilterComparator['='], + values: ['test@email.com', 'test2@email.com'], + }, + ], + operator: AudienceOperator.and, + }, + }, { - _id: '6153557f1ed7bac7268ea0d9', filters: { filters: [ { @@ -85,7 +101,6 @@ export const audiences: PublicAudience[] = [ }, }, { - _id: '6153557f1ed7bac7268ea0d5', filters: { filters: [ { @@ -115,7 +130,6 @@ export const audiences: PublicAudience[] = [ }, }, { - _id: '6153557f1ed7bac7268ea0d6', filters: { filters: [ { @@ -139,7 +153,6 @@ export const audiences: PublicAudience[] = [ }, }, { - _id: '6153557f1ed7bac7268ea074', filters: { filters: [ { @@ -152,7 +165,6 @@ export const audiences: PublicAudience[] = [ }, }, { - _id: '6153557f1ed7bac7268ea0d7', filters: { filters: [ { @@ -168,7 +180,6 @@ export const audiences: PublicAudience[] = [ }, }, { - _id: '6153557f1ed7bac7268ea0d8', filters: { filters: [ { @@ -404,10 +415,11 @@ function configBodyAudiences(audiences: PublicAudience[]): { }) return auds } + export const config: ConfigBody = { project, environment, - audiences: configBodyAudiences(audiences), + audiences: configBodyAudiences(reusableAudiences), features: [ { _id: '614ef6aa473928459060721a', @@ -634,7 +646,7 @@ export const barrenConfig: ConfigBody = { export const configWithNullCustomData: ConfigBody = { project, environment, - audiences: configBodyAudiences(audiences), + audiences: configBodyAudiences(reusableAudiences), features: [ { _id: '614ef6aa475928459060721d', diff --git a/lib/shared/bucketing/src/bucketing.ts b/lib/shared/bucketing/src/bucketing.ts index 94c7415d5..c0a0da597 100644 --- a/lib/shared/bucketing/src/bucketing.ts +++ b/lib/shared/bucketing/src/bucketing.ts @@ -4,15 +4,16 @@ import pick from 'lodash/pick' import last from 'lodash/last' import first from 'lodash/first' import { + AudienceOperator, + BucketedUserConfig, ConfigBody, - PublicTarget, + DVCBucketingUser, + Feature, PublicFeature, - BucketedUserConfig, PublicRollout, PublicRolloutStage, - DVCBucketingUser, + PublicTarget, Variation, - Feature, } from '@devcycle/types' import murmurhash from 'murmurhash' diff --git a/lib/shared/types/src/types/config/models/audience.ts b/lib/shared/types/src/types/config/models/audience.ts index f9fd087be..49dceb2f0 100644 --- a/lib/shared/types/src/types/config/models/audience.ts +++ b/lib/shared/types/src/types/config/models/audience.ts @@ -102,6 +102,43 @@ export enum AudienceOperator { or = 'or', } +export class AudienceFilter { + /** + * Filter type of this audience filter (user, audienceTemplate etc.) + */ + type: FilterType + + /** + * Sub type of this filter (appVersion, mixpanel etc.) + */ + subType?: UserSubType + + /** + * Comparator to use if this is a filter + */ + comparator: FilterComparator + + /** + * Data Key used for custom data and other filter sub-type that require a key-value mapping. + */ + dataKey?: string + + /** + * Data Key used for custom data and other filter sub-type that require a key-value mapping. + */ + dataKeyType?: DataKeyType + + /** + * Filter values to segment against, must be set for all filter types other than 'all' + */ + values?: string[] | boolean[] | number[] + + /** + * Array of audience id's for filters of type audienceMatch + */ + _audiences?: IdType[] +} + /** * Audience filter used to describe a segmentation for a user audience. */ diff --git a/lib/shared/types/src/types/config/models/featureConfiguration.ts b/lib/shared/types/src/types/config/models/featureConfiguration.ts index 6364986c9..8cb42579d 100644 --- a/lib/shared/types/src/types/config/models/featureConfiguration.ts +++ b/lib/shared/types/src/types/config/models/featureConfiguration.ts @@ -1,4 +1,4 @@ -import { Audience } from './audience' +import { Audience, AudienceFilter, TopLevelOperator } from './audience' import { Type } from 'class-transformer' export enum TargetingRuleTypes { @@ -62,6 +62,10 @@ export class TargetDistribution { percentage: number } +export class TargetAudience { + filters: TopLevelOperator +} + /** * Defines an Audience Target including the Audience model, rollout, and variation distribution * _id needed as it will be used as the seed in the hashing function to determine a given users position @@ -73,7 +77,7 @@ export class Target { /** * Audience model describing target segmentation. */ - _audience: Audience + _audience: TargetAudience /** * Rollout sub-document describing how a Target's audience is rolled out