Skip to content

Commit 09a0479

Browse files
committed
feat: add team roles management and update related hooks and types
1 parent 6d8f6fa commit 09a0479

4 files changed

Lines changed: 85 additions & 7 deletions

File tree

src/authz-module/data/api.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
2-
import { LibraryMetadata, TeamMember } from '@src/types';
2+
import { LibraryMetadata, TeamMember, TeamRole } from '@src/types';
33
import { camelCaseObject } from '@edx/frontend-platform';
44
import { getApiUrl, getStudioApiUrl } from '@src/data/utils';
55

@@ -8,12 +8,35 @@ export interface GetTeamMembersResponse {
88
totalCount: number;
99
}
1010

11+
export interface PutTeamMembersResponse {
12+
completed: { user: string; status: string }[];
13+
errors: { user: string; error: string }[];
14+
}
15+
16+
export interface AddTeamMembersRequest {
17+
users: string[];
18+
role: string;
19+
scope: string;
20+
}
21+
1122
// TODO: replece api path once is created
1223
export const getTeamMembers = async (object: string): Promise<TeamMember[]> => {
1324
const { data } = await getAuthenticatedHttpClient().get(getApiUrl(`/api/authz/v1/roles/users?scope=${object}`));
1425
return camelCaseObject(data.results);
1526
};
1627

28+
export const addTeamMembers = async (
29+
data: AddTeamMembersRequest,
30+
): Promise<PutTeamMembersResponse> => {
31+
const res = await getAuthenticatedHttpClient().put(getApiUrl('/api/authz/v1/roles/users'), data);
32+
return camelCaseObject(res.data);
33+
};
34+
35+
export const getTeamRoles = async (libraryId: string): Promise<TeamRole[]> => {
36+
const { data } = await getAuthenticatedHttpClient().get(getApiUrl(`/api/authz/v1/roles/?scope=${libraryId}`));
37+
return data;
38+
};
39+
1740
// TODO: this should be replaced in the future with Console API
1841
export const getLibrary = async (libraryId: string): Promise<LibraryMetadata> => {
1942
const { data } = await getAuthenticatedHttpClient().get(getStudioApiUrl(`/api/libraries/v2/${libraryId}/`));

src/authz-module/data/hooks.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
1+
import {
2+
useMutation, useQuery, useQueryClient, useSuspenseQuery,
3+
} from '@tanstack/react-query';
24
import { appId } from '@src/constants';
3-
import { LibraryMetadata, TeamMember } from '@src/types';
4-
import { getLibrary, getTeamMembers } from './api';
5+
import { LibraryMetadata, TeamMember, TeamRole } from '@src/types';
6+
import {
7+
addTeamMembers, AddTeamMembersRequest, getLibrary, getTeamMembers, getTeamRoles,
8+
} from './api';
59

610
const authzQueryKeys = {
711
all: [appId, 'authz'] as const,
812
teamMembers: (object: string) => [...authzQueryKeys.all, 'teamMembers', object] as const,
13+
teamRoles: (libraryId: string) => [...authzQueryKeys.all, 'teamRoles', libraryId] as const,
914
library: (libraryId: string) => [...authzQueryKeys.all, 'library', libraryId] as const,
1015
};
1116

@@ -40,3 +45,42 @@ export const useLibrary = (libraryId: string) => useSuspenseQuery<LibraryMetadat
4045
queryFn: () => getLibrary(libraryId),
4146
retry: false,
4247
});
48+
49+
/**
50+
* React Query hook to add new team members to a specific library.
51+
* It provides a mutation function to add users with specified roles to the library's team.
52+
*
53+
* @example
54+
* ```tsx
55+
* const { mutate: addTeamMember, isPending } = useAddTeamMember();
56+
* addTeamMember({ data: { libraryId: 'lib:123', users: ['jdoe'], role: 'editor' } });
57+
* ```
58+
*/
59+
export const useAddTeamMember = () => {
60+
const queryClient = useQueryClient();
61+
return useMutation({
62+
mutationFn: async ({ data }: {
63+
data: AddTeamMembersRequest
64+
}) => addTeamMembers(data),
65+
onSettled: (_data, _error, { data: { scope } }) => {
66+
queryClient.invalidateQueries({ queryKey: authzQueryKeys.teamMembers(scope) });
67+
},
68+
});
69+
};
70+
71+
/**
72+
* React Query hook to fetch all roles available for a specific library.
73+
* It retrieves the list of roles that can be assigned to team members within the given library.
74+
*
75+
* @param libraryId - The unique identifier of the library
76+
*
77+
* @example
78+
* ```tsx
79+
* const { data: teamRoles, isLoading, isError } = useTeamRoles('lib:123');
80+
* ```
81+
*/
82+
export const useTeamRoles = (libraryId: string) => useSuspenseQuery<TeamRole[], Error>({
83+
queryKey: authzQueryKeys.teamRoles(libraryId),
84+
queryFn: () => getTeamRoles(libraryId),
85+
staleTime: 1000 * 60 * 60 * 24, // refetch after 24 hours
86+
});

src/authz-module/libraries-manager/context.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
import { useParams } from 'react-router-dom';
55
import { AppContext } from '@edx/frontend-platform/react';
66
import { useValidateUserPermissions } from '@src/data/hooks';
7+
import { TeamRole } from 'types';
8+
import { useTeamRoles } from '../data/hooks';
79

810
const LIBRARY_TEAM_PERMISSIONS = ['act:view_library_team', 'act:manage_library_team'];
911

@@ -18,7 +20,7 @@ type LibraryAuthZContextType = {
1820
canManageTeam: boolean;
1921
username: string;
2022
libraryId: string;
21-
roles: string[];
23+
roles: TeamRole[];
2224
permissions: string[];
2325
};
2426

@@ -45,13 +47,15 @@ export const LibraryAuthZProvider: React.FC<AuthZProviderProps> = ({ children }:
4547
throw new Error('NoAccess');
4648
}
4749

50+
const { data: teamRoles } = useTeamRoles(libraryId);
51+
4852
const value = useMemo((): LibraryAuthZContextType => ({
4953
username: authenticatedUser.username,
5054
libraryId,
51-
roles: [],
55+
roles: teamRoles,
5256
permissions: [],
5357
canManageTeam,
54-
}), [libraryId, authenticatedUser.username, canManageTeam]);
58+
}), [libraryId, authenticatedUser.username, canManageTeam, teamRoles]);
5559

5660
return (
5761
<LibraryAuthZContext.Provider value={value}>

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ export interface LibraryMetadata {
2222
slug: string;
2323
}
2424

25+
export interface TeamRole {
26+
role: string;
27+
description: string;
28+
userCount: number;
29+
objects: { object: string; description: string; actions: string[] }[];
30+
}
31+
2532
// Paragon table type
2633
export interface TableCellValue<T> {
2734
row: {

0 commit comments

Comments
 (0)