style: update linter validation and fix lint issues
This commit is contained in:
committed by
Adolfo R. Brandes
parent
30dd1604d1
commit
66fb56e198
@@ -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",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const ROUTES = {
|
||||
LIBRARIES_TEAM_PATH: '/libraries/:libraryId',
|
||||
LIBRARIES_USER_PATH: '/libraries/user/:username'
|
||||
};
|
||||
LIBRARIES_USER_PATH: '/libraries/user/:username',
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ export const getTeamMembers = async (object: string): Promise<TeamMember[]> => {
|
||||
return camelCaseObject(data.results);
|
||||
};
|
||||
|
||||
|
||||
// TODO: this should be replaced in the future with Console API
|
||||
export const getLibrary = async (libraryId: string): Promise<LibraryMetadata> => {
|
||||
const { data } = await getAuthenticatedHttpClient().get(getStudioApiUrl(`/api/libraries/v2/${libraryId}/`));
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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<TeamMember[], Error>(
|
||||
* const { data } = useLibrary('lib:123',);
|
||||
*
|
||||
*/
|
||||
export const useLibrary = (libraryId: string) => {
|
||||
return useSuspenseQuery<LibraryMetadata, Error>({
|
||||
queryKey: authzQueryKeys.library(libraryId),
|
||||
queryFn: () => getLibrary(libraryId),
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
export const useLibrary = (libraryId: string) => useSuspenseQuery<LibraryMetadata, Error>({
|
||||
queryKey: authzQueryKeys.library(libraryId),
|
||||
queryFn: () => getLibrary(libraryId),
|
||||
retry: false,
|
||||
});
|
||||
|
||||
@@ -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<any> }>(resolve =>
|
||||
setTimeout(() => resolve({ default: () => <div data-testid="libraries-manager">Loaded</div> }), 100)
|
||||
)
|
||||
);
|
||||
});
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
jest.mock('./libraries-manager/LibrariesTeamManager', () => lazy(() => new Promise<{ default: ComponentType<any> }>(resolve => setTimeout(() => resolve({ default: () => <div data-testid="libraries-manager">Loaded</div> }), 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', () => {
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<AuthZModule />
|
||||
</MemoryRouter>
|
||||
</QueryClientProvider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('loading-page')).toBeInTheDocument();
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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')}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<DataTable
|
||||
isPaginated
|
||||
@@ -88,7 +69,7 @@ const TeamTable = () => {
|
||||
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'])}
|
||||
</Button>
|
||||
@@ -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,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
<LibraryAuthZProvider>
|
||||
<TestComponent />
|
||||
</LibraryAuthZProvider>
|
||||
</LibraryAuthZProvider>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('username')).toHaveTextContent('testuser');
|
||||
@@ -62,7 +61,7 @@ describe('LibraryAuthZProvider', () => {
|
||||
renderWrapper(
|
||||
<LibraryAuthZProvider>
|
||||
<TestComponent />
|
||||
</LibraryAuthZProvider>
|
||||
</LibraryAuthZProvider>,
|
||||
);
|
||||
}).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(
|
||||
<LibraryAuthZProvider>
|
||||
<TestComponent />
|
||||
</LibraryAuthZProvider>
|
||||
</LibraryAuthZProvider>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('canManageTeam')).toHaveTextContent('false');
|
||||
@@ -91,8 +90,8 @@ describe('LibraryAuthZProvider', () => {
|
||||
renderWrapper(
|
||||
<LibraryAuthZProvider>
|
||||
<TestComponent />
|
||||
</LibraryAuthZProvider>
|
||||
);;
|
||||
</LibraryAuthZProvider>,
|
||||
);
|
||||
}).toThrow('MissingLibrary');
|
||||
});
|
||||
|
||||
|
||||
@@ -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<AuthZProviderProps> = ({ children }) => {
|
||||
export const LibraryAuthZProvider: React.FC<AuthZProviderProps> = ({ children }:AuthZProviderProps) => {
|
||||
const { libraryId } = useParams<{ libraryId: string }>();
|
||||
const { authenticatedUser } = useContext(AppContext) as AppContextType;
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ import LibrariesTeamManager from './LibrariesTeamManager';
|
||||
|
||||
export {
|
||||
LibrariesTeamManager,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Spinner, Container } from '@openedx/paragon';
|
||||
|
||||
const LoadingPage = () => {
|
||||
return (
|
||||
<Container className="d-flex vh-100" data-testid="loading-page">
|
||||
<Spinner
|
||||
variant="primary"
|
||||
animation="border"
|
||||
role="status"
|
||||
className="mb-3 m-auto"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
const LoadingPage = () => (
|
||||
<Container className="d-flex vh-100" data-testid="loading-page">
|
||||
<Spinner
|
||||
variant="primary"
|
||||
animation="border"
|
||||
role="status"
|
||||
className="mb-3 m-auto"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default LoadingPage;
|
||||
|
||||
@@ -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<PermissionValidationResponse[]> => {
|
||||
const { data } = await getAuthenticatedHttpClient().post(getApiUrl(`/api/authz/v1/permissions/validate/me`), validations);
|
||||
export const validateUserPermissions = async (
|
||||
validations: PermissionValidationRequest[],
|
||||
): Promise<PermissionValidationResponse[]> => {
|
||||
const { data } = await getAuthenticatedHttpClient().post(getApiUrl('/api/authz/v1/permissions/validate/me'), validations);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<PermissionValidationResponse[], Error>({
|
||||
queryKey: adminConsoleQueryKeys.permissions(permissions),
|
||||
queryFn: () => validateUserPermissions(permissions),
|
||||
retry: false,
|
||||
});
|
||||
};
|
||||
export const useValidateUserPermissions = (
|
||||
permissions: PermissionValidationRequest[],
|
||||
) => useSuspenseQuery<PermissionValidationResponse[], Error>({
|
||||
queryKey: adminConsoleQueryKeys.permissions(permissions),
|
||||
queryFn: () => validateUserPermissions(permissions),
|
||||
retry: false,
|
||||
});
|
||||
|
||||
@@ -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, () => {
|
||||
<Routes>
|
||||
<Route path="/authz/*" element={<AuthZModule />} />
|
||||
</Routes>
|
||||
</ AppProvider>
|
||||
</AppProvider>
|
||||
</QueryClientProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
14
src/types.ts
14
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 {
|
||||
|
||||
Reference in New Issue
Block a user