Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions frontend/src/constants/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { retrieveValue, storeValue } from '@/utils/storage'

export type FeatureFlag = 'useWebsockets' | 'sendViaWebsocket' | 'refreshStaleData'
export type FeatureFlag = 'sendViaWebsocket' | 'refreshStaleData'

export interface FeatureFlagDefinition {
name: FeatureFlag
Expand All @@ -9,14 +9,9 @@ export interface FeatureFlagDefinition {
}

export const featureFlagDefinitions: FeatureFlagDefinition[] = [
{
name: 'useWebsockets',
description: 'Use websockets',
defaultValue: false,
},
{
name: 'sendViaWebsocket',
description: 'Send requests via WebSocket (requires websockets)',
description: 'Send requests via WebSocket',
defaultValue: false,
},
{
Expand Down
23 changes: 2 additions & 21 deletions frontend/src/utils/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export class WebSocketManager {
private socket: WebSocket | null = null
private retryCount = 0
private manualClose = false
private enabled: boolean = store.getState().featureFlags.useWebsockets
private listeners: Map<WSEvent, Set<WebSocketEventListener>> = new Map()
private pendingRequests: Map<
string,
Expand All @@ -30,25 +29,7 @@ export class WebSocketManager {
private reconnectTimer?: ReturnType<typeof setTimeout>

private constructor() {
if (this.enabled) {
this.connect()
}

store.subscribe(() => {
const newState = store.getState()
const newEnabledState = newState.featureFlags.useWebsockets

// If websockets were disabled, disconnect
if (this.enabled && !newEnabledState) {
this.enabled = newEnabledState
this.disconnect()
}
// If websockets were enabled, try to connect
else if (!this.enabled && newEnabledState) {
this.enabled = newEnabledState
this.connect()
}
})
this.connect()
}

static getInstance(): WebSocketManager {
Expand Down Expand Up @@ -305,7 +286,7 @@ export class WebSocketManager {
}

private scheduleReconnect() {
if (this.manualClose || !this.enabled) {
if (this.manualClose) {
return
}

Expand Down
7 changes: 1 addition & 6 deletions frontend/src/views/Navigation/SyncStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { RootState } from '@/store/store'
import { SyncState } from '@/models/sync'

interface SyncStatusProps {
websocketsEnabled: boolean

style?: React.CSSProperties
userStatus: SyncState
userError: string | null
Expand Down Expand Up @@ -77,9 +75,7 @@ class SyncStatusImpl extends React.Component<SyncStatusProps, SyncStatusState> {
{ name: 'Tokens', status: tokensStatus, error: tokensError },
]

if (this.props.websocketsEnabled) {
statuses.push({ name: 'WebSocket', status: wsStatus, error: wsError })
}
statuses.push({ name: 'WebSocket', status: wsStatus, error: wsError })

return statuses
}
Expand Down Expand Up @@ -154,7 +150,6 @@ class SyncStatusImpl extends React.Component<SyncStatusProps, SyncStatusState> {
}

const mapStateToProps = (state: RootState) => ({
websocketsEnabled: state.featureFlags.useWebsockets,
userStatus: state.user.status,
userError: state.user.error,
tasksStatus: state.tasks.status,
Expand Down
54 changes: 28 additions & 26 deletions frontend/test/constants/featureFlags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ describe('featureFlags', () => {

describe('setFeatureFlag', () => {
it('should store feature flag with correct prefix', () => {
setFeatureFlag('useWebsockets', true)
const storedValue = localStorage.getItem(FEATURE_FLAG_PREFIX + 'useWebsockets')
setFeatureFlag('sendViaWebsocket', true)
const storedValue = localStorage.getItem(
FEATURE_FLAG_PREFIX + 'sendViaWebsocket',
)
expect(storedValue).toBe('true')
})

Expand All @@ -25,36 +27,36 @@ describe('featureFlags', () => {
})

it('should update existing feature flag', () => {
setFeatureFlag('useWebsockets', true)
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'useWebsockets')).toBe('true')
setFeatureFlag('sendViaWebsocket', true)
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'sendViaWebsocket')).toBe('true')

setFeatureFlag('useWebsockets', false)
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'useWebsockets')).toBe('false')
setFeatureFlag('sendViaWebsocket', false)
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'sendViaWebsocket')).toBe('false')
})

it('should handle multiple feature flags independently', () => {
setFeatureFlag('useWebsockets', true)
setFeatureFlag('sendViaWebsocket', true)
setFeatureFlag('refreshStaleData', false)

expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'useWebsockets')).toBe('true')
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'sendViaWebsocket')).toBe('true')
expect(localStorage.getItem(FEATURE_FLAG_PREFIX + 'refreshStaleData')).toBe('false')
})
})

describe('getFeatureFlag', () => {
it('should return stored value when flag exists', () => {
localStorage.setItem(FEATURE_FLAG_PREFIX + 'useWebsockets', 'true')
const result = getFeatureFlag('useWebsockets', false)
localStorage.setItem(FEATURE_FLAG_PREFIX + 'sendViaWebsocket', 'true')
const result = getFeatureFlag('sendViaWebsocket', false)
expect(result).toBe(true)
})

it('should return default value when flag does not exist', () => {
const result = getFeatureFlag('useWebsockets', false)
const result = getFeatureFlag('sendViaWebsocket', false)
expect(result).toBe(false)
})

it('should return different default values correctly', () => {
const result1 = getFeatureFlag('useWebsockets', true)
const result1 = getFeatureFlag('sendViaWebsocket', true)
expect(result1).toBe(true)

const result2 = getFeatureFlag('refreshStaleData', false)
Expand All @@ -68,13 +70,13 @@ describe('featureFlags', () => {
})

it('should retrieve true value correctly', () => {
localStorage.setItem(FEATURE_FLAG_PREFIX + 'useWebsockets', 'true')
const result = getFeatureFlag('useWebsockets', false)
localStorage.setItem(FEATURE_FLAG_PREFIX + 'sendViaWebsocket', 'true')
const result = getFeatureFlag('sendViaWebsocket', false)
expect(result).toBe(true)
})

it('should handle all feature flag types', () => {
const flags: FeatureFlag[] = ['useWebsockets', 'sendViaWebsocket', 'refreshStaleData']
const flags: FeatureFlag[] = ['sendViaWebsocket', 'refreshStaleData']

flags.forEach(flag => {
setFeatureFlag(flag, true)
Expand All @@ -85,8 +87,8 @@ describe('featureFlags', () => {

describe('getFeatureFlag and setFeatureFlag integration', () => {
it('should store and retrieve feature flag correctly', () => {
setFeatureFlag('useWebsockets', true)
expect(getFeatureFlag('useWebsockets', false)).toBe(true)
setFeatureFlag('sendViaWebsocket', true)
expect(getFeatureFlag('sendViaWebsocket', false)).toBe(true)
})

it('should update and retrieve updated value', () => {
Expand All @@ -98,25 +100,25 @@ describe('featureFlags', () => {
})

it('should maintain feature flag values independently', () => {
setFeatureFlag('useWebsockets', true)
setFeatureFlag('sendViaWebsocket', true)
setFeatureFlag('refreshStaleData', false)

expect(getFeatureFlag('useWebsockets', false)).toBe(true)
expect(getFeatureFlag('sendViaWebsocket', false)).toBe(true)
expect(getFeatureFlag('refreshStaleData', true)).toBe(false)
})

it('should handle toggling feature flags', () => {
// Initial state
setFeatureFlag('useWebsockets', false)
expect(getFeatureFlag('useWebsockets', false)).toBe(false)
setFeatureFlag('sendViaWebsocket', false)
expect(getFeatureFlag('sendViaWebsocket', false)).toBe(false)

// Toggle on
setFeatureFlag('useWebsockets', true)
expect(getFeatureFlag('useWebsockets', false)).toBe(true)
setFeatureFlag('sendViaWebsocket', true)
expect(getFeatureFlag('sendViaWebsocket', false)).toBe(true)

// Toggle off
setFeatureFlag('useWebsockets', false)
expect(getFeatureFlag('useWebsockets', false)).toBe(false)
setFeatureFlag('sendViaWebsocket', false)
expect(getFeatureFlag('sendViaWebsocket', false)).toBe(false)
})
})

Expand All @@ -126,7 +128,7 @@ describe('featureFlags', () => {
})

it('should be used as prefix for all feature flags', () => {
setFeatureFlag('useWebsockets', true)
setFeatureFlag('sendViaWebsocket', true)
const keys = Object.keys(localStorage)
const flagKeys = keys.filter(key => key.startsWith(FEATURE_FLAG_PREFIX))
expect(flagKeys.length).toBeGreaterThan(0)
Expand Down
Loading