Skip to content

Commit

Permalink
feat: initialize feature flags and their payloads from cache (#1679)
Browse files Browse the repository at this point in the history
  • Loading branch information
szymonmaslowski authored and mchappell committed Jan 29, 2025
1 parent 41fdfbd commit 2097040
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ import styles from './MainFooter.module.scss';
import { useAnalyticsContext, useBackgroundServiceAPIContext } from '@providers';
import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker';
import { useWalletStore } from '@stores';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { BrowserViewSections } from '@lib/scripts/types';
import { ExperimentName } from '@lib/scripts/types/feature-flags';

const includesCoin = /coin/i;

Expand All @@ -40,7 +40,7 @@ export const MainFooter = (): React.ReactElement => {
const posthog = usePostHogClientContext();
const backgroundServices = useBackgroundServiceAPIContext();

const isDappExplorerEnabled = posthog.isFeatureEnabled(ExperimentName.DAPP_EXPLORER);
const isDappExplorerEnabled = posthog.isFeatureFlagEnabled(ExperimentName.DAPP_EXPLORER);
const currentLocation = location?.pathname;
const isWalletIconActive =
currentLocation === walletRoutePaths.assets || includesCoin.test(currentLocation) || currentLocation === '/';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { RemoteApiProperties, RemoteApiPropertyType } from '@cardano-sdk/web-ext
import { getBaseUrlForChain, getMagicForChain } from '@src/utils/chain';
import { BackgroundService, UserIdService as UserIdServiceInterface } from '../types';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { logger } from '@lace/common';
import { config } from '@src/config';
import Bottleneck from 'bottleneck';
import { RateLimiter } from '@cardano-sdk/cardano-services-client';
import { ExperimentName } from '../types/feature-flags';

export const backgroundServiceProperties: RemoteApiProperties<BackgroundService> = {
requestMessage$: RemoteApiPropertyType.HotObservable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import { getBaseUrlForChain, getMagicForChain } from '@utils/chain';
import { cacheNamiMetadataSubscription } from './cache-nami-metadata';
import { logger } from '@lace/common';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { requestMessage$ } from './services/utilityServices';
import { BackgroundStorage, MessageTypes } from '../types';
import { ExtensionDocumentStore } from './storage/extension-document-store';
Expand All @@ -50,6 +49,7 @@ import { ExtensionBlobCollectionStore } from './storage/extension-blob-collectio
import { migrateCollectionStore, migrateWalletStores, shouldAttemptWalletStoresMigration } from './storage/migrations';
import { isLacePopupOpen$, createUserSessionTracker, isLaceTabActive$ } from './session';
import { TrackerSubject } from '@cardano-sdk/util-rxjs';
import { ExperimentName } from '../types/feature-flags';

export const dAppConnectorActivity$ = new Subject<void>();
const pollController$ = new TrackerSubject(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import { z, ZodIssueCode } from 'zod';
import { DeepRequired } from 'utility-types';

export enum ExperimentName {
CREATE_PAPER_WALLET = 'create-paper-wallet',
RESTORE_PAPER_WALLET = 'restore-paper-wallet',
USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode',
SHARED_WALLETS = 'shared-wallets',
WEBSOCKET_API = 'websocket-api',
DAPP_EXPLORER = 'dapp-explorer'
}

export type FeatureFlag = `${ExperimentName}`;

type FeatureFlags = {
[key in FeatureFlag]: boolean;
};

export type GroupedFeatureFlags = {
[number: number]: FeatureFlags;
};

const parseJsonPreprocessor = (value: unknown, ctx: z.RefinementCtx) => {
if (typeof value === 'string') {
try {
Expand Down Expand Up @@ -59,3 +78,20 @@ export const featureFlagSchema = {
common: z.preprocess(parseJsonPreprocessor, commonSchema),
dappExplorer: z.preprocess(parseJsonPreprocessor, dappExplorerSchema)
};

type FeatureFlagCommonPayload = {
allowedNetworks: ('preview' | 'preprod' | 'mainnet' | 'sanchonet')[];
};

// Using `false` as a fallback type for the payload, as it can be optional, and we (sadly) don't have
// strict null checks enabled so `false` is a replacement for `undefined` in this case
// eslint-disable-next-line @typescript-eslint/ban-types
type FeatureFlagPayload<T extends Record<string, unknown> = {}> = (FeatureFlagCommonPayload & T) | false;

type FeatureFlagCustomPayloads = {
[ExperimentName.DAPP_EXPLORER]: FeatureFlagPayload<FeatureFlagDappExplorerSchema>;
};

export type FeatureFlagPayloads = {
[key in FeatureFlag]: FeatureFlagPayload;
} & FeatureFlagCustomPayloads;

Check failure on line 97 in apps/browser-extension-wallet/src/lib/scripts/types/feature-flags.ts

View workflow job for this annotation

GitHub Actions / Release package

Insert `⏎·`
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AuthorizedDappStorage } from '@src/types/dappConnector';
import type { Message } from './background-service';
import { ADAPrices } from './prices';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { FeatureFlagPayloads, GroupedFeatureFlags } from '@lib/scripts/types/feature-flags';

export interface PendingMigrationState {
from: string;
Expand Down Expand Up @@ -29,7 +29,8 @@ export interface BackgroundStorage {
fiatPrices?: { prices: ADAPrices; timestamp: number };
userId?: string;
usePersistentUserId?: boolean;
featureFlags?: Record<number, Record<ExperimentName, string | boolean>>;
featureFlags?: GroupedFeatureFlags;
featureFlagPayloads?: FeatureFlagPayloads;
customSubmitTxUrl?: string;
namiMigration?: {
completed: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ExperimentName, ExperimentsConfig, FallbackConfiguration } from './types';
import { ExperimentName } from '@lib/scripts/types/feature-flags';
import { ExperimentsConfig, FallbackConfiguration } from './types';

export const getDefaultFeatureFlags = (): FallbackConfiguration => ({
[ExperimentName.CREATE_PAPER_WALLET]: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { ExperimentName, ExperimentsConfigStatus } from './types';
import { ExperimentsConfigStatus } from './types';
import { ExperimentName } from '@lib/scripts/types/feature-flags';

type ExperimentsContext = {
areExperimentsLoading: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import { ExperimentName } from '@lib/scripts/types/feature-flags';

export enum ExperimentsConfigStatus {
IDLE = 'idle',
LOADING = 'loading',
LOADED = 'loaded',
ERROR = 'error'
}

export enum ExperimentName {
CREATE_PAPER_WALLET = 'create-paper-wallet',
RESTORE_PAPER_WALLET = 'restore-paper-wallet',
USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode',
SHARED_WALLETS = 'shared-wallets',
WEBSOCKET_API = 'websocket-api',
DAPP_EXPLORER = 'dapp-explorer'
}

interface FeatureFlag {
value: boolean;
default: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,22 @@ import {
} from './config';
import { BackgroundService, UserIdService } from '@lib/scripts/types';
import { experiments, getDefaultFeatureFlags } from '@providers/ExperimentsProvider/config';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { BehaviorSubject, distinctUntilChanged, Observable, Subscription } from 'rxjs';
import { PostHogAction, PostHogProperties } from '@lace/common';
import {
ExperimentName,
FeatureFlag,
FeatureFlagCommonSchema,
FeatureFlagDappExplorerSchema,
FeatureFlagPayloads,
featureFlagSchema,
networksEnumSchema,
NetworksEnumSchema
} from './featureFlagPayloadsSchema';

type FeatureFlag = `${ExperimentName}`;
GroupedFeatureFlags,
NetworksEnumSchema,
networksEnumSchema
} from '@lib/scripts/types/feature-flags';

const isNetworkOfExpectedSchema = (n: string): n is NetworksEnumSchema => networksEnumSchema.safeParse(n).success;

type FeatureFlags = {
[key in FeatureFlag]: boolean;
};

type GroupedFeatureFlags = {
[number: number]: FeatureFlags;
};

type FeatureFlagCommonPayload = {
allowedNetworks: ('preview' | 'preprod' | 'mainnet' | 'sanchonet')[];
};

// Using `false` as a fallback type for the payload, as it can be optional, and we (sadly) don't have
// strict null checks enabled so `false` is a replacement for `undefined` in this case
// eslint-disable-next-line @typescript-eslint/ban-types
type FeatureFlagPayload<T extends Record<string, unknown> = {}> = (FeatureFlagCommonPayload & T) | false;

type FeatureFlagCustomPayloads = {
[ExperimentName.DAPP_EXPLORER]: FeatureFlagPayload<FeatureFlagDappExplorerSchema>;
};

type FeatureFlagPayloads = {
[key in FeatureFlag]: FeatureFlagPayload;
} &
FeatureFlagCustomPayloads;

/**
* PostHog API reference:
* https://posthog.com/docs/libraries/js
Expand Down Expand Up @@ -94,6 +69,12 @@ export class PostHogClient<Action extends string = string> {
.getBackgroundStorage()
.then((storage) => {
this.optedInBeta$.next(storage?.optedInBeta ?? false);
if (storage.featureFlags) {
this.featureFlags = storage.featureFlags;
}
if (storage.featureFlagPayloads) {
this.featureFlagPayloads = storage.featureFlagPayloads;
}

this.initSuccess = this.userIdService
.getUserId(chain.networkMagic)
Expand Down Expand Up @@ -140,6 +121,10 @@ export class PostHogClient<Action extends string = string> {
[Wallet.Cardano.NetworkMagics.Preview]: getDefaultFeatureFlags(),
[Wallet.Cardano.NetworkMagics.Sanchonet]: getDefaultFeatureFlags()
};
this.featureFlagPayloads = Object.values(ExperimentName).reduce((payloads, featureFlagName) => {
payloads[featureFlagName] = false;
return payloads;
}, {} as FeatureFlagPayloads);
}

static getInstance(
Expand Down Expand Up @@ -284,7 +269,7 @@ export class PostHogClient<Action extends string = string> {
// if the variant does not exist, we need to check for out cache
if (!variant) {
const backgroundStorage = await this.backgroundServiceUtils.getBackgroundStorage();
return (backgroundStorage?.featureFlags?.[this.chain.networkMagic][key] as string) || experiments[key].default;
return backgroundStorage?.featureFlags?.[this.chain.networkMagic][key] || experiments[key].default;
}

return variant;
Expand Down Expand Up @@ -330,7 +315,8 @@ export class PostHogClient<Action extends string = string> {
if (!PostHogClient.areAllFeatureFlagsEmpty(this.featureFlags)) {
// save current posthog config in background storage
await this.backgroundServiceUtils.setBackgroundStorage({
featureFlags: this.featureFlags
featureFlags: this.featureFlags,
featureFlagPayloads: this.featureFlagPayloads
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export { PostHogClient } from './PostHogClient';
export { POSTHOG_ENABLED, POSTHOG_OPTED_OUT_EVENTS_DISABLED, POSTHOG_EXCLUDED_EVENTS } from './config';
export * from './featureFlagPayloadsSchema';
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { sideMenuConfig } from './side-menu-config';
import { SideMenuContent } from './SideMenuContent';
import { walletRoutePaths as routes } from '@routes/wallet-paths';
import { useWalletStore } from '@stores';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { ExperimentName } from '@lib/scripts/types/feature-flags';

const isPathAvailable = (path: string) => Object.values(routes).includes(path);

Expand All @@ -21,7 +21,7 @@ export const SideMenu = (): React.ReactElement => {
} = useHistory();
const analytics = useAnalyticsContext();
const posthog = usePostHogClientContext();
const isDappExplorerEnabled = posthog.isFeatureEnabled(ExperimentName.DAPP_EXPLORER);
const isDappExplorerEnabled = posthog.isFeatureFlagEnabled(ExperimentName.DAPP_EXPLORER);

const { isSharedWallet } = useWalletStore();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Layout, SectionLayout } from '@views/browser/components';
import SimpleView from './SimpleView';
import React from 'react';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { ExperimentName } from '@lib/scripts/types/feature-flags';

export const DAppExplorer: React.FC = () => {
const posthog = usePostHogClientContext();
const dappExplorerEnabled = posthog.isFeatureEnabled(ExperimentName.DAPP_EXPLORER);
const dappExplorerEnabled = posthog.isFeatureFlagEnabled(ExperimentName.DAPP_EXPLORER);
return (
<>
<Layout noAside>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import ArrowChartUp from '../../../../../../../../../assets/icons/arrow-chart-up
import ArrowsOppositeDirection from '../../../../../../../../../assets/icons/arrows-opposite-direction.component.svg';
import Ticket from '../../../../../../../../../assets/icons/ticket-icon.component.svg';
import Persons from '../../../../../../../../../assets/icons/persons.component.svg';
import { dappCategoriesEnumSchema } from '@providers/PostHogClientProvider';
import { dappCategoriesEnumSchema } from '@lib/scripts/types/feature-flags';

const mapOfCategoryToIcon: Record<DefaultCategory, React.ComponentType> = {
[DefaultCategory.All]: ShowAll,
Expand Down

0 comments on commit 2097040

Please sign in to comment.