Implement special data loading for admins

This commit is contained in:
IRBorisov 2024-04-28 21:07:04 +03:00
parent dcaf930570
commit dc1024dce1
7 changed files with 88 additions and 28 deletions

View File

@ -29,7 +29,7 @@ class EndpointTester(APITestCase):
self.client = APIClient() self.client = APIClient()
self.client.force_authenticate(user=self.user) self.client.force_authenticate(user=self.user)
def toggle_staff(self, value: bool = True): def toggle_admin(self, value: bool = True):
self.user.is_staff = value self.user.is_staff = value
self.user.save() self.user.save()

View File

@ -66,7 +66,7 @@ class TestLibraryViewset(EndpointTester):
self.assertTrue(response.status_code in [status.HTTP_202_ACCEPTED, status.HTTP_204_NO_CONTENT]) self.assertTrue(response.status_code in [status.HTTP_202_ACCEPTED, status.HTTP_204_NO_CONTENT])
self.assertForbidden(item=self.unowned.id) self.assertForbidden(item=self.unowned.id)
self.toggle_staff(True) self.toggle_admin(True)
response = self.execute(item=self.unowned.id) response = self.execute(item=self.unowned.id)
self.assertTrue(response.status_code in [status.HTTP_202_ACCEPTED, status.HTTP_204_NO_CONTENT]) self.assertTrue(response.status_code in [status.HTTP_202_ACCEPTED, status.HTTP_204_NO_CONTENT])
@ -111,6 +111,21 @@ class TestLibraryViewset(EndpointTester):
self.assertFalse(response_contains(response, self.owned)) self.assertFalse(response_contains(response, self.owned))
@decl_endpoint('/api/library/all', method='get')
def test_retrieve_all(self):
self.toggle_admin(False)
self.assertForbidden()
self.toggle_admin(True)
response = self.execute()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response_contains(response, self.common))
self.assertTrue(response_contains(response, self.unowned))
self.assertTrue(response_contains(response, self.owned))
self.logout()
self.assertForbidden()
@decl_endpoint('/api/library/active', method='get') @decl_endpoint('/api/library/active', method='get')
def test_retrieve_subscribed(self): def test_retrieve_subscribed(self):
response = self.execute() response = self.execute()

View File

@ -8,7 +8,8 @@ library_router.register('library', views.LibraryViewSet, 'Library')
library_router.register('rsforms', views.RSFormViewSet, 'RSForm') library_router.register('rsforms', views.RSFormViewSet, 'RSForm')
urlpatterns = [ urlpatterns = [
path('library/active', views.LibraryActiveView.as_view(), name='library'), path('library/active', views.LibraryActiveView.as_view()),
path('library/all', views.LibraryAdminView.as_view()),
path('library/templates', views.LibraryTemplatesView.as_view(), name='templates'), path('library/templates', views.LibraryTemplatesView.as_view(), name='templates'),
path('constituents/<int:pk>', views.ConstituentAPIView.as_view(), name='constituenta-detail'), path('constituents/<int:pk>', views.ConstituentAPIView.as_view(), name='constituenta-detail'),
path('rsforms/import-trs', views.TrsImportView.as_view()), path('rsforms/import-trs', views.TrsImportView.as_view()),

View File

@ -1,6 +1,7 @@
''' REST API: Endpoint processors. ''' ''' REST API: Endpoint processors. '''
from .library import ( from .library import (
LibraryActiveView, LibraryActiveView,
LibraryAdminView,
LibraryTemplatesView, LibraryTemplatesView,
LibraryViewSet LibraryViewSet
) )

View File

@ -34,6 +34,17 @@ class LibraryActiveView(generics.ListAPIView):
).distinct().order_by('-time_update') ).distinct().order_by('-time_update')
@extend_schema(tags=['Library'])
@extend_schema_view()
class LibraryAdminView(generics.ListAPIView):
''' Endpoint: Get list of all library items. Admin only '''
permission_classes = (permissions.IsAdminUser,)
serializer_class = s.LibraryItemSerializer
def get_queryset(self):
return m.LibraryItem.objects.all().order_by('-time_update')
@extend_schema(tags=['Library']) @extend_schema(tags=['Library'])
@extend_schema_view() @extend_schema_view()
class LibraryTemplatesView(generics.ListAPIView): class LibraryTemplatesView(generics.ListAPIView):

View File

@ -192,6 +192,14 @@ export function getLibrary(request: FrontPull<ILibraryItem[]>) {
}); });
} }
export function getAdminLibrary(request: FrontPull<ILibraryItem[]>) {
AxiosGet({
title: 'All LibraryItems list',
endpoint: '/api/library/all',
request: request
});
}
export function getTemplates(request: FrontPull<ILibraryItem[]>) { export function getTemplates(request: FrontPull<ILibraryItem[]>) {
AxiosGet({ AxiosGet({
title: 'Available LibraryItems list', title: 'Available LibraryItems list',

View File

@ -2,23 +2,25 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { ErrorData } from '@/components/info/InfoError';
import { ILibraryItem } from '@/models/library';
import { matchLibraryItem } from '@/models/libraryAPI';
import { ILibraryFilter } from '@/models/miscellaneous';
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
import { RSFormLoader } from '@/models/RSFormLoader';
import { import {
DataCallback, DataCallback,
deleteLibraryItem, deleteLibraryItem,
getAdminLibrary,
getLibrary, getLibrary,
getRSFormDetails, getRSFormDetails,
getTemplates, getTemplates,
postCloneLibraryItem, postCloneLibraryItem,
postNewRSForm postNewRSForm
} from '@/app/backendAPI'; } from '@/app/backendAPI';
import { ErrorData } from '@/components/info/InfoError';
import { ILibraryItem } from '@/models/library';
import { matchLibraryItem } from '@/models/libraryAPI';
import { ILibraryFilter } from '@/models/miscellaneous';
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
import { RSFormLoader } from '@/models/RSFormLoader';
import { useAuth } from './AuthContext'; import { useAuth } from './AuthContext';
import { useConceptOptions } from './OptionsContext';
interface ILibraryContext { interface ILibraryContext {
items: ILibraryItem[]; items: ILibraryItem[];
@ -53,6 +55,7 @@ interface LibraryStateProps {
export const LibraryState = ({ children }: LibraryStateProps) => { export const LibraryState = ({ children }: LibraryStateProps) => {
const { user } = useAuth(); const { user } = useAuth();
const { adminMode } = useConceptOptions();
const [items, setItems] = useState<ILibraryItem[]>([]); const [items, setItems] = useState<ILibraryItem[]>([]);
const [templates, setTemplates] = useState<ILibraryItem[]>([]); const [templates, setTemplates] = useState<ILibraryItem[]>([]);
@ -109,19 +112,36 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
[cachedTemplates] [cachedTemplates]
); );
const reload = useCallback((callback?: () => void) => { const reloadItems = useCallback(
setItems([]); (callback?: () => void) => {
setError(undefined); setItems([]);
getLibrary({ setError(undefined);
setLoading: setLoading, if (user?.is_staff && adminMode) {
showError: true, getAdminLibrary({
onError: setError, setLoading: setLoading,
onSuccess: newData => { showError: true,
setItems(newData); onError: setError,
if (callback) callback(); onSuccess: newData => {
setItems(newData);
if (callback) callback();
}
});
} else {
getLibrary({
setLoading: setLoading,
showError: true,
onError: setError,
onSuccess: newData => {
setItems(newData);
if (callback) callback();
}
});
} }
}); },
[user, adminMode]
);
const reloadTemplates = useCallback(() => {
setTemplates([]); setTemplates([]);
getTemplates({ getTemplates({
showError: true, showError: true,
@ -130,8 +150,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}, []); }, []);
useEffect(() => { useEffect(() => {
reload(); reloadItems();
}, [reload, user]); }, [reloadItems]);
useEffect(() => {
reloadTemplates();
}, [reloadTemplates]);
const localUpdateItem = useCallback( const localUpdateItem = useCallback(
(data: ILibraryItem) => { (data: ILibraryItem) => {
@ -160,7 +184,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
setLoading: setProcessing, setLoading: setProcessing,
onError: setError, onError: setError,
onSuccess: newSchema => onSuccess: newSchema =>
reload(() => { reloadItems(() => {
if (user && !user.subscriptions.includes(newSchema.id)) { if (user && !user.subscriptions.includes(newSchema.id)) {
user.subscriptions.push(newSchema.id); user.subscriptions.push(newSchema.id);
} }
@ -168,7 +192,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}) })
}); });
}, },
[reload, user] [reloadItems, user]
); );
const destroyItem = useCallback( const destroyItem = useCallback(
@ -179,7 +203,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
setLoading: setProcessing, setLoading: setProcessing,
onError: setError, onError: setError,
onSuccess: () => onSuccess: () =>
reload(() => { reloadItems(() => {
if (user && user.subscriptions.includes(target)) { if (user && user.subscriptions.includes(target)) {
user.subscriptions.splice( user.subscriptions.splice(
user.subscriptions.findIndex(item => item === target), user.subscriptions.findIndex(item => item === target),
@ -190,7 +214,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}) })
}); });
}, },
[setError, reload, user] [setError, reloadItems, user]
); );
const cloneItem = useCallback( const cloneItem = useCallback(
@ -205,7 +229,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
setLoading: setProcessing, setLoading: setProcessing,
onError: setError, onError: setError,
onSuccess: newSchema => onSuccess: newSchema =>
reload(() => { reloadItems(() => {
if (user && !user.subscriptions.includes(newSchema.id)) { if (user && !user.subscriptions.includes(newSchema.id)) {
user.subscriptions.push(newSchema.id); user.subscriptions.push(newSchema.id);
} }
@ -213,7 +237,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}) })
}); });
}, },
[reload, setError, user] [reloadItems, setError, user]
); );
return ( return (