mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
feat: gate node replacement loading on server feature flag (#8750)
## Summary
Gates the node replacement store's `load()` call behind the
`node_replacements` server feature flag, so the frontend only calls
`/api/node_replacements` when the backend advertises support.
## Changes
- Added `NODE_REPLACEMENTS = 'node_replacements'` to `ServerFeatureFlag`
enum
- Added `nodeReplacementsEnabled` getter to `useFeatureFlags()`
- Added `api.serverSupportsFeature('node_replacements')` guard in
`useNodeReplacementStore.load()`
## Context
Without this guard, the frontend would attempt to fetch node
replacements from backends that don't support the endpoint, causing 404
errors.
Companion backend PR: https://github.com/Comfy-Org/ComfyUI/pull/12362
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8750-feat-gate-node-replacement-loading-on-server-feature-flag-3026d73d365081ec9246d77ad88f5bdc)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,8 @@ export enum ServerFeatureFlag {
|
|||||||
ONBOARDING_SURVEY_ENABLED = 'onboarding_survey_enabled',
|
ONBOARDING_SURVEY_ENABLED = 'onboarding_survey_enabled',
|
||||||
LINEAR_TOGGLE_ENABLED = 'linear_toggle_enabled',
|
LINEAR_TOGGLE_ENABLED = 'linear_toggle_enabled',
|
||||||
TEAM_WORKSPACES_ENABLED = 'team_workspaces_enabled',
|
TEAM_WORKSPACES_ENABLED = 'team_workspaces_enabled',
|
||||||
USER_SECRETS_ENABLED = 'user_secrets_enabled'
|
USER_SECRETS_ENABLED = 'user_secrets_enabled',
|
||||||
|
NODE_REPLACEMENTS = 'node_replacements'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +97,9 @@ export function useFeatureFlags() {
|
|||||||
remoteConfig.value.user_secrets_enabled ??
|
remoteConfig.value.user_secrets_enabled ??
|
||||||
api.getServerFeature(ServerFeatureFlag.USER_SECRETS_ENABLED, false)
|
api.getServerFeature(ServerFeatureFlag.USER_SECRETS_ENABLED, false)
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
get nodeReplacementsEnabled() {
|
||||||
|
return api.getServerFeature(ServerFeatureFlag.NODE_REPLACEMENTS, false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,18 @@ vi.mock('./nodeReplacementService', () => ({
|
|||||||
fetchNodeReplacements: vi.fn()
|
fetchNodeReplacements: vi.fn()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const mockNodeReplacementsEnabled = vi.hoisted(() => ({ value: true }))
|
||||||
|
|
||||||
|
vi.mock('@/composables/useFeatureFlags', () => ({
|
||||||
|
useFeatureFlags: vi.fn(() => ({
|
||||||
|
flags: {
|
||||||
|
get nodeReplacementsEnabled() {
|
||||||
|
return mockNodeReplacementsEnabled.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
function mockSettingStore(enabled: boolean) {
|
function mockSettingStore(enabled: boolean) {
|
||||||
vi.mocked(useSettingStore, { partial: true }).mockReturnValue({
|
vi.mocked(useSettingStore, { partial: true }).mockReturnValue({
|
||||||
get: vi.fn().mockImplementation((key: string) => {
|
get: vi.fn().mockImplementation((key: string) => {
|
||||||
@@ -27,9 +39,10 @@ function mockSettingStore(enabled: boolean) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStore(enabled = true) {
|
function createStore(enabled = true, featureEnabled = true) {
|
||||||
setActivePinia(createPinia())
|
setActivePinia(createPinia())
|
||||||
mockSettingStore(enabled)
|
mockSettingStore(enabled)
|
||||||
|
mockNodeReplacementsEnabled.value = featureEnabled
|
||||||
return useNodeReplacementStore()
|
return useNodeReplacementStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +51,7 @@ describe('useNodeReplacementStore', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
mockNodeReplacementsEnabled.value = true
|
||||||
store = createStore(true)
|
store = createStore(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -257,5 +271,15 @@ describe('useNodeReplacementStore', () => {
|
|||||||
expect(fetchNodeReplacements).not.toHaveBeenCalled()
|
expect(fetchNodeReplacements).not.toHaveBeenCalled()
|
||||||
expect(store.isLoaded).toBe(false)
|
expect(store.isLoaded).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not call API when server feature flag is disabled', async () => {
|
||||||
|
vi.mocked(fetchNodeReplacements).mockResolvedValue(mockReplacements)
|
||||||
|
store = createStore(true, false)
|
||||||
|
|
||||||
|
await store.load()
|
||||||
|
|
||||||
|
expect(fetchNodeReplacements).not.toHaveBeenCalled()
|
||||||
|
expect(store.isLoaded).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { NodeReplacement, NodeReplacementResponse } from './types'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import { fetchNodeReplacements } from './nodeReplacementService'
|
import { fetchNodeReplacements } from './nodeReplacementService'
|
||||||
|
|
||||||
@@ -14,8 +15,12 @@ export const useNodeReplacementStore = defineStore('nodeReplacement', () => {
|
|||||||
settingStore.get('Comfy.NodeReplacement.Enabled')
|
settingStore.get('Comfy.NodeReplacement.Enabled')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { flags } = useFeatureFlags()
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
if (!isEnabled.value || isLoaded.value) return
|
if (!isEnabled.value || isLoaded.value) return
|
||||||
|
if (!flags.nodeReplacementsEnabled) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
replacements.value = await fetchNodeReplacements()
|
replacements.value = await fetchNodeReplacements()
|
||||||
isLoaded.value = true
|
isLoaded.value = true
|
||||||
|
|||||||
Reference in New Issue
Block a user