Skip to content

Commit

Permalink
feat: count number of combinations from playground (#4077)
Browse files Browse the repository at this point in the history
This PR adds plausible tracking of the number of feature combinations
that we get from the advanced playground API.

The event type has been added to plausible

 Relates to #3545
  • Loading branch information
thomasheartman committed Jun 23, 2023
1 parent 89cf16f commit 12c0073
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 1 deletion.
1 change: 1 addition & 0 deletions frontend/package.json
Expand Up @@ -62,6 +62,7 @@
"@uiw/codemirror-theme-duotone": "4.21.3",
"@uiw/react-codemirror": "4.21.1",
"@vitejs/plugin-react": "3.1.0",
"cartesian": "^1.0.1",
"chart.js": "3.9.1",
"chartjs-adapter-date-fns": "3.0.0",
"classnames": "2.3.2",
Expand Down
Expand Up @@ -36,6 +36,8 @@ import {
} from 'openapi';
import { capitalizeFirst } from 'utils/capitalizeFirst';
import { AdvancedPlaygroundEnvironmentDiffCell } from './AdvancedPlaygroundEnvironmentCell/AdvancedPlaygroundEnvironmentDiffCell';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { countCombinations } from './combinationCounter';

const defaultSort: SortingRule<string> = { id: 'name' };
const { value, setValue } = createLocalStorage(
Expand All @@ -61,6 +63,16 @@ export const AdvancedPlaygroundResultsTable = ({
input,
loading,
}: IAdvancedPlaygroundResultsTableProps) => {
const { trackEvent } = usePlausibleTracker();
if (features) {
trackEvent('playground', {
props: {
eventType: 'number-of-combinations',
count: countCombinations(features),
},
});
}

const [searchParams, setSearchParams] = useSearchParams();
const ref = useLoading(loading);
const [searchValue, setSearchValue] = useState(
Expand Down
@@ -0,0 +1,97 @@
import { countCombinations } from './combinationCounter';
import {
AdvancedPlaygroundEnvironmentFeatureSchema,
AdvancedPlaygroundFeatureSchema,
} from 'openapi';
// @ts-expect-error no types available
import cartesian from 'cartesian';

const generateFeature = (
context: Record<string, string>
): AdvancedPlaygroundEnvironmentFeatureSchema => ({
isEnabled: false,
isEnabledInCurrentEnvironment: true,
variant: {
name: 'disabled',
enabled: false,
},
context: {
appName: 'playground',
},
variants: [],
name: 'default',
environment: 'development',
projectId: 'default',
strategies: {
result: false,
data: [
{
name: 'default',
id: '7b233aae-cbc4-45ea-ace2-4c78c8e7e760',
disabled: false,
parameters: {},
result: {
enabled: false,
evaluationStatus: 'complete' as 'complete',
},
constraints: [
{
inverted: false,
values: ['k'],
operator: 'IN',
contextName: 'appName',
caseInsensitive: false,
result: false,
},
],
segments: [],
links: {
edit: '/projects/default/features/default/strategies/edit?environmentId=development&strategyId=7b233aae-cbc4-45ea-ace2-4c78c8e7e760',
},
},
],
},
});

const generateInput = (
featureCount: number,
environments: string[],
contextValues: { [key: string]: string[] }
): AdvancedPlaygroundFeatureSchema[] => {
const cartesianContext = cartesian(contextValues);

return Array.from(Array(featureCount)).map((_, i) => ({
name: `feature-${i}`,
projectId: 'default',
environments: Object.fromEntries(
environments.map(env => [
env,
cartesianContext.map(generateFeature),
])
),
}));
};

it('counts the correct number of combinations', () => {
const assertCount = (
numberOfFeatures: number,
envs: string[],
context: { [k: string]: string[] }
) => {
const totalCombinations =
numberOfFeatures *
envs.length *
Object.values(context)
.map(contextValues => contextValues.length)
.reduce((total, n) => total + n);
const input = generateInput(numberOfFeatures, envs, context);
expect(countCombinations(input)).toEqual(totalCombinations);
};

assertCount(1, ['development'], { x: ['2'] });
assertCount(10, ['development', 'production'], {
x: ['1', '2'],
y: ['x', 'abc'],
});
assertCount(5, ['development'], { x: ['1', '2'] });
});
@@ -0,0 +1,12 @@
import { AdvancedPlaygroundFeatureSchema } from 'openapi';

export const countCombinations = (
features: AdvancedPlaygroundFeatureSchema[]
) =>
features.reduce(
(total, feature) =>
total +
Object.values(feature.environments).flatMap(env => Object.keys(env))
.length,
0
);
3 changes: 2 additions & 1 deletion frontend/src/hooks/usePlausibleTracker.ts
Expand Up @@ -40,7 +40,8 @@ export type CustomEvents =
| 'demo-open-demo-web'
| 'context-usage'
| 'segment-usage'
| 'strategy-add';
| 'strategy-add'
| 'playground';

export const usePlausibleTracker = () => {
const plausible = useContext(PlausibleContext);
Expand Down
12 changes: 12 additions & 0 deletions frontend/yarn.lock
Expand Up @@ -3613,6 +3613,13 @@ caniuse-lite@^1.0.30001400:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447"
integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==

cartesian@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cartesian/-/cartesian-1.0.1.tgz#ae3fc8a63e2ba7e2c4989ce696207457bcae65af"
integrity sha512-tR3qKRYpRJ6FXEGuoBwpuCYcwydrk1N2rduy7eWg1Msepi3i5fCxheryw4VBlCqjCbk3Vhjh3eg+IGHtl5H74A==
dependencies:
xtend "^4.0.1"

caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
Expand Down Expand Up @@ -9587,6 +9594,11 @@ xregexp@2.0.0:
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
integrity sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==

xtend@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==

y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
Expand Down

0 comments on commit 12c0073

Please sign in to comment.