diff --git a/.secrets.baseline b/.secrets.baseline index ada3a52..a4cd209 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "package-lock.json|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-07-17T19:49:02Z", + "generated_at": "2025-08-06T10:27:10Z", "plugins_used": [ { "name": "AWSKeyDetector" diff --git a/package-lock.json b/package-lock.json index 54b9bbf..0f833e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ibm-appconfiguration-js-client-sdk", - "version": "0.2.0-beta.3", + "version": "0.2.0-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ibm-appconfiguration-js-client-sdk", - "version": "0.2.0-beta.3", + "version": "0.2.0-beta.4", "license": "Apache-2.0", "dependencies": { "eventemitter3": "^4.0.7", diff --git a/package.json b/package.json index fc0e9a8..58d77a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ibm-appconfiguration-js-client-sdk", - "version": "0.2.0-beta.3", + "version": "0.2.0-beta.4", "description": "IBM Cloud App Configuration JavaScript Client SDK", "keywords": [ "client-sdk", diff --git a/src/models/Experiment.ts b/src/models/Experiment.ts index 29e27d6..3ed3b1b 100644 --- a/src/models/Experiment.ts +++ b/src/models/Experiment.ts @@ -26,8 +26,9 @@ export interface IExperiment { export interface TrafficDistribution { type: string; rule_id: string; - control_group: Group; experimental_group: Group[]; + control_variation: string; + non_experimental_group?: Group; traffic_reassignment: boolean; } diff --git a/src/models/Feature.ts b/src/models/Feature.ts index 307f017..42131bd 100644 --- a/src/models/Feature.ts +++ b/src/models/Feature.ts @@ -41,14 +41,14 @@ interface EvaluationResult { value: any; is_enabled: boolean; variation_id: string; - audience_group: string; + group_type: string; } -// variation details with audience type (experiment or control) -interface VariationWithAudience { +// variation details with group type (experimental or non_experimental) +interface VariationWithGroup { variation_id: string; rollout_percentage: number; - audience_group: string; + group_type: string; } export default class Feature { @@ -185,7 +185,7 @@ export default class Feature { value: undefined, is_enabled: false, variation_id: '', - audience_group: '', + group_type: '', }; try { // evaluate the feature flag only if the toggle state is enabled @@ -196,17 +196,19 @@ export default class Feature { const { iteration } = this.experiment; const { variations } = this.experiment; if (trafficDistribution.type === 'ALL') { - const allVariations: VariationWithAudience[] = []; + const allVariations: VariationWithGroup[] = []; for (const expV of trafficDistribution.experimental_group) { - allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, audience_group: 'experiment' }); + allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, group_type: 'experimental' }); + } + if (trafficDistribution.non_experimental_group) { + allVariations.push({ variation_id: trafficDistribution.non_experimental_group.variation_id, rollout_percentage: trafficDistribution.non_experimental_group.rollout_percentage, group_type: 'non_experimental' }); } - allVariations.push({ variation_id: trafficDistribution.control_group.variation_id, rollout_percentage: trafficDistribution.control_group.rollout_percentage, audience_group: 'control' }); let variationId = ''; let totalPercentage = 0; - let audienceGroup = ''; + let groupType = ''; const hashValue = getNormalizedValue(''.concat(entityId, ':', this.feature_id, ':', iteration.iteration_key)); for (const v of allVariations) { - audienceGroup = v.audience_group; + groupType = v.group_type; variationId = v.variation_id; totalPercentage += v.rollout_percentage; if (hashValue < totalPercentage) { @@ -215,7 +217,7 @@ export default class Feature { } for (const v of variations) { if (variationId === v.variation_id) { - evaluationResult.audience_group = audienceGroup; + evaluationResult.group_type = groupType; evaluationResult.variation_id = v.variation_id return { current_value: v.variation_value }; } @@ -265,17 +267,19 @@ export default class Feature { // case 1: user doesn't belong to any of the rules // case 2: feature flag is not targeted with rules // case 3: feature flag is targeted with rules, but entityAttributes are not passed - const allVariations: VariationWithAudience[] = []; + const allVariations: VariationWithGroup[] = []; for (const expV of trafficDistribution.experimental_group) { - allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, audience_group: 'experiment' }); + allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, group_type: 'experimental' }); + } + if (trafficDistribution.non_experimental_group) { + allVariations.push({ variation_id: trafficDistribution.non_experimental_group.variation_id, rollout_percentage: trafficDistribution.non_experimental_group.rollout_percentage, group_type: 'non_experimental' }); } - allVariations.push({ variation_id: trafficDistribution.control_group.variation_id, rollout_percentage: trafficDistribution.control_group.rollout_percentage, audience_group: 'control' }); let variationId = ''; let totalPercentage = 0; - let audienceGroup = ''; + let groupType = ''; const hashValue = getNormalizedValue(''.concat(entityId, ':', this.feature_id, ':', iteration.iteration_key)); for (const v of allVariations) { - audienceGroup = v.audience_group; + groupType = v.group_type; variationId = v.variation_id; totalPercentage += v.rollout_percentage; if (hashValue < totalPercentage) { @@ -284,7 +288,7 @@ export default class Feature { } for (const v of variations) { if (variationId === v.variation_id) { - evaluationResult.audience_group = audienceGroup; + evaluationResult.group_type = groupType; evaluationResult.variation_id = v.variation_id return { current_value: v.variation_value }; } @@ -311,17 +315,19 @@ export default class Feature { evaluationResult.evaluated_segment_id = segmentId; const expRuleId = parseInt(trafficDistribution.rule_id) if (expRuleId === segmentRule.order) { - const allVariations: VariationWithAudience[] = []; + const allVariations: VariationWithGroup[] = []; for (const expV of trafficDistribution.experimental_group) { - allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, audience_group: 'experiment' }); + allVariations.push({ variation_id: expV.variation_id, rollout_percentage: expV.rollout_percentage, group_type: 'experimental' }); + } + if (trafficDistribution.non_experimental_group) { + allVariations.push({ variation_id: trafficDistribution.non_experimental_group.variation_id, rollout_percentage: trafficDistribution.non_experimental_group.rollout_percentage, group_type: 'non_experimental' }); } - allVariations.push({ variation_id: trafficDistribution.control_group.variation_id, rollout_percentage: trafficDistribution.control_group.rollout_percentage, audience_group: 'control' }); let variationId = ''; let totalPercentage = 0; - let audienceGroup = ''; + let groupType = ''; const hashValue = getNormalizedValue(''.concat(entityId, ':', this.feature_id, ':', iteration.iteration_key)); for (const v of allVariations) { - audienceGroup = v.audience_group; + groupType = v.group_type; variationId = v.variation_id; totalPercentage += v.rollout_percentage; if (hashValue < totalPercentage) { @@ -330,7 +336,7 @@ export default class Feature { } for (const v of variations) { if (variationId === v.variation_id) { - evaluationResult.audience_group = audienceGroup; + evaluationResult.group_type = groupType; evaluationResult.variation_id = v.variation_id return { current_value: v.variation_value }; } @@ -386,8 +392,8 @@ export default class Feature { } return { current_value: this.disabled_value, is_enabled: false }; } finally { - if (this.experiment && this.experiment.experiment_status === 'RUNNING' && evaluationResult.variation_id.length > 0) { - ExpEvalMetering.getInstance().addMetering({ experiment_id: this.experiment.experiment_id, iteration_id: this.experiment?.iteration.iteration_id, feature_id: this.feature_id, variation_id: evaluationResult.variation_id, entity_id: entityId, audience_group: evaluationResult.audience_group, }) + if (this.experiment && this.experiment.experiment_status === 'RUNNING' && evaluationResult.variation_id.length > 0 && evaluationResult.group_type === 'experimental') { + ExpEvalMetering.getInstance().addMetering({ experiment_id: this.experiment.experiment_id, iteration_id: this.experiment?.iteration.iteration_id, feature_id: this.feature_id, variation_id: evaluationResult.variation_id, entity_id: entityId, }) } else { Metering.getInstance().addMetering(entityId, evaluationResult.evaluated_segment_id, this.feature_id, null); } @@ -408,7 +414,7 @@ export default class Feature { value: undefined, is_enabled: false, variation_id: '', - audience_group: '' + group_type: '' }; try { diff --git a/src/utils/expevaluationmetering.ts b/src/utils/expevaluationmetering.ts index a8fdb77..81a86f3 100644 --- a/src/utils/expevaluationmetering.ts +++ b/src/utils/expevaluationmetering.ts @@ -30,7 +30,6 @@ export interface EvaluationUsage { feature_id: string; variation_id: string; entity_id: string; - audience_group: string; timestamp?: string; } @@ -41,7 +40,7 @@ export default class ExpEvalMetering { private environmentId: string | undefined; - private meteringInterval: number = 60 * 1000; // 1 minute + private meteringInterval: number = 30 * 1000; // 30 seconds private timer: any; diff --git a/src/utils/expmetricmetering.ts b/src/utils/expmetricmetering.ts index 0901114..216466d 100644 --- a/src/utils/expmetricmetering.ts +++ b/src/utils/expmetricmetering.ts @@ -41,7 +41,7 @@ export default class ExpMetricMetering { private environmentId: string | undefined; - private meteringInterval: number = 60 * 1000; // 1 minute + private meteringInterval: number = 30 * 1000; // 30 seconds private timer: any;