Add API for django messages

This commit is contained in:
Moritz Pflanzer 2023-12-30 11:54:19 +01:00
parent 2e597a7176
commit 36db77cf89
7 changed files with 174 additions and 1 deletions

View File

@ -21,6 +21,7 @@ import { IfPermissionsDirective } from 'src/app/directives/if-permissions.direct
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { of, throwError } from 'rxjs'
import { ToastService } from 'src/app/services/toast.service'
import { MessagesService } from 'src/app/services/messages.service'
import { environment } from 'src/environments/environment'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { ActivatedRoute, Router } from '@angular/router'
@ -83,6 +84,7 @@ describe('AppFrameComponent', () => {
let permissionsService: PermissionsService
let remoteVersionService: RemoteVersionService
let toastService: ToastService
let messagesService: MessagesService
let openDocumentsService: OpenDocumentsService
let searchService: SearchService
let documentListViewService: DocumentListViewService
@ -123,6 +125,7 @@ describe('AppFrameComponent', () => {
RemoteVersionService,
IfPermissionsDirective,
ToastService,
MessagesService,
OpenDocumentsService,
SearchService,
NgbModal,
@ -151,6 +154,7 @@ describe('AppFrameComponent', () => {
permissionsService = TestBed.inject(PermissionsService)
remoteVersionService = TestBed.inject(RemoteVersionService)
toastService = TestBed.inject(ToastService)
messagesService = TestBed.inject(MessagesService)
openDocumentsService = TestBed.inject(OpenDocumentsService)
searchService = TestBed.inject(SearchService)
documentListViewService = TestBed.inject(DocumentListViewService)
@ -393,4 +397,19 @@ describe('AppFrameComponent', () => {
backdrop: 'static',
})
})
it('should show toasts for django messages', () => {
const toastErrorSpy = jest.spyOn(toastService, 'showError')
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
jest.spyOn(messagesService, 'get').mockReturnValue(
of([
{ level: 'error', message: 'Test error', tags: '' },
{ level: 'info', message: 'Test info', tags: '' },
])
)
component.ngOnInit()
httpTestingController.expectOne(`${environment.apiBaseUrl}messages/`)
expect(toastErrorSpy).toHaveBeenCalled()
expect(toastInfoSpy).toHaveBeenCalled()
})
})

View File

@ -12,6 +12,7 @@ import {
} from 'rxjs/operators'
import { Document } from 'src/app/data/document'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { MessagesService } from 'src/app/services/messages.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SearchService } from 'src/app/services/rest/search.service'
import { environment } from 'src/environments/environment'
@ -73,7 +74,8 @@ export class AppFrameComponent
public tasksService: TasksService,
private readonly toastService: ToastService,
private modalService: NgbModal,
permissionsService: PermissionsService
permissionsService: PermissionsService,
private messagesService: MessagesService
) {
super()
@ -92,6 +94,25 @@ export class AppFrameComponent
this.checkForUpdates()
}
this.tasksService.reload()
this.messagesService
.get()
.pipe(first())
.subscribe((msgs) => {
for (const m of msgs) {
switch (m.level) {
case 'error':
case 'warning':
this.toastService.showError(m.message)
break
case 'success':
case 'info':
case 'debug':
this.toastService.showInfo(m.message)
break
}
}
})
}
toggleSlimSidebar(): void {

View File

@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing'
import { MessagesService } from './messages.service'
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { environment } from 'src/environments/environment'
describe('MessagesService', () => {
let httpTestingController: HttpTestingController
let service: MessagesService
beforeEach(() => {
TestBed.configureTestingModule({
providers: [MessagesService],
imports: [HttpClientTestingModule],
})
httpTestingController = TestBed.inject(HttpTestingController)
service = TestBed.inject(MessagesService)
})
afterEach(() => {
httpTestingController.verify()
})
it('calls get profile endpoint', () => {
service.get().subscribe()
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}messages/`
)
expect(req.request.method).toEqual('GET')
})
})

View File

@ -0,0 +1,25 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { environment } from 'src/environments/environment'
export interface DjangoMessage {
level: string
message: string
tags: string
}
@Injectable({
providedIn: 'root',
})
export class MessagesService {
private endpoint = 'messages'
constructor(private http: HttpClient) {}
get(): Observable<DjangoMessage[]> {
return this.http.get<DjangoMessage[]>(
`${environment.apiBaseUrl}${this.endpoint}/`
)
}
}

View File

@ -0,0 +1,55 @@
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.messages.storage.fallback import FallbackStorage
from django.test import RequestFactory
from rest_framework import status
from rest_framework.test import APITestCase
from documents.tests.utils import DirectoriesMixin
from paperless.views import MessagesView
class TestApiMessages(DirectoriesMixin, APITestCase):
ENDPOINT = "/api/messages/"
def setUp(self):
super().setUp()
self.user = User.objects.create_superuser(
username="temp_admin",
first_name="firstname",
last_name="surname",
)
self.client.force_authenticate(user=self.user)
def test_get_django_messages(self):
"""
GIVEN:
- Configured user
- Pending django message
WHEN:
- API call is made to get the django messages
THEN:
- Pending message is returned
- No more messages are pending
"""
factory = RequestFactory()
request = factory.get(self.ENDPOINT)
request.user = self.user
# Fake middleware support for RequestFactory
# See https://stackoverflow.com/a/66473588/1022690
setattr(request, "session", "session")
setattr(request, "_messages", FallbackStorage(request))
msg = "Test message"
messages.error(request, msg)
response = MessagesView.as_view()(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]["level"], "error")
self.assertEqual(response.data[0]["message"], msg)

View File

@ -45,6 +45,7 @@ from paperless.views import DisconnectSocialAccountView
from paperless.views import FaviconView
from paperless.views import GenerateAuthTokenView
from paperless.views import GroupViewSet
from paperless.views import MessagesView
from paperless.views import ProfileView
from paperless.views import SocialAccountProvidersView
from paperless.views import UserViewSet
@ -147,6 +148,7 @@ urlpatterns = [
ProfileView.as_view(),
name="profile_view",
),
path("messages/", MessagesView.as_view()),
*api_router.urls,
],
),

View File

@ -2,6 +2,7 @@ import os
from collections import OrderedDict
from allauth.socialaccount.adapter import get_adapter
from django.contrib import messages
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.db.models.functions import Lower
@ -222,3 +223,18 @@ class SocialAccountProvidersView(APIView):
]
return Response(sorted(resp, key=lambda p: p["name"]))
class MessagesView(APIView):
"""
Expose django messages. This clears the messages in django
"""
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
data = [
{"level": m.level_tag, "message": m.message, "tags": m.extra_tags}
for m in messages.get_messages(request)
]
return Response(data)