From 2a3196a98c218ee7833e02779d86e40be6644e57 Mon Sep 17 00:00:00 2001 From: Jason Salaber Date: Thu, 12 Dec 2024 13:06:51 -0500 Subject: [PATCH] chore: added property to know when instance of ConfigBody is created --- .../bucketing-test-data/src/data/testData.ts | 12 +++-- .../bucketing/__tests__/bucketing.test.ts | 52 +++++++++++++++++-- lib/shared/bucketing/src/bucketing.ts | 6 +++ .../types/src/types/config/configBody.ts | 6 +++ .../src/edge-config.spec.ts | 7 ++- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/lib/shared/bucketing-test-data/src/data/testData.ts b/lib/shared/bucketing-test-data/src/data/testData.ts index 8e2a8d9ff..3924f5f9d 100644 --- a/lib/shared/bucketing-test-data/src/data/testData.ts +++ b/lib/shared/bucketing-test-data/src/data/testData.ts @@ -14,6 +14,7 @@ import { UserSubType, VariableType, } from '@devcycle/types' +import { plainToInstance } from 'class-transformer' import moment from 'moment' @@ -449,7 +450,7 @@ function configBodyAudiences(audiences: PublicAudience[]): { return auds } -export const config: ConfigBody = { +export const config = { project, environment, audiences: configBodyAudiences(reusableAudiences), @@ -653,7 +654,7 @@ export const config: ConfigBody = { clientSDKKey: 'test', } -export const barrenConfig: ConfigBody = { +export const barrenConfig = { project, environment, audiences: {}, @@ -730,7 +731,7 @@ export const barrenConfig: ConfigBody = { variableHashes: {}, } -export const configWithNullCustomData: ConfigBody = { +export const configWithNullCustomData = { project, environment, audiences: configBodyAudiences(reusableAudiences), @@ -783,9 +784,10 @@ export const configWithNullCustomData: ConfigBody = { clientSDKKey: 'test', } +const instancedConfig = plainToInstance(ConfigBody, config) export const configWithBucketingKey = (bucketingKey: string): ConfigBody => ({ - ...config, - features: config.features.map((feature) => ({ + ...instancedConfig, + features: instancedConfig.features.map((feature) => ({ ...feature, configuration: { ...feature.configuration, diff --git a/lib/shared/bucketing/__tests__/bucketing.test.ts b/lib/shared/bucketing/__tests__/bucketing.test.ts index 1733e27cc..ff280a0ca 100644 --- a/lib/shared/bucketing/__tests__/bucketing.test.ts +++ b/lib/shared/bucketing/__tests__/bucketing.test.ts @@ -1,5 +1,12 @@ /* eslint-disable max-len */ -import { Audience, FeatureType, PublicRollout, Rollout } from '@devcycle/types' +import { + Audience, + FeatureType, + PublicRollout, + Rollout, + ConfigBody, + Test, +} from '@devcycle/types' import { generateBoundedHashes, decideTargetVariation, @@ -7,9 +14,9 @@ import { doesUserPassRollout, } from '../src/bucketing' import { - config, - barrenConfig, - configWithNullCustomData, + config as plainConfig, + barrenConfig as plainBarrenConfig, + configWithNullCustomData as plainConfigWithNullCustomData, configWithBucketingKey, } from '@devcycle/bucketing-test-data' @@ -17,6 +24,14 @@ import moment from 'moment' import times from 'lodash/times' import filter from 'lodash/filter' import * as uuid from 'uuid' +import { plainToInstance } from 'class-transformer' + +const config = plainToInstance(ConfigBody, plainConfig) +const barrenConfig = plainToInstance(ConfigBody, plainBarrenConfig) +const configWithNullCustomData = plainToInstance( + ConfigBody, + plainConfigWithNullCustomData, +) describe('User Hashing and Bucketing', () => { it('generates buckets approximately in the same distribution as the variation distributions', () => { @@ -1597,6 +1612,35 @@ describe('Rollout Logic', () => { expect(doesUserPassRollout({ boundedHash: 0.9 })).toBeTruthy() }) + it('should not throw when given a ConfigBody config', () => { + expect(() => + generateBucketedConfig({ + config, + user: { + user_id: 'asuh', + }, + }), + ).not.toThrow() + }) + + it('should throw when given a non-ConfigBody config', () => { + const config = {} as ConfigBody + + expect(() => + generateBucketedConfig({ + config, + user: { + user_id: 'asuh', + }, + }), + ).toThrow() + }) + + it('should have isConfigBody set to true', () => { + const config = plainToInstance(ConfigBody, plainConfig) + expect(config.isConfigBody).toBeTruthy() + }) + describe('overrides', () => { it('correctly overrides a bucketing decision and a feature that doesnt normally pass segmentation', () => { const user = { diff --git a/lib/shared/bucketing/src/bucketing.ts b/lib/shared/bucketing/src/bucketing.ts index abf6dce06..562964e77 100644 --- a/lib/shared/bucketing/src/bucketing.ts +++ b/lib/shared/bucketing/src/bucketing.ts @@ -232,6 +232,12 @@ export const generateBucketedConfig = ({ user: DVCBucketingUser overrides?: Record }): BucketedUserConfig => { + if (!config.isConfigBody) { + console.log('config', config) + throw new Error( + 'Config is not a ConfigBody, transform config using plainToInstance', + ) + } const variableMap: BucketedUserConfig['variables'] = {} const featureKeyMap: BucketedUserConfig['features'] = {} const featureVariationMap: BucketedUserConfig['featureVariationMap'] = {} diff --git a/lib/shared/types/src/types/config/configBody.ts b/lib/shared/types/src/types/config/configBody.ts index d9e9b5ab2..5d6a5c26d 100644 --- a/lib/shared/types/src/types/config/configBody.ts +++ b/lib/shared/types/src/types/config/configBody.ts @@ -38,7 +38,13 @@ export { Environment as PublicEnvironment, } +export class Test { + defaultProp = true +} + export class ConfigBody { + isConfigBody = true + /** * Basic project data used for building bucketing response */ diff --git a/lib/shared/vercel-edge-config/src/edge-config.spec.ts b/lib/shared/vercel-edge-config/src/edge-config.spec.ts index a3f54656b..767f82788 100644 --- a/lib/shared/vercel-edge-config/src/edge-config.spec.ts +++ b/lib/shared/vercel-edge-config/src/edge-config.spec.ts @@ -24,7 +24,11 @@ describe('EdgeConfigSource', () => { expect(get).toHaveBeenCalledWith('devcycle-config-v2-server-sdk-key') expect(result).toEqual({ - config: { key: 'value', lastModified: 'some date' }, + config: { + key: 'value', + lastModified: 'some date', + isConfigBody: true, + }, lastModified: 'some date', metaData: { resLastModified: 'some date' }, }) @@ -64,6 +68,7 @@ describe('EdgeConfigSource', () => { expect(result).toEqual({ config: { + isConfigBody: true, key: 'value', lastModified: 'some date', features: [