feat: create app utilities for the API url resolution and permission validation
This commit is contained in:
committed by
Adolfo R. Brandes
parent
f0298dc9eb
commit
80d04ed000
99
src/helpers/useValidateUserPermissions.test.tsx
Normal file
99
src/helpers/useValidateUserPermissions.test.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { act, ReactNode } from 'react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { useValidateUserPermissions } from './useValidateUserPermissions';
|
||||
|
||||
jest.mock('@edx/frontend-platform/auth', () => ({
|
||||
getAuthenticatedHttpClient: jest.fn(),
|
||||
}));
|
||||
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
const permissions = [
|
||||
{
|
||||
action: 'act:read',
|
||||
object: 'lib:test-lib',
|
||||
scope: 'org:OpenedX',
|
||||
},
|
||||
];
|
||||
|
||||
const mockValidPermissions = [
|
||||
{ action: 'act:read', object: 'lib:test-lib', allowed: true },
|
||||
];
|
||||
|
||||
const mockInvalidPermissions = [
|
||||
{ action: 'act:read', object: 'lib:test-lib', allowed: false },
|
||||
];
|
||||
|
||||
describe('useValidateUserPermissions', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('returns allowed true when permissions are valid', async () => {
|
||||
getAuthenticatedHttpClient.mockReturnValue({
|
||||
post: jest.fn().mockResolvedValueOnce({ data: mockValidPermissions }),
|
||||
});
|
||||
|
||||
|
||||
const { result } = renderHook(() => useValidateUserPermissions(permissions), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current).toBeDefined());
|
||||
|
||||
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
|
||||
expect(result.current.data[0].allowed).toBe(true);
|
||||
});
|
||||
|
||||
it('returns allowed false when permissions are invalid', async () => {
|
||||
getAuthenticatedHttpClient.mockReturnValue({
|
||||
post: jest.fn().mockResolvedValue({ data: mockInvalidPermissions }),
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useValidateUserPermissions(permissions), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current).toBeDefined());
|
||||
|
||||
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
|
||||
expect(result.current.data[0].allowed).toBe(false);
|
||||
});
|
||||
|
||||
it('handles error when the API call fails', async () => {
|
||||
const mockError = new Error('API Error');
|
||||
|
||||
getAuthenticatedHttpClient.mockReturnValue({
|
||||
post: jest.fn().mockRejectedValue(new Error('API Error')),
|
||||
});
|
||||
|
||||
try {
|
||||
act(() => {
|
||||
renderHook(() => useValidateUserPermissions(permissions), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
expect(error).toEqual(mockError); // Check for the expected error
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
44
src/helpers/useValidateUserPermissions.ts
Normal file
44
src/helpers/useValidateUserPermissions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { getApiUrl } from './utils';
|
||||
|
||||
export interface PermissionValidationRequest {
|
||||
action: string;
|
||||
object?: string;
|
||||
scope?: string;
|
||||
}
|
||||
|
||||
export interface PermissionValidationResponse extends PermissionValidationRequest{
|
||||
allowed: boolean;
|
||||
}
|
||||
|
||||
const validateUserPermissions = async (validations: PermissionValidationRequest[]): Promise<PermissionValidationResponse[]> => {
|
||||
const { data } = await getAuthenticatedHttpClient().post(getApiUrl(`/api/authz/v1/permissions/validate/me`), validations);
|
||||
return data;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* React Query hook to validate if the current user has permissions over a certain object in the instance.
|
||||
* It helps to:
|
||||
* - Determine whether the current user can access certain object.
|
||||
* - Provide role-based rendering logic for UI components.
|
||||
*
|
||||
* @param permissions - The array of objects and actions to validate.
|
||||
*
|
||||
* @example
|
||||
* const { data } = useValidateTeamMember([{
|
||||
"action": "act:read",
|
||||
"object": "lib:test-lib",
|
||||
"scope": "org:OpenedX"
|
||||
}]);
|
||||
* if (data[0].allowed) { ... }
|
||||
*
|
||||
*/
|
||||
export const useValidateUserPermissions = (permissions: PermissionValidationRequest[]) => {
|
||||
return useSuspenseQuery<PermissionValidationResponse[], Error>({
|
||||
queryKey: ['validate-user-permissions', permissions],
|
||||
queryFn: () => validateUserPermissions(permissions),
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
4
src/helpers/utils.ts
Normal file
4
src/helpers/utils.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
export const getApiUrl = (path: string) => `${getConfig().LMS_BASE_URL}${path || ''}`;
|
||||
export const getStudioApiUrl = (path: string) => `${getConfig().STUDIO_BASE_URL}${path || ''}`;
|
||||
Reference in New Issue
Block a user