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
106 changes: 106 additions & 0 deletions src/web/hooks/use-query/__tests__/Groups.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* SPDX-FileCopyrightText: 2026 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {describe, expect, test, testing} from '@gsa/testing';
import {rendererWith, screen, waitFor} from 'web/testing';
import CollectionCounts from 'gmp/collection/collection-counts';
import QueryFilter from 'gmp/models/filter/query-filter';
import {createSession} from 'gmp/testing';
import {useGetAllGroups, useGetGroups} from 'web/hooks/use-query/groups';

const filter = QueryFilter.fromString('name~group');
const groups = [{id: 'group-1', name: 'Group 1'}];

const createGmp = () => {
const response = {
data: groups,
meta: {
filter,
counts: new CollectionCounts({all: 1, filtered: 1, length: 1}),
},
};

return {
session: createSession({token: 'test-token'}),
settings: {},
groups: {
get: testing.fn().mockResolvedValue(response),
getAll: testing.fn().mockResolvedValue(response),
},
};
};

describe('group query hooks', () => {
test('should fetch groups with a filter', async () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
const {data} = useGetGroups({filter});
return <div data-testid="group">{data?.entities[0]?.name}</div>;
};

render(<TestComponent />);

await waitFor(() => {
expect(screen.getByTestId('group')).toHaveTextContent('Group 1');
});

expect(gmp.groups.get).toHaveBeenCalledWith({filter});
expect(gmp.groups.getAll).not.toHaveBeenCalled();
});

test('should fetch all groups with a filter', async () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
const {data} = useGetAllGroups({filter});
return <div data-testid="group">{data?.entities[0]?.name}</div>;
};

render(<TestComponent />);

await waitFor(() => {
expect(screen.getByTestId('group')).toHaveTextContent('Group 1');
});

expect(gmp.groups.getAll).toHaveBeenCalledWith({filter});
expect(gmp.groups.get).not.toHaveBeenCalled();
});

test('should not fetch groups when disabled', () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
useGetGroups({enabled: false});
useGetAllGroups({enabled: false});
return <div />;
};

render(<TestComponent />);

expect(gmp.groups.get).not.toHaveBeenCalled();
expect(gmp.groups.getAll).not.toHaveBeenCalled();
});

test('should not fetch groups without a session token', () => {
const gmp = createGmp();
gmp.session.token = undefined;
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
useGetGroups({filter});
useGetAllGroups({filter});
return <div />;
};

render(<TestComponent />);

expect(gmp.groups.get).not.toHaveBeenCalled();
expect(gmp.groups.getAll).not.toHaveBeenCalled();
});
});
106 changes: 106 additions & 0 deletions src/web/hooks/use-query/__tests__/Roles.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* SPDX-FileCopyrightText: 2026 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {describe, expect, test, testing} from '@gsa/testing';
import {rendererWith, screen, waitFor} from 'web/testing';
import CollectionCounts from 'gmp/collection/collection-counts';
import QueryFilter from 'gmp/models/filter/query-filter';
import {createSession} from 'gmp/testing';
import {useGetAllRoles, useGetRoles} from 'web/hooks/use-query/roles';

const filter = QueryFilter.fromString('name~role');
const roles = [{id: 'role-1', name: 'Role 1'}];

const createGmp = () => {
const response = {
data: roles,
meta: {
filter,
counts: new CollectionCounts({all: 1, filtered: 1, length: 1}),
},
};

return {
session: createSession({token: 'test-token'}),
settings: {},
roles: {
get: testing.fn().mockResolvedValue(response),
getAll: testing.fn().mockResolvedValue(response),
},
};
};

describe('role query hooks', () => {
test('should fetch roles with a filter', async () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
const {data} = useGetRoles({filter});
return <div data-testid="role">{data?.entities[0]?.name}</div>;
};

render(<TestComponent />);

await waitFor(() => {
expect(screen.getByTestId('role')).toHaveTextContent('Role 1');
});

expect(gmp.roles.get).toHaveBeenCalledWith({filter});
expect(gmp.roles.getAll).not.toHaveBeenCalled();
});

test('should fetch all roles with a filter', async () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
const {data} = useGetAllRoles({filter});
return <div data-testid="role">{data?.entities[0]?.name}</div>;
};

render(<TestComponent />);

await waitFor(() => {
expect(screen.getByTestId('role')).toHaveTextContent('Role 1');
});

expect(gmp.roles.getAll).toHaveBeenCalledWith({filter});
expect(gmp.roles.get).not.toHaveBeenCalled();
});

test('should not fetch roles when disabled', () => {
const gmp = createGmp();
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
useGetRoles({enabled: false});
useGetAllRoles({enabled: false});
return <div />;
};

render(<TestComponent />);

expect(gmp.roles.get).not.toHaveBeenCalled();
expect(gmp.roles.getAll).not.toHaveBeenCalled();
});

test('should not fetch roles without a session token', () => {
const gmp = createGmp();
gmp.session.token = undefined;
const {render} = rendererWith({gmp, router: true});

const TestComponent = () => {
useGetRoles({filter});
useGetAllRoles({filter});
return <div />;
};

render(<TestComponent />);

expect(gmp.roles.get).not.toHaveBeenCalled();
expect(gmp.roles.getAll).not.toHaveBeenCalled();
});
});
36 changes: 36 additions & 0 deletions src/web/hooks/use-query/groups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2026 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {type FilterType} from 'gmp/models/filter';
import type Group from 'gmp/models/group';
import useGmp from 'web/hooks/useGmp';
import useGetEntities from 'web/queries/useGetEntities';

interface UseGetGroupsParams {
enabled?: boolean;
filter?: FilterType;
}

export const useGetGroups = ({filter, enabled}: UseGetGroupsParams = {}) => {
const gmp = useGmp();
return useGetEntities<Group>({
gmpMethod: gmp.groups.get.bind(gmp.groups),
enabled,
queryId: 'get_groups',
filter,
keepPreviousData: true,
});
};

export const useGetAllGroups = ({filter, enabled}: UseGetGroupsParams = {}) => {
const gmp = useGmp();
return useGetEntities<Group>({
gmpMethod: gmp.groups.getAll.bind(gmp.groups),
enabled,
queryId: 'get_groups',
filter,
keepPreviousData: true,
});
};
36 changes: 36 additions & 0 deletions src/web/hooks/use-query/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2026 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {type FilterType} from 'gmp/models/filter';
import type Role from 'gmp/models/role';
import useGmp from 'web/hooks/useGmp';
import useGetEntities from 'web/queries/useGetEntities';

interface UseGetRolesParams {
enabled?: boolean;
filter?: FilterType;
}

export const useGetRoles = ({filter, enabled}: UseGetRolesParams = {}) => {
const gmp = useGmp();
return useGetEntities<Role>({
gmpMethod: gmp.roles.get.bind(gmp.roles),
enabled,
queryId: 'get_roles',
filter,
keepPreviousData: true,
});
};

export const useGetAllRoles = ({filter, enabled}: UseGetRolesParams = {}) => {
const gmp = useGmp();
return useGetEntities<Role>({
gmpMethod: gmp.roles.getAll.bind(gmp.roles),
enabled,
queryId: 'get_roles',
filter,
keepPreviousData: true,
});
};
19 changes: 3 additions & 16 deletions src/web/pages/users/UserComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import {type ReactNode, useState} from 'react';
import {type EntityActionData} from 'gmp/commands/entity';
import type Model from 'gmp/models/model';
import type Settings from 'gmp/models/settings';
import type User from 'gmp/models/user';
import {isDefined} from 'gmp/utils/identity';
Expand Down Expand Up @@ -67,12 +66,10 @@ const UserComponent = ({
const [accessHosts, setAccessHosts] = useState<string[]>([]);
const [comment, setComment] = useState<string>();
const [groupIds, setGroupIds] = useState<string[]>();
const [groups, setGroups] = useState<Model[]>();
const [hostsAllow, setHostsAllow] = useState<string>();
const [name, setName] = useState<string>();
const [oldName, setOldName] = useState<string>();
const [roleIds, setRoleIds] = useState<string[]>();
const [roles, setRoles] = useState<Model[]>();
const [settings, setSettings] = useState<Settings>();
const [title, setTitle] = useState<string>();
const [user, setUser] = useState<User>();
Expand Down Expand Up @@ -111,17 +108,9 @@ const UserComponent = ({

const openUserDialog = async (user?: User) => {
try {
const [groupsResponse, rolesResponse, authSettingsResponse] =
await Promise.all([
gmp.groups.getAll({
filter: 'permission=modify_group', // list only groups current user may modify
}),
gmp.roles.getAll(),
gmp.user.currentAuthSettings(),
]);

setGroups(groupsResponse.data);
setRoles(rolesResponse.data);
const [authSettingsResponse] = await Promise.all([
gmp.user.currentAuthSettings(),
]);

const settings = authSettingsResponse.data;
setSettings(settings);
Expand Down Expand Up @@ -200,12 +189,10 @@ const UserComponent = ({
accessHosts={accessHosts}
comment={comment}
groupIds={groupIds}
groups={groups}
hostsAllow={hostsAllow}
name={name}
oldName={oldName}
roleIds={roleIds}
roles={roles}
settings={settings}
title={title}
user={user}
Expand Down
Loading
Loading