diff --git a/package.json b/package.json index 5223eb4..e0e23aa 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "scripts": { "build": "fedx-scripts webpack", "i18n_extract": "fedx-scripts formatjs extract", - "lint": "fedx-scripts eslint --ext .js --ext .jsx .", - "lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .", + "lint": "fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .", + "lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .", "snapshot": "fedx-scripts jest --updateSnapshot", "start": "fedx-scripts webpack-dev-server --progress", "start:with-theme": "paragon install-theme && npm start && npm install", diff --git a/src/authz-module/constants.ts b/src/authz-module/constants.ts index 18bad6b..7864147 100644 --- a/src/authz-module/constants.ts +++ b/src/authz-module/constants.ts @@ -1,4 +1,4 @@ export const ROUTES = { LIBRARIES_TEAM_PATH: '/libraries/:libraryId', - LIBRARIES_USER_PATH: '/libraries/user/:username' -}; + LIBRARIES_USER_PATH: '/libraries/user/:username', +}; diff --git a/src/authz-module/data/api.ts b/src/authz-module/data/api.ts index 7e4498c..7529c60 100644 --- a/src/authz-module/data/api.ts +++ b/src/authz-module/data/api.ts @@ -14,7 +14,6 @@ export const getTeamMembers = async (object: string): Promise => { return camelCaseObject(data.results); }; - // TODO: this should be replaced in the future with Console API export const getLibrary = async (libraryId: string): Promise => { const { data } = await getAuthenticatedHttpClient().get(getStudioApiUrl(`/api/libraries/v2/${libraryId}/`)); diff --git a/src/authz-module/data/hooks.test.tsx b/src/authz-module/data/hooks.test.tsx index 940168d..28aced0 100644 --- a/src/authz-module/data/hooks.test.tsx +++ b/src/authz-module/data/hooks.test.tsx @@ -113,9 +113,9 @@ describe('useLibrary', () => { const wrapper = createWrapper(); try { - act(()=>{ + act(() => { renderHook(() => useLibrary('lib123'), { wrapper }); - }) + }); } catch (e) { expect(e).toEqual(new Error('Not found')); } diff --git a/src/authz-module/data/hooks.ts b/src/authz-module/data/hooks.ts index a9f82e5..e374c09 100644 --- a/src/authz-module/data/hooks.ts +++ b/src/authz-module/data/hooks.ts @@ -3,7 +3,6 @@ import { appId } from '@src/constants'; import { LibraryMetadata, TeamMember } from '@src/types'; import { getLibrary, getTeamMembers } from './api'; - const authzQueryKeys = { all: [appId, 'authz'] as const, teamMembers: (object: string) => [...authzQueryKeys.all, 'teamMembers', object] as const, @@ -36,10 +35,8 @@ export const useTeamMembers = (object: string) => useQuery( * const { data } = useLibrary('lib:123',); * */ -export const useLibrary = (libraryId: string) => { - return useSuspenseQuery({ - queryKey: authzQueryKeys.library(libraryId), - queryFn: () => getLibrary(libraryId), - retry: false, - }); -} +export const useLibrary = (libraryId: string) => useSuspenseQuery({ + queryKey: authzQueryKeys.library(libraryId), + queryFn: () => getLibrary(libraryId), + retry: false, +}); diff --git a/src/authz-module/index.test.tsx b/src/authz-module/index.test.tsx index e476608..5b5ab02 100644 --- a/src/authz-module/index.test.tsx +++ b/src/authz-module/index.test.tsx @@ -4,22 +4,16 @@ import { MemoryRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import AuthZModule from './index'; -jest.mock('./libraries-manager/LibrariesTeamManager', () => { - return lazy(() => - new Promise<{ default: ComponentType }>(resolve => - setTimeout(() => resolve({ default: () =>
Loaded
}), 100) - ) - ); -}); +// eslint-disable-next-line no-promise-executor-return +jest.mock('./libraries-manager/LibrariesTeamManager', () => lazy(() => new Promise<{ default: ComponentType }>(resolve => setTimeout(() => resolve({ default: () =>
Loaded
}), 100)))); -const createTestQueryClient = () => - new QueryClient({ - defaultOptions: { - queries: { - retry: false, - }, +const createTestQueryClient = () => new QueryClient({ + defaultOptions: { + queries: { + retry: false, }, - }); + }, +}); describe('AuthZModule', () => { it('renders LoadingPage then LibrariesTeamManager when route matches', async () => { @@ -31,7 +25,7 @@ describe('AuthZModule', () => { - + , ); expect(screen.getByTestId('loading-page')).toBeInTheDocument(); diff --git a/src/authz-module/index.tsx b/src/authz-module/index.tsx index e95a136..2812df3 100644 --- a/src/authz-module/index.tsx +++ b/src/authz-module/index.tsx @@ -2,7 +2,7 @@ import { Suspense } from 'react'; import { Routes, Route } from 'react-router-dom'; import { ErrorBoundary } from '@edx/frontend-platform/react'; import LoadingPage from '@src/components/LoadingPage'; -import { LibrariesTeamManager } from './libraries-manager/'; +import { LibrariesTeamManager } from './libraries-manager'; import { ROUTES } from './constants'; import './index.scss'; diff --git a/src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx b/src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx index 95b1be9..ad3a8d9 100644 --- a/src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx +++ b/src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx @@ -1,9 +1,9 @@ import { screen } from '@testing-library/react'; -import LibrariesTeamManager from './LibrariesTeamManager'; -import { useLibraryAuthZ } from './context'; import { renderWrapper } from '@src/setupTest'; import { initializeMockApp } from '@edx/frontend-platform/testing'; import { useLibrary } from '@src/authz-module/data/hooks'; +import { useLibraryAuthZ } from './context'; +import LibrariesTeamManager from './LibrariesTeamManager'; jest.mock('./context', () => { const actual = jest.requireActual('./context'); @@ -28,9 +28,9 @@ describe('LibrariesTeamManager', () => { beforeEach(() => { initializeMockApp({ authenticatedUser: { - username: 'admin' - } - }) + username: 'admin', + }, + }); mockedUseLibraryAuthZ.mockReturnValue({ libraryId: 'lib-001', libraryName: 'Mock Library', diff --git a/src/authz-module/libraries-manager/LibrariesTeamManager.tsx b/src/authz-module/libraries-manager/LibrariesTeamManager.tsx index 28db7a3..ae81a3e 100644 --- a/src/authz-module/libraries-manager/LibrariesTeamManager.tsx +++ b/src/authz-module/libraries-manager/LibrariesTeamManager.tsx @@ -1,8 +1,8 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { Tab, Tabs } from '@openedx/paragon'; +import { useLibrary } from '@src/authz-module/data/hooks'; import TeamTable from './components/TeamTable'; import AuthZLayout from '../components/AuthZLayout'; -import { useLibrary } from '@src/authz-module/data/hooks'; import { LibraryAuthZProvider, useLibraryAuthZ } from './context'; import messages from './messages'; @@ -10,7 +10,7 @@ import messages from './messages'; const LibrariesAuthZTeamView = () => { const intl = useIntl(); const { libraryId } = useLibraryAuthZ(); - const { data: library } = useLibrary(libraryId) + const { data: library } = useLibrary(libraryId); const rootBradecrumb = intl.formatMessage(messages['library.authz.breadcrumb.root']) || ''; const pageTitle = intl.formatMessage(messages['library.authz.manage.page.title']); return ( diff --git a/src/authz-module/libraries-manager/components/TeamTable.test.tsx b/src/authz-module/libraries-manager/components/TeamTable.test.tsx index 434c048..32ba801 100644 --- a/src/authz-module/libraries-manager/components/TeamTable.test.tsx +++ b/src/authz-module/libraries-manager/components/TeamTable.test.tsx @@ -2,13 +2,13 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ROUTES } from '@src/authz-module/constants'; import { renderWrapper } from '@src/setupTest'; -import TeamTable from './TeamTable'; import { useTeamMembers } from '@src/authz-module/data/hooks'; +import TeamTable from './TeamTable'; import { useLibraryAuthZ } from '../context'; const mockNavigate = jest.fn(); -jest.mock('react-router', () => ({ - ...jest.requireActual('react-router'), +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), useNavigate: () => mockNavigate, })); @@ -93,7 +93,7 @@ describe('TeamTable', () => { await userEvent.click(editButtons[0]); expect(mockNavigate).toHaveBeenCalledWith( - `/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', 'alice')}`, + `/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', 'bob')}`, ); }); diff --git a/src/authz-module/libraries-manager/components/TeamTable.tsx b/src/authz-module/libraries-manager/components/TeamTable.tsx index a751123..e008855 100644 --- a/src/authz-module/libraries-manager/components/TeamTable.tsx +++ b/src/authz-module/libraries-manager/components/TeamTable.tsx @@ -1,5 +1,4 @@ -import { useMemo } from 'react'; -import { useNavigate } from 'react-router'; +import { useNavigate } from 'react-router-dom'; import { useIntl } from '@edx/frontend-platform/i18n'; import { DataTable, Button, Chip, Skeleton, @@ -46,31 +45,13 @@ const TeamTable = () => { // TODO: Display error in the notification system const { - data: teamMembers, isLoading, isError + data: teamMembers, isLoading, isError, } = useTeamMembers(libraryId); const rows = isError ? [] : (teamMembers || SKELETON_ROWS); const navigate = useNavigate(); - const columns = useMemo(() => [ - { - Header: intl.formatMessage(messages['library.authz.team.table.display.name']), - accessor: 'displayName', - Cell: NameCell, - }, - { - Header: intl.formatMessage(messages['library.authz.team.table.email']), - accessor: 'email', - Cell: EmailCell, - }, - { - Header: intl.formatMessage(messages['library.authz.team.table.roles']), - accessor: 'roles', - Cell: RolesCell, - }, - ], [isLoading]); - return ( { variant="link" size="sm" // TODO: update the view with the team member view - onClick={() => navigate(`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', username)}`)} + onClick={() => navigate(`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', row.original.username)}`)} > {intl.formatMessage(messages['authz.libraries.team.table.edit.action'])} @@ -98,7 +79,25 @@ const TeamTable = () => { initialState={{ pageSize: 10, }} - columns={columns} + columns={ + [ + { + Header: intl.formatMessage(messages['library.authz.team.table.display.name']), + accessor: 'displayName', + Cell: NameCell, + }, + { + Header: intl.formatMessage(messages['library.authz.team.table.email']), + accessor: 'email', + Cell: EmailCell, + }, + { + Header: intl.formatMessage(messages['library.authz.team.table.roles']), + accessor: 'roles', + Cell: RolesCell, + }, + ] + } /> ); }; diff --git a/src/authz-module/libraries-manager/context.test.tsx b/src/authz-module/libraries-manager/context.test.tsx index 8cf3521..f133f41 100644 --- a/src/authz-module/libraries-manager/context.test.tsx +++ b/src/authz-module/libraries-manager/context.test.tsx @@ -25,7 +25,6 @@ const TestComponent = () => { }; describe('LibraryAuthZProvider', () => { - beforeEach(() => { jest.clearAllMocks(); (useParams as jest.Mock).mockReturnValue({ libraryId: 'lib123' }); @@ -42,7 +41,7 @@ describe('LibraryAuthZProvider', () => { renderWrapper( - + , ); expect(screen.getByTestId('username')).toHaveTextContent('testuser'); @@ -62,7 +61,7 @@ describe('LibraryAuthZProvider', () => { renderWrapper( - + , ); }).toThrow('NoAccess'); }); @@ -70,7 +69,7 @@ describe('LibraryAuthZProvider', () => { it('provides context when user can view but not manage team', () => { (useValidateUserPermissions as jest.Mock).mockReturnValue({ data: [ - { allowed: true }, // canViewTeam + { allowed: true }, // canViewTeam { allowed: false }, // canManageTeam ], }); @@ -78,7 +77,7 @@ describe('LibraryAuthZProvider', () => { renderWrapper( - + , ); expect(screen.getByTestId('canManageTeam')).toHaveTextContent('false'); @@ -91,8 +90,8 @@ describe('LibraryAuthZProvider', () => { renderWrapper( - - );; + , + ); }).toThrow('MissingLibrary'); }); diff --git a/src/authz-module/libraries-manager/context.tsx b/src/authz-module/libraries-manager/context.tsx index 149d3a5..b013e6b 100644 --- a/src/authz-module/libraries-manager/context.tsx +++ b/src/authz-module/libraries-manager/context.tsx @@ -4,7 +4,6 @@ import { import { useParams } from 'react-router-dom'; import { AppContext } from '@edx/frontend-platform/react'; import { useValidateUserPermissions } from '@src/data/hooks'; -import { useLibrary } from '../data/hooks'; const LIBRARY_TEAM_PERMISSIONS = ['act:view_library_team', 'act:manage_library_team']; @@ -29,7 +28,7 @@ type AuthZProviderProps = { children: ReactNode; }; -export const LibraryAuthZProvider: React.FC = ({ children }) => { +export const LibraryAuthZProvider: React.FC = ({ children }:AuthZProviderProps) => { const { libraryId } = useParams<{ libraryId: string }>(); const { authenticatedUser } = useContext(AppContext) as AppContextType; diff --git a/src/authz-module/libraries-manager/index.ts b/src/authz-module/libraries-manager/index.ts index a085b74..b12092e 100644 --- a/src/authz-module/libraries-manager/index.ts +++ b/src/authz-module/libraries-manager/index.ts @@ -2,4 +2,4 @@ import LibrariesTeamManager from './LibrariesTeamManager'; export { LibrariesTeamManager, -} +}; diff --git a/src/components/LoadingPage.tsx b/src/components/LoadingPage.tsx index bdcc6ea..047546f 100644 --- a/src/components/LoadingPage.tsx +++ b/src/components/LoadingPage.tsx @@ -1,16 +1,14 @@ import { Spinner, Container } from '@openedx/paragon'; -const LoadingPage = () => { - return ( - - - - ); -}; +const LoadingPage = () => ( + + + +); export default LoadingPage; diff --git a/src/data/api.ts b/src/data/api.ts index cd2349f..b3d3953 100644 --- a/src/data/api.ts +++ b/src/data/api.ts @@ -2,7 +2,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { PermissionValidationRequest, PermissionValidationResponse } from '@src/types'; import { getApiUrl } from './utils'; -export const validateUserPermissions = async (validations: PermissionValidationRequest[]): Promise => { - const { data } = await getAuthenticatedHttpClient().post(getApiUrl(`/api/authz/v1/permissions/validate/me`), validations); +export const validateUserPermissions = async ( + validations: PermissionValidationRequest[], +): Promise => { + const { data } = await getAuthenticatedHttpClient().post(getApiUrl('/api/authz/v1/permissions/validate/me'), validations); return data; }; diff --git a/src/data/hooks.test.tsx b/src/data/hooks.test.tsx index 799fc96..7dc20ec 100644 --- a/src/data/hooks.test.tsx +++ b/src/data/hooks.test.tsx @@ -90,10 +90,8 @@ describe('useValidateUserPermissions', () => { wrapper: createWrapper(), }); }); - } catch (error) { - expect(error).toEqual(mockError); // Check for the expected error - return; + expect(error).toEqual(mockError); // Check for the expected error } }); }); diff --git a/src/data/hooks.ts b/src/data/hooks.ts index 1d587f7..80d4154 100644 --- a/src/data/hooks.ts +++ b/src/data/hooks.ts @@ -1,7 +1,7 @@ import { useSuspenseQuery } from '@tanstack/react-query'; import { PermissionValidationRequest, PermissionValidationResponse } from '@src/types'; -import { validateUserPermissions } from './api'; import { appId } from '@src/constants'; +import { validateUserPermissions } from './api'; const adminConsoleQueryKeys = { all: [appId] as const, @@ -25,10 +25,10 @@ const adminConsoleQueryKeys = { * if (data[0].allowed) { ... } * */ -export const useValidateUserPermissions = (permissions: PermissionValidationRequest[]) => { - return useSuspenseQuery({ - queryKey: adminConsoleQueryKeys.permissions(permissions), - queryFn: () => validateUserPermissions(permissions), - retry: false, - }); -}; +export const useValidateUserPermissions = ( + permissions: PermissionValidationRequest[], +) => useSuspenseQuery({ + queryKey: adminConsoleQueryKeys.permissions(permissions), + queryFn: () => validateUserPermissions(permissions), + retry: false, +}); diff --git a/src/index.tsx b/src/index.tsx index 6170656..0408594 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,11 +6,11 @@ import { AppProvider, ErrorPage } from '@edx/frontend-platform/react'; import { APP_INIT_ERROR, APP_READY, subscribe, initialize, } from '@edx/frontend-platform'; +import AuthZModule from 'authz-module'; import messages from './i18n'; import './index.scss'; -import AuthZModule from 'authz-module'; const queryClient = new QueryClient(); @@ -24,7 +24,7 @@ subscribe(APP_READY, () => { } /> - + , ); diff --git a/src/types.ts b/src/types.ts index f913d14..0d4acfe 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,12 +1,12 @@ export interface PermissionValidationRequest { - action: string; - object?: string; - scope?: string; -}; + action: string; + object?: string; + scope?: string; +} -export interface PermissionValidationResponse extends PermissionValidationRequest{ - allowed: boolean; -}; +export interface PermissionValidationResponse extends PermissionValidationRequest { + allowed: boolean; +} // Libraries AuthZ types export interface TeamMember {