mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 12:37:20 +08:00
refactor plugin installation query state
This commit is contained in:
parent
4c0fc56dd5
commit
1785dce04e
@ -86,15 +86,13 @@ const PluginPage = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (bundleInfo) {
|
if (bundleInfo) {
|
||||||
// bundleInfo is a JSON string from URL, needs parsing
|
|
||||||
try {
|
try {
|
||||||
const parsedBundleInfo = typeof bundleInfo === 'string' ? JSON.parse(bundleInfo) : bundleInfo
|
const { data } = await fetchBundleInfoFromMarketPlace(bundleInfo)
|
||||||
const { data } = await fetchBundleInfoFromMarketPlace(parsedBundleInfo)
|
|
||||||
setDependencies(data.version.dependencies)
|
setDependencies(data.version.dependencies)
|
||||||
showInstallFromMarketplace()
|
showInstallFromMarketplace()
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (error) {
|
||||||
console.error('Failed to parse bundle info:', e)
|
console.error('Failed to load bundle info:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|||||||
@ -474,9 +474,10 @@ describe('useQueryParams hooks', () => {
|
|||||||
describe('usePluginInstallation', () => {
|
describe('usePluginInstallation', () => {
|
||||||
it('should parse package ids from JSON arrays', () => {
|
it('should parse package ids from JSON arrays', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
const bundleInfo = { org: 'org', name: 'bundle', version: '1.0.0' }
|
||||||
const { result } = renderWithAdapter(
|
const { result } = renderWithAdapter(
|
||||||
() => usePluginInstallation(),
|
() => usePluginInstallation(),
|
||||||
'?package-ids=%5B%22org%2Fplugin%22%5D&bundle-info=bundle',
|
`?package-ids=%5B%22org%2Fplugin%22%5D&bundle-info=${encodeURIComponent(JSON.stringify(bundleInfo))}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -484,7 +485,7 @@ describe('useQueryParams hooks', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(state.packageId).toBe('org/plugin')
|
expect(state.packageId).toBe('org/plugin')
|
||||||
expect(state.bundleInfo).toBe('bundle')
|
expect(state.bundleInfo).toEqual(bundleInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return raw package id when JSON parsing fails', () => {
|
it('should return raw package id when JSON parsing fails', () => {
|
||||||
@ -532,24 +533,26 @@ describe('useQueryParams hooks', () => {
|
|||||||
|
|
||||||
it('should set bundle info when provided', async () => {
|
it('should set bundle info when provided', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
const bundleInfo = { org: 'org', name: 'bundle', version: '1.0.0' }
|
||||||
const { result, onUrlUpdate } = renderWithAdapter(() => usePluginInstallation())
|
const { result, onUrlUpdate } = renderWithAdapter(() => usePluginInstallation())
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
act(() => {
|
act(() => {
|
||||||
result.current[1]({ bundleInfo: 'bundle' })
|
result.current[1]({ bundleInfo })
|
||||||
})
|
})
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
|
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
|
||||||
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
|
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
|
||||||
expect(update.searchParams.get('bundle-info')).toBe('bundle')
|
expect(update.searchParams.get('bundle-info')).toBe(JSON.stringify(bundleInfo))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should clear installation params when state is null', async () => {
|
it('should clear installation params when state is null', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
const bundleInfo = { org: 'org', name: 'bundle', version: '1.0.0' }
|
||||||
const { result, onUrlUpdate } = renderWithAdapter(
|
const { result, onUrlUpdate } = renderWithAdapter(
|
||||||
() => usePluginInstallation(),
|
() => usePluginInstallation(),
|
||||||
'?package-ids=%5B%22org%2Fplugin%22%5D&bundle-info=bundle',
|
`?package-ids=%5B%22org%2Fplugin%22%5D&bundle-info=${encodeURIComponent(JSON.stringify(bundleInfo))}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -566,9 +569,10 @@ describe('useQueryParams hooks', () => {
|
|||||||
|
|
||||||
it('should preserve bundle info when only packageId is updated', async () => {
|
it('should preserve bundle info when only packageId is updated', async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
const bundleInfo = { org: 'org', name: 'bundle', version: '1.0.0' }
|
||||||
const { result, onUrlUpdate } = renderWithAdapter(
|
const { result, onUrlUpdate } = renderWithAdapter(
|
||||||
() => usePluginInstallation(),
|
() => usePluginInstallation(),
|
||||||
'?bundle-info=bundle',
|
`?bundle-info=${encodeURIComponent(JSON.stringify(bundleInfo))}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@ -579,7 +583,7 @@ describe('useQueryParams hooks', () => {
|
|||||||
// Assert
|
// Assert
|
||||||
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
|
await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled())
|
||||||
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
|
const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0]
|
||||||
expect(update.searchParams.get('bundle-info')).toBe('bundle')
|
expect(update.searchParams.get('bundle-info')).toBe(JSON.stringify(bundleInfo))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -141,6 +141,47 @@ export function useMarketplaceFilters() {
|
|||||||
*/
|
*/
|
||||||
const PACKAGE_IDS_PARAM = 'package-ids'
|
const PACKAGE_IDS_PARAM = 'package-ids'
|
||||||
const BUNDLE_INFO_PARAM = 'bundle-info'
|
const BUNDLE_INFO_PARAM = 'bundle-info'
|
||||||
|
type BundleInfoQuery = {
|
||||||
|
org: string
|
||||||
|
name: string
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseAsPackageId = createParser<string>({
|
||||||
|
parse: (value) => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value)
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
const first = parsed[0]
|
||||||
|
return typeof first === 'string' ? first : null
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
serialize: value => JSON.stringify([value]),
|
||||||
|
})
|
||||||
|
|
||||||
|
const parseAsBundleInfo = createParser<BundleInfoQuery>({
|
||||||
|
parse: (value) => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(value) as Partial<BundleInfoQuery>
|
||||||
|
if (parsed
|
||||||
|
&& typeof parsed.org === 'string'
|
||||||
|
&& typeof parsed.name === 'string'
|
||||||
|
&& typeof parsed.version === 'string') {
|
||||||
|
return { org: parsed.org, name: parsed.name, version: parsed.version }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
serialize: value => JSON.stringify(value),
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to manage plugin installation state via URL
|
* Hook to manage plugin installation state via URL
|
||||||
@ -149,53 +190,45 @@ const BUNDLE_INFO_PARAM = 'bundle-info'
|
|||||||
* @example
|
* @example
|
||||||
* const [installState, setInstallState] = usePluginInstallation()
|
* const [installState, setInstallState] = usePluginInstallation()
|
||||||
* setInstallState({ packageId: 'org/plugin' }) // Sets ?package-ids=["org/plugin"]
|
* setInstallState({ packageId: 'org/plugin' }) // Sets ?package-ids=["org/plugin"]
|
||||||
|
* setInstallState({ bundleInfo: { org: 'org', name: 'bundle', version: '1.0.0' } }) // Sets ?bundle-info=...
|
||||||
* setInstallState(null) // Clears installation params
|
* setInstallState(null) // Clears installation params
|
||||||
*/
|
*/
|
||||||
export function usePluginInstallation() {
|
export function usePluginInstallation() {
|
||||||
const [packageIds, setPackageIds] = useQueryState(
|
const [installState, setInstallStateState] = useQueryStates(
|
||||||
PACKAGE_IDS_PARAM,
|
{
|
||||||
parseAsString,
|
packageId: parseAsPackageId,
|
||||||
)
|
bundleInfo: parseAsBundleInfo,
|
||||||
const [bundleInfo, setBundleInfo] = useQueryState(
|
},
|
||||||
BUNDLE_INFO_PARAM,
|
{
|
||||||
parseAsString,
|
urlKeys: {
|
||||||
|
packageId: PACKAGE_IDS_PARAM,
|
||||||
|
bundleInfo: BUNDLE_INFO_PARAM,
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const setInstallState = useCallback(
|
const setInstallState = useCallback(
|
||||||
(state: { packageId?: string, bundleInfo?: string } | null) => {
|
(state: { packageId?: string, bundleInfo?: BundleInfoQuery } | null) => {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
setPackageIds(null)
|
setInstallStateState(null)
|
||||||
setBundleInfo(null)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (state.packageId) {
|
const patch: { packageId?: string, bundleInfo?: BundleInfoQuery } = {}
|
||||||
// Store as JSON array for consistency with existing code
|
if (state.packageId)
|
||||||
setPackageIds(JSON.stringify([state.packageId]))
|
patch.packageId = state.packageId
|
||||||
}
|
if (state.bundleInfo)
|
||||||
if (state.bundleInfo) {
|
patch.bundleInfo = state.bundleInfo
|
||||||
setBundleInfo(state.bundleInfo)
|
if (Object.keys(patch).length === 0)
|
||||||
}
|
return
|
||||||
|
setInstallStateState(patch)
|
||||||
},
|
},
|
||||||
[setBundleInfo, setPackageIds],
|
[setInstallStateState],
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse packageIds from JSON array
|
|
||||||
const currentPackageId = packageIds
|
|
||||||
? (() => {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(packageIds)
|
|
||||||
return Array.isArray(parsed) ? parsed[0] : packageIds
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return packageIds
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
: null
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
packageId: currentPackageId,
|
packageId: installState.packageId,
|
||||||
bundleInfo,
|
bundleInfo: installState.bundleInfo,
|
||||||
},
|
},
|
||||||
setInstallState,
|
setInstallState,
|
||||||
] as const
|
] as const
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user