mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-06 02:48:28 +00:00
fix(frontend): bound namespace page queries
Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
parent
9de8a51d89
commit
18c9370318
5 changed files with 104 additions and 12 deletions
|
|
@ -238,6 +238,36 @@ describe('namespaceApi.listMinePage', () => {
|
|||
size: 25,
|
||||
})
|
||||
})
|
||||
|
||||
it('encodes namespace filters without issuing an unbounded request', async () => {
|
||||
window.__SKILLHUB_RUNTIME_CONFIG__ = { apiBaseUrl: 'https://api.example.com' }
|
||||
const fetchMock = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
code: 0,
|
||||
msg: 'ok',
|
||||
data: { items: [], total: 0, page: 1, size: 20 },
|
||||
timestamp: '2026-05-07T00:00:00Z',
|
||||
requestId: 'req-filtered',
|
||||
}),
|
||||
})
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
|
||||
await namespaceApi.listMinePage({
|
||||
page: 1,
|
||||
size: 20,
|
||||
status: 'ACTIVE',
|
||||
q: 'team ai',
|
||||
slug: 'team-ai',
|
||||
roles: ['OWNER', 'ADMIN'],
|
||||
})
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1)
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
'https://api.example.com/api/web/me/namespaces/page?page=1&size=20&status=ACTIVE&q=team+ai&slug=team-ai&roles=OWNER&roles=ADMIN',
|
||||
expect.objectContaining({ headers: expect.any(Headers) }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDirectAuthRuntimeConfig', () => {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,15 @@ import type {
|
|||
import { ApiError } from '@/shared/lib/api-error'
|
||||
import i18n from '@/i18n/config'
|
||||
|
||||
export interface MyNamespacePageParams {
|
||||
page?: number
|
||||
size?: number
|
||||
status?: 'ACTIVE' | 'FROZEN' | 'ARCHIVED'
|
||||
q?: string
|
||||
slug?: string
|
||||
roles?: Array<'OWNER' | 'ADMIN' | 'MEMBER'>
|
||||
}
|
||||
|
||||
/**
|
||||
* Front-end API foundation for generated OpenAPI calls and hand-written convenience wrappers.
|
||||
*
|
||||
|
|
@ -643,10 +652,21 @@ export const namespaceApi = {
|
|||
return fetchJson<ManagedNamespace[]>(`${WEB_API_PREFIX}/me/namespaces`)
|
||||
},
|
||||
|
||||
async listMinePage(params: { page?: number; size?: number } = {}): Promise<PagedResponse<ManagedNamespace>> {
|
||||
async listMinePage(params: MyNamespacePageParams = {}): Promise<PagedResponse<ManagedNamespace>> {
|
||||
const page = params.page ?? 0
|
||||
const size = params.size ?? 20
|
||||
return fetchJson<PagedResponse<ManagedNamespace>>(`${WEB_API_PREFIX}/me/namespaces/page?page=${page}&size=${size}`)
|
||||
const query = new URLSearchParams({ page: String(page), size: String(size) })
|
||||
if (params.status) {
|
||||
query.set('status', params.status)
|
||||
}
|
||||
if (params.q?.trim()) {
|
||||
query.set('q', params.q.trim())
|
||||
}
|
||||
if (params.slug?.trim()) {
|
||||
query.set('slug', normalizeNamespaceSlug(params.slug))
|
||||
}
|
||||
params.roles?.forEach((role) => query.append('roles', role))
|
||||
return fetchJson<PagedResponse<ManagedNamespace>>(`${WEB_API_PREFIX}/me/namespaces/page?${query.toString()}`)
|
||||
},
|
||||
|
||||
async getDetail(slug: string): Promise<Namespace> {
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ export function MyNamespacesPage() {
|
|||
const canCreateNamespace = hasRole('SKILL_ADMIN') || hasRole('SUPER_ADMIN')
|
||||
const [page, setPage] = useState(0)
|
||||
const [pendingAction, setPendingAction] = useState<PendingNamespaceAction | null>(null)
|
||||
const { data: namespacePage, isLoading } = useMyNamespacesPage(page, PAGE_SIZE)
|
||||
const { data: namespacePage, isLoading } = useMyNamespacesPage({ page, size: PAGE_SIZE })
|
||||
const freezeMutation = useFreezeNamespace()
|
||||
const unfreezeMutation = useUnfreezeNamespace()
|
||||
const archiveMutation = useArchiveNamespace()
|
||||
|
|
|
|||
|
|
@ -59,14 +59,42 @@ describe('use-namespace-queries exports', () => {
|
|||
}))
|
||||
})
|
||||
|
||||
it('passes page and size to the paged my namespaces query', async () => {
|
||||
it('passes bounded filters to a single paged my namespaces query', async () => {
|
||||
const mod = await import('./use-namespace-queries')
|
||||
|
||||
mod.useMyNamespacesPage(3, 15)
|
||||
mod.useMyNamespacesPage({
|
||||
page: 3,
|
||||
size: 15,
|
||||
status: 'ACTIVE',
|
||||
q: 'team',
|
||||
slug: 'team-ai',
|
||||
roles: ['OWNER', 'ADMIN'],
|
||||
})
|
||||
|
||||
expect(useQueryMock).toHaveBeenCalledWith(expect.objectContaining({
|
||||
queryKey: ['namespaces', 'my', { page: 3, size: 15 }],
|
||||
queryKey: ['namespaces', 'my', {
|
||||
page: 3,
|
||||
size: 15,
|
||||
status: 'ACTIVE',
|
||||
q: 'team',
|
||||
slug: 'team-ai',
|
||||
roles: ['OWNER', 'ADMIN'],
|
||||
}],
|
||||
}))
|
||||
const queryOptions = useQueryMock.mock.calls[useQueryMock.mock.calls.length - 1]?.[0]
|
||||
listMinePageMock.mockResolvedValue({ items: [], total: 101, page: 3, size: 15 })
|
||||
|
||||
await queryOptions.queryFn()
|
||||
|
||||
expect(listMinePageMock).toHaveBeenCalledTimes(1)
|
||||
expect(listMinePageMock).toHaveBeenCalledWith({
|
||||
page: 3,
|
||||
size: 15,
|
||||
status: 'ACTIVE',
|
||||
q: 'team',
|
||||
slug: 'team-ai',
|
||||
roles: ['OWNER', 'ADMIN'],
|
||||
})
|
||||
})
|
||||
|
||||
it('fetches every page for compatibility consumers instead of truncating after the first page', async () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import type { Namespace, NamespaceMember, ManagedNamespace, CreateNamespaceRequest, NamespaceCandidateUser, NamespaceRole, BatchMemberResponse, PagedResponse } from '@/api/types'
|
||||
import { namespaceApi } from '@/api/client'
|
||||
import { namespaceApi, type MyNamespacePageParams } from '@/api/client'
|
||||
import { replaceNamespaceMemberRole } from '@/shared/lib/namespace-member-cache'
|
||||
import { shouldEnableNamespaceMemberCandidates } from './skill-query-helpers'
|
||||
|
||||
|
|
@ -25,8 +25,21 @@ async function getMyNamespaces(): Promise<ManagedNamespace[]> {
|
|||
return namespaces
|
||||
}
|
||||
|
||||
async function getMyNamespacesPage(page = 0, size = MY_NAMESPACES_PAGE_SIZE): Promise<PagedResponse<ManagedNamespace>> {
|
||||
return namespaceApi.listMinePage({ page, size })
|
||||
function normalizeMyNamespacePageParams(params: MyNamespacePageParams = {}): MyNamespacePageParams {
|
||||
const q = params.q?.trim()
|
||||
const slug = params.slug?.trim()
|
||||
return {
|
||||
page: params.page ?? 0,
|
||||
size: params.size ?? MY_NAMESPACES_PAGE_SIZE,
|
||||
...(params.status ? { status: params.status } : {}),
|
||||
...(q ? { q } : {}),
|
||||
...(slug ? { slug } : {}),
|
||||
...(params.roles?.length ? { roles: [...params.roles] } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
async function getMyNamespacesPage(params: MyNamespacePageParams): Promise<PagedResponse<ManagedNamespace>> {
|
||||
return namespaceApi.listMinePage(params)
|
||||
}
|
||||
|
||||
async function createNamespace(request: CreateNamespaceRequest): Promise<Namespace> {
|
||||
|
|
@ -80,10 +93,11 @@ export function useMyNamespaces(enabled = true) {
|
|||
})
|
||||
}
|
||||
|
||||
export function useMyNamespacesPage(page = 0, size = MY_NAMESPACES_PAGE_SIZE, enabled = true) {
|
||||
export function useMyNamespacesPage(params: MyNamespacePageParams = {}, enabled = true) {
|
||||
const normalizedParams = normalizeMyNamespacePageParams(params)
|
||||
return useQuery({
|
||||
queryKey: ['namespaces', 'my', { page, size }],
|
||||
queryFn: () => getMyNamespacesPage(page, size),
|
||||
queryKey: ['namespaces', 'my', normalizedParams],
|
||||
queryFn: () => getMyNamespacesPage(normalizedParams),
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue