mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-15 10:13:15 -05:00
Use different channels, refactor because now its not just consumer
This commit is contained in:
parent
37368e64bd
commit
6eb8479871
@ -9233,122 +9233,6 @@
|
|||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">11</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2119857572761283468" datatype="html">
|
|
||||||
<source>Document already exists.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">17</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5103087968344279314" datatype="html">
|
|
||||||
<source>Document already exists. Note: existing document is in the trash.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">18</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6108404046106249255" datatype="html">
|
|
||||||
<source>Document with ASN already exists.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">19</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="65951081560571094" datatype="html">
|
|
||||||
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">20</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="148389968432135849" datatype="html">
|
|
||||||
<source>File not found.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">21</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1520671543092565667" datatype="html">
|
|
||||||
<source>Pre-consume script does not exist.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">22</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="7742915911032564889" datatype="html">
|
|
||||||
<source>Error while executing pre-consume script.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">23</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8995193730018060346" datatype="html">
|
|
||||||
<source>Post-consume script does not exist.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">24</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="256773668518189604" datatype="html">
|
|
||||||
<source>Error while executing post-consume script.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">25</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6252258095055634191" datatype="html">
|
|
||||||
<source>Received new file.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">26</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="7337565919209746135" datatype="html">
|
|
||||||
<source>File type not supported.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">27</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5002399167376099234" datatype="html">
|
|
||||||
<source>Processing document...</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">28</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1085975194762600381" datatype="html">
|
|
||||||
<source>Generating thumbnail...</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">29</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="3280851677698431426" datatype="html">
|
|
||||||
<source>Retrieving date from document...</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">30</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="7162102384876037296" datatype="html">
|
|
||||||
<source>Saving document...</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">31</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="4550450765009165976" datatype="html">
|
|
||||||
<source>Finished.</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
|
|
||||||
<context context-type="linenumber">32</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5523607037798226031" datatype="html">
|
<trans-unit id="5523607037798226031" datatype="html">
|
||||||
<source>You have unsaved changes to the document</source>
|
<source>You have unsaved changes to the document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
@ -9664,6 +9548,122 @@
|
|||||||
<context context-type="linenumber">70</context>
|
<context context-type="linenumber">70</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="2119857572761283468" datatype="html">
|
||||||
|
<source>Document already exists.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">23</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5103087968344279314" datatype="html">
|
||||||
|
<source>Document already exists. Note: existing document is in the trash.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6108404046106249255" datatype="html">
|
||||||
|
<source>Document with ASN already exists.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">25</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="65951081560571094" datatype="html">
|
||||||
|
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">26</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="148389968432135849" datatype="html">
|
||||||
|
<source>File not found.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">27</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1520671543092565667" datatype="html">
|
||||||
|
<source>Pre-consume script does not exist.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">28</context>
|
||||||
|
</context-group>
|
||||||
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7742915911032564889" datatype="html">
|
||||||
|
<source>Error while executing pre-consume script.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">29</context>
|
||||||
|
</context-group>
|
||||||
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8995193730018060346" datatype="html">
|
||||||
|
<source>Post-consume script does not exist.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">30</context>
|
||||||
|
</context-group>
|
||||||
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="256773668518189604" datatype="html">
|
||||||
|
<source>Error while executing post-consume script.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">31</context>
|
||||||
|
</context-group>
|
||||||
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6252258095055634191" datatype="html">
|
||||||
|
<source>Received new file.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">32</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7337565919209746135" datatype="html">
|
||||||
|
<source>File type not supported.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">33</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5002399167376099234" datatype="html">
|
||||||
|
<source>Processing document...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">34</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1085975194762600381" datatype="html">
|
||||||
|
<source>Generating thumbnail...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">35</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3280851677698431426" datatype="html">
|
||||||
|
<source>Retrieving date from document...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">36</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7162102384876037296" datatype="html">
|
||||||
|
<source>Saving document...</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">37</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4550450765009165976" datatype="html">
|
||||||
|
<source>Finished.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
|
<context context-type="linenumber">38</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -18,20 +18,20 @@ import { ToastsComponent } from './components/common/toasts/toasts.component'
|
|||||||
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
||||||
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
|
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
|
||||||
import { PermissionsGuard } from './guards/permissions.guard'
|
import { PermissionsGuard } from './guards/permissions.guard'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
|
||||||
} from './services/consumer-status.service'
|
|
||||||
import { HotKeyService } from './services/hot-key.service'
|
import { HotKeyService } from './services/hot-key.service'
|
||||||
import { PermissionsService } from './services/permissions.service'
|
import { PermissionsService } from './services/permissions.service'
|
||||||
import { SettingsService } from './services/settings.service'
|
import { SettingsService } from './services/settings.service'
|
||||||
import { Toast, ToastService } from './services/toast.service'
|
import { Toast, ToastService } from './services/toast.service'
|
||||||
|
import {
|
||||||
|
FileStatus,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from './services/websocket-status.service'
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
let component: AppComponent
|
let component: AppComponent
|
||||||
let fixture: ComponentFixture<AppComponent>
|
let fixture: ComponentFixture<AppComponent>
|
||||||
let tourService: TourService
|
let tourService: TourService
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
let permissionsService: PermissionsService
|
let permissionsService: PermissionsService
|
||||||
let toastService: ToastService
|
let toastService: ToastService
|
||||||
let router: Router
|
let router: Router
|
||||||
@ -59,7 +59,7 @@ describe('AppComponent', () => {
|
|||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
tourService = TestBed.inject(TourService)
|
tourService = TestBed.inject(TourService)
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
permissionsService = TestBed.inject(PermissionsService)
|
permissionsService = TestBed.inject(PermissionsService)
|
||||||
settingsService = TestBed.inject(SettingsService)
|
settingsService = TestBed.inject(SettingsService)
|
||||||
toastService = TestBed.inject(ToastService)
|
toastService = TestBed.inject(ToastService)
|
||||||
@ -90,7 +90,7 @@ describe('AppComponent', () => {
|
|||||||
const toastSpy = jest.spyOn(toastService, 'show')
|
const toastSpy = jest.spyOn(toastService, 'show')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
const status = new FileStatus()
|
const status = new FileStatus()
|
||||||
@ -109,7 +109,7 @@ describe('AppComponent', () => {
|
|||||||
const toastSpy = jest.spyOn(toastService, 'show')
|
const toastSpy = jest.spyOn(toastService, 'show')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
@ -122,7 +122,7 @@ describe('AppComponent', () => {
|
|||||||
const toastSpy = jest.spyOn(toastService, 'show')
|
const toastSpy = jest.spyOn(toastService, 'show')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentDetected')
|
.spyOn(websocketStatusService, 'onDocumentDetected')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
@ -136,7 +136,7 @@ describe('AppComponent', () => {
|
|||||||
const toastSpy = jest.spyOn(toastService, 'show')
|
const toastSpy = jest.spyOn(toastService, 'show')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentDetected')
|
.spyOn(websocketStatusService, 'onDocumentDetected')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
@ -148,7 +148,7 @@ describe('AppComponent', () => {
|
|||||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFailed')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFailed')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
|
@ -6,7 +6,6 @@ import { ToastsComponent } from './components/common/toasts/toasts.component'
|
|||||||
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
import { FileDropComponent } from './components/file-drop/file-drop.component'
|
||||||
import { SETTINGS_KEYS } from './data/ui-settings'
|
import { SETTINGS_KEYS } from './data/ui-settings'
|
||||||
import { ComponentRouterService } from './services/component-router.service'
|
import { ComponentRouterService } from './services/component-router.service'
|
||||||
import { ConsumerStatusService } from './services/consumer-status.service'
|
|
||||||
import { HotKeyService } from './services/hot-key.service'
|
import { HotKeyService } from './services/hot-key.service'
|
||||||
import {
|
import {
|
||||||
PermissionAction,
|
PermissionAction,
|
||||||
@ -16,6 +15,7 @@ import {
|
|||||||
import { SettingsService } from './services/settings.service'
|
import { SettingsService } from './services/settings.service'
|
||||||
import { TasksService } from './services/tasks.service'
|
import { TasksService } from './services/tasks.service'
|
||||||
import { ToastService } from './services/toast.service'
|
import { ToastService } from './services/toast.service'
|
||||||
|
import { WebsocketStatusService } from './services/websocket-status.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-root',
|
selector: 'pngx-root',
|
||||||
@ -35,7 +35,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private settings: SettingsService,
|
private settings: SettingsService,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private websocketStatusService: WebsocketStatusService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private tasksService: TasksService,
|
private tasksService: TasksService,
|
||||||
@ -51,7 +51,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.consumerStatusService.disconnect()
|
this.websocketStatusService.disconnect()
|
||||||
if (this.successSubscription) {
|
if (this.successSubscription) {
|
||||||
this.successSubscription.unsubscribe()
|
this.successSubscription.unsubscribe()
|
||||||
}
|
}
|
||||||
@ -76,9 +76,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.consumerStatusService.connect()
|
this.websocketStatusService.connect()
|
||||||
|
|
||||||
this.successSubscription = this.consumerStatusService
|
this.successSubscription = this.websocketStatusService
|
||||||
.onDocumentConsumptionFinished()
|
.onDocumentConsumptionFinished()
|
||||||
.subscribe((status) => {
|
.subscribe((status) => {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
@ -108,7 +108,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.failedSubscription = this.consumerStatusService
|
this.failedSubscription = this.websocketStatusService
|
||||||
.onDocumentConsumptionFailed()
|
.onDocumentConsumptionFailed()
|
||||||
.subscribe((status) => {
|
.subscribe((status) => {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
@ -121,7 +121,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.newDocumentSubscription = this.consumerStatusService
|
this.newDocumentSubscription = this.websocketStatusService
|
||||||
.onDocumentDetected()
|
.onDocumentDetected()
|
||||||
.subscribe((status) => {
|
.subscribe((status) => {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
|
@ -33,14 +33,14 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
|||||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||||
import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe'
|
import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
|
||||||
} from 'src/app/services/consumer-status.service'
|
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
|
import {
|
||||||
|
FileStatus,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from 'src/app/services/websocket-status.service'
|
||||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||||
import { SavedViewWidgetComponent } from './saved-view-widget.component'
|
import { SavedViewWidgetComponent } from './saved-view-widget.component'
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ describe('SavedViewWidgetComponent', () => {
|
|||||||
let component: SavedViewWidgetComponent
|
let component: SavedViewWidgetComponent
|
||||||
let fixture: ComponentFixture<SavedViewWidgetComponent>
|
let fixture: ComponentFixture<SavedViewWidgetComponent>
|
||||||
let documentService: DocumentService
|
let documentService: DocumentService
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
let documentListViewService: DocumentListViewService
|
let documentListViewService: DocumentListViewService
|
||||||
let router: Router
|
let router: Router
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ describe('SavedViewWidgetComponent', () => {
|
|||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
documentService = TestBed.inject(DocumentService)
|
documentService = TestBed.inject(DocumentService)
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
documentListViewService = TestBed.inject(DocumentListViewService)
|
documentListViewService = TestBed.inject(DocumentListViewService)
|
||||||
router = TestBed.inject(Router)
|
router = TestBed.inject(Router)
|
||||||
fixture = TestBed.createComponent(SavedViewWidgetComponent)
|
fixture = TestBed.createComponent(SavedViewWidgetComponent)
|
||||||
@ -235,7 +235,7 @@ describe('SavedViewWidgetComponent', () => {
|
|||||||
it('should reload on document consumption finished', () => {
|
it('should reload on document consumption finished', () => {
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
const reloadSpy = jest.spyOn(component, 'reload')
|
const reloadSpy = jest.spyOn(component, 'reload')
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
|
@ -42,7 +42,6 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
|||||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
|
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
||||||
import {
|
import {
|
||||||
@ -53,6 +52,7 @@ import {
|
|||||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
|
import { WebsocketStatusService } from 'src/app/services/websocket-status.service'
|
||||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -94,7 +94,7 @@ export class SavedViewWidgetComponent
|
|||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private list: DocumentListViewService,
|
private list: DocumentListViewService,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private websocketStatusService: WebsocketStatusService,
|
||||||
public openDocumentsService: OpenDocumentsService,
|
public openDocumentsService: OpenDocumentsService,
|
||||||
public documentListViewService: DocumentListViewService,
|
public documentListViewService: DocumentListViewService,
|
||||||
public permissionsService: PermissionsService,
|
public permissionsService: PermissionsService,
|
||||||
@ -124,7 +124,7 @@ export class SavedViewWidgetComponent
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.reload()
|
this.reload()
|
||||||
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
|
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
|
||||||
this.consumerStatusService
|
this.websocketStatusService
|
||||||
.onDocumentConsumptionFinished()
|
.onDocumentConsumptionFinished()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
|
@ -12,9 +12,9 @@ import { routes } from 'src/app/app-routing.module'
|
|||||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||||
import {
|
import {
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
FileStatus,
|
||||||
} from 'src/app/services/consumer-status.service'
|
WebsocketStatusService,
|
||||||
|
} from 'src/app/services/websocket-status.service'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||||
import { StatisticsWidgetComponent } from './statistics-widget.component'
|
import { StatisticsWidgetComponent } from './statistics-widget.component'
|
||||||
@ -23,7 +23,7 @@ describe('StatisticsWidgetComponent', () => {
|
|||||||
let component: StatisticsWidgetComponent
|
let component: StatisticsWidgetComponent
|
||||||
let fixture: ComponentFixture<StatisticsWidgetComponent>
|
let fixture: ComponentFixture<StatisticsWidgetComponent>
|
||||||
let httpTestingController: HttpTestingController
|
let httpTestingController: HttpTestingController
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -44,9 +44,9 @@ describe('StatisticsWidgetComponent', () => {
|
|||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
fixture = TestBed.createComponent(StatisticsWidgetComponent)
|
fixture = TestBed.createComponent(StatisticsWidgetComponent)
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import { first, Subject, Subscription, takeUntil } from 'rxjs'
|
|||||||
import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component'
|
||||||
import { FILTER_HAS_TAGS_ANY } from 'src/app/data/filter-rule-type'
|
import { FILTER_HAS_TAGS_ANY } from 'src/app/data/filter-rule-type'
|
||||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
|
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
|
import { WebsocketStatusService } from 'src/app/services/websocket-status.service'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export class StatisticsWidgetComponent
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private websocketConnectionService: WebsocketStatusService,
|
||||||
private documentListViewService: DocumentListViewService
|
private documentListViewService: DocumentListViewService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
@ -109,7 +109,7 @@ export class StatisticsWidgetComponent
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.reload()
|
this.reload()
|
||||||
this.subscription = this.consumerStatusService
|
this.subscription = this.websocketConnectionService
|
||||||
.onDocumentConsumptionFinished()
|
.onDocumentConsumptionFinished()
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.reload()
|
this.reload()
|
||||||
|
@ -12,13 +12,13 @@ import { NgbAlert, NgbCollapse } from '@ng-bootstrap/ng-bootstrap'
|
|||||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||||
import { routes } from 'src/app/app-routing.module'
|
import { routes } from 'src/app/app-routing.module'
|
||||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
|
||||||
FileStatusPhase,
|
|
||||||
} from 'src/app/services/consumer-status.service'
|
|
||||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||||
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
|
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
|
||||||
|
import {
|
||||||
|
FileStatus,
|
||||||
|
FileStatusPhase,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from 'src/app/services/websocket-status.service'
|
||||||
import { UploadFileWidgetComponent } from './upload-file-widget.component'
|
import { UploadFileWidgetComponent } from './upload-file-widget.component'
|
||||||
|
|
||||||
const FAILED_STATUSES = [new FileStatus()]
|
const FAILED_STATUSES = [new FileStatus()]
|
||||||
@ -42,7 +42,7 @@ const DEFAULT_STATUSES = [
|
|||||||
describe('UploadFileWidgetComponent', () => {
|
describe('UploadFileWidgetComponent', () => {
|
||||||
let component: UploadFileWidgetComponent
|
let component: UploadFileWidgetComponent
|
||||||
let fixture: ComponentFixture<UploadFileWidgetComponent>
|
let fixture: ComponentFixture<UploadFileWidgetComponent>
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
let uploadDocumentsService: UploadDocumentsService
|
let uploadDocumentsService: UploadDocumentsService
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -65,7 +65,7 @@ describe('UploadFileWidgetComponent', () => {
|
|||||||
],
|
],
|
||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
||||||
fixture = TestBed.createComponent(UploadFileWidgetComponent)
|
fixture = TestBed.createComponent(UploadFileWidgetComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
@ -91,14 +91,14 @@ describe('UploadFileWidgetComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should generate stats summary', () => {
|
it('should generate stats summary', () => {
|
||||||
mockConsumerStatuses(consumerStatusService)
|
mockConsumerStatuses(websocketStatusService)
|
||||||
expect(component.getStatusSummary()).toEqual(
|
expect(component.getStatusSummary()).toEqual(
|
||||||
'Processing: 6, Failed: 1, Added: 4'
|
'Processing: 6, Failed: 1, Added: 4'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should report an upload progress summary', () => {
|
it('should report an upload progress summary', () => {
|
||||||
mockConsumerStatuses(consumerStatusService)
|
mockConsumerStatuses(websocketStatusService)
|
||||||
expect(component.getTotalUploadProgress()).toEqual(0.75)
|
expect(component.getTotalUploadProgress()).toEqual(0.75)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ describe('UploadFileWidgetComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should enforce a maximum number of alerts', () => {
|
it('should enforce a maximum number of alerts', () => {
|
||||||
mockConsumerStatuses(consumerStatusService)
|
mockConsumerStatuses(websocketStatusService)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
// 5 total, 1 hidden
|
// 5 total, 1 hidden
|
||||||
expect(fixture.debugElement.queryAll(By.directive(NgbAlert))).toHaveLength(
|
expect(fixture.debugElement.queryAll(By.directive(NgbAlert))).toHaveLength(
|
||||||
@ -131,19 +131,19 @@ describe('UploadFileWidgetComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should allow dismissing an alert', () => {
|
it('should allow dismissing an alert', () => {
|
||||||
const dismissSpy = jest.spyOn(consumerStatusService, 'dismiss')
|
const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss')
|
||||||
component.dismiss(new FileStatus())
|
component.dismiss(new FileStatus())
|
||||||
expect(dismissSpy).toHaveBeenCalled()
|
expect(dismissSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow dismissing completed alerts', fakeAsync(() => {
|
it('should allow dismissing completed alerts', fakeAsync(() => {
|
||||||
mockConsumerStatuses(consumerStatusService)
|
mockConsumerStatuses(websocketStatusService)
|
||||||
component.alertsExpanded = true
|
component.alertsExpanded = true
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
jest
|
jest
|
||||||
.spyOn(component, 'getStatusCompleted')
|
.spyOn(component, 'getStatusCompleted')
|
||||||
.mockImplementation(() => SUCCESS_STATUSES)
|
.mockImplementation(() => SUCCESS_STATUSES)
|
||||||
const dismissSpy = jest.spyOn(consumerStatusService, 'dismiss')
|
const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss')
|
||||||
component.dismissCompleted()
|
component.dismissCompleted()
|
||||||
tick(1000)
|
tick(1000)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
@ -12,13 +12,13 @@ import { TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
|
|||||||
import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
|
||||||
FileStatusPhase,
|
|
||||||
} from 'src/app/services/consumer-status.service'
|
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
|
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
|
||||||
|
import {
|
||||||
|
FileStatus,
|
||||||
|
FileStatusPhase,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from 'src/app/services/websocket-status.service'
|
||||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||||
|
|
||||||
const MAX_ALERTS = 5
|
const MAX_ALERTS = 5
|
||||||
@ -46,7 +46,7 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions {
|
|||||||
@ViewChildren(NgbAlert) alerts: QueryList<NgbAlert>
|
@ViewChildren(NgbAlert) alerts: QueryList<NgbAlert>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private websocketStatusService: WebsocketStatusService,
|
||||||
private uploadDocumentsService: UploadDocumentsService,
|
private uploadDocumentsService: UploadDocumentsService,
|
||||||
public settingsService: SettingsService
|
public settingsService: SettingsService
|
||||||
) {
|
) {
|
||||||
@ -54,13 +54,13 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getStatus() {
|
getStatus() {
|
||||||
return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
|
return this.websocketStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusSummary() {
|
getStatusSummary() {
|
||||||
let strings = []
|
let strings = []
|
||||||
let countUploadingAndProcessing =
|
let countUploadingAndProcessing =
|
||||||
this.consumerStatusService.getConsumerStatusNotCompleted().length
|
this.websocketStatusService.getConsumerStatusNotCompleted().length
|
||||||
let countFailed = this.getStatusFailed().length
|
let countFailed = this.getStatusFailed().length
|
||||||
let countSuccess = this.getStatusSuccess().length
|
let countSuccess = this.getStatusSuccess().length
|
||||||
if (countUploadingAndProcessing > 0) {
|
if (countUploadingAndProcessing > 0) {
|
||||||
@ -78,27 +78,30 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getStatusHidden() {
|
getStatusHidden() {
|
||||||
if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS)
|
if (this.websocketStatusService.getConsumerStatus().length < MAX_ALERTS)
|
||||||
return []
|
return []
|
||||||
else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
|
else
|
||||||
|
return this.websocketStatusService.getConsumerStatus().slice(MAX_ALERTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusUploading() {
|
getStatusUploading() {
|
||||||
return this.consumerStatusService.getConsumerStatus(
|
return this.websocketStatusService.getConsumerStatus(
|
||||||
FileStatusPhase.UPLOADING
|
FileStatusPhase.UPLOADING
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusFailed() {
|
getStatusFailed() {
|
||||||
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
return this.websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusSuccess() {
|
getStatusSuccess() {
|
||||||
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.SUCCESS)
|
return this.websocketStatusService.getConsumerStatus(
|
||||||
|
FileStatusPhase.SUCCESS
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusCompleted() {
|
getStatusCompleted() {
|
||||||
return this.consumerStatusService.getConsumerStatusCompleted()
|
return this.websocketStatusService.getConsumerStatusCompleted()
|
||||||
}
|
}
|
||||||
|
|
||||||
getTotalUploadProgress() {
|
getTotalUploadProgress() {
|
||||||
@ -134,12 +137,12 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dismiss(status: FileStatus) {
|
dismiss(status: FileStatus) {
|
||||||
this.consumerStatusService.dismiss(status)
|
this.websocketStatusService.dismiss(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissCompleted() {
|
dismissCompleted() {
|
||||||
this.getStatusCompleted().forEach((status) =>
|
this.getStatusCompleted().forEach((status) =>
|
||||||
this.consumerStatusService.dismiss(status)
|
this.websocketStatusService.dismiss(status)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,16 +38,16 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
|||||||
import { FilterPipe } from 'src/app/pipes/filter.pipe'
|
import { FilterPipe } from 'src/app/pipes/filter.pipe'
|
||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatus,
|
|
||||||
} from 'src/app/services/consumer-status.service'
|
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import {
|
||||||
|
FileStatus,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from 'src/app/services/websocket-status.service'
|
||||||
import { DocumentCardLargeComponent } from './document-card-large/document-card-large.component'
|
import { DocumentCardLargeComponent } from './document-card-large/document-card-large.component'
|
||||||
import { DocumentCardSmallComponent } from './document-card-small/document-card-small.component'
|
import { DocumentCardSmallComponent } from './document-card-small/document-card-small.component'
|
||||||
import { DocumentListComponent } from './document-list.component'
|
import { DocumentListComponent } from './document-list.component'
|
||||||
@ -81,7 +81,7 @@ describe('DocumentListComponent', () => {
|
|||||||
let fixture: ComponentFixture<DocumentListComponent>
|
let fixture: ComponentFixture<DocumentListComponent>
|
||||||
let documentListService: DocumentListViewService
|
let documentListService: DocumentListViewService
|
||||||
let documentService: DocumentService
|
let documentService: DocumentService
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
let savedViewService: SavedViewService
|
let savedViewService: SavedViewService
|
||||||
let router: Router
|
let router: Router
|
||||||
let activatedRoute: ActivatedRoute
|
let activatedRoute: ActivatedRoute
|
||||||
@ -112,7 +112,7 @@ describe('DocumentListComponent', () => {
|
|||||||
|
|
||||||
documentListService = TestBed.inject(DocumentListViewService)
|
documentListService = TestBed.inject(DocumentListViewService)
|
||||||
documentService = TestBed.inject(DocumentService)
|
documentService = TestBed.inject(DocumentService)
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
savedViewService = TestBed.inject(SavedViewService)
|
savedViewService = TestBed.inject(SavedViewService)
|
||||||
router = TestBed.inject(Router)
|
router = TestBed.inject(Router)
|
||||||
activatedRoute = TestBed.inject(ActivatedRoute)
|
activatedRoute = TestBed.inject(ActivatedRoute)
|
||||||
@ -128,7 +128,7 @@ describe('DocumentListComponent', () => {
|
|||||||
const reloadSpy = jest.spyOn(documentListService, 'reload')
|
const reloadSpy = jest.spyOn(documentListService, 'reload')
|
||||||
const fileStatusSubject = new Subject<FileStatus>()
|
const fileStatusSubject = new Subject<FileStatus>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentConsumptionFinished')
|
.spyOn(websocketStatusService, 'onDocumentConsumptionFinished')
|
||||||
.mockReturnValue(fileStatusSubject)
|
.mockReturnValue(fileStatusSubject)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
fileStatusSubject.next(new FileStatus())
|
fileStatusSubject.next(new FileStatus())
|
||||||
@ -139,7 +139,7 @@ describe('DocumentListComponent', () => {
|
|||||||
const reloadSpy = jest.spyOn(documentListService, 'reload')
|
const reloadSpy = jest.spyOn(documentListService, 'reload')
|
||||||
const documentDeletedSubject = new Subject<boolean>()
|
const documentDeletedSubject = new Subject<boolean>()
|
||||||
jest
|
jest
|
||||||
.spyOn(consumerStatusService, 'onDocumentDeleted')
|
.spyOn(websocketStatusService, 'onDocumentDeleted')
|
||||||
.mockReturnValue(documentDeletedSubject)
|
.mockReturnValue(documentDeletedSubject)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
documentDeletedSubject.next(true)
|
documentDeletedSubject.next(true)
|
||||||
|
@ -43,7 +43,6 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
|||||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
|
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||||
import { HotKeyService } from 'src/app/services/hot-key.service'
|
import { HotKeyService } from 'src/app/services/hot-key.service'
|
||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
||||||
@ -51,6 +50,7 @@ import { PermissionsService } from 'src/app/services/permissions.service'
|
|||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { WebsocketStatusService } from 'src/app/services/websocket-status.service'
|
||||||
import {
|
import {
|
||||||
filterRulesDiffer,
|
filterRulesDiffer,
|
||||||
isFullTextFilterRule,
|
isFullTextFilterRule,
|
||||||
@ -113,7 +113,7 @@ export class DocumentListComponent
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private consumerStatusService: ConsumerStatusService,
|
private websocketStatusService: WebsocketStatusService,
|
||||||
public openDocumentsService: OpenDocumentsService,
|
public openDocumentsService: OpenDocumentsService,
|
||||||
public settingsService: SettingsService,
|
public settingsService: SettingsService,
|
||||||
private hotKeyService: HotKeyService,
|
private hotKeyService: HotKeyService,
|
||||||
@ -234,14 +234,14 @@ export class DocumentListComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.consumerStatusService
|
this.websocketStatusService
|
||||||
.onDocumentConsumptionFinished()
|
.onDocumentConsumptionFinished()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.list.reload()
|
this.list.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.consumerStatusService.onDocumentDeleted().subscribe(() => {
|
this.websocketStatusService.onDocumentDeleted().subscribe(() => {
|
||||||
this.list.reload()
|
this.list.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
export interface WebsocketDocumentsDeletedMessage {
|
||||||
|
documents: number[]
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
export interface WebsocketConsumerStatusMessage {
|
export interface WebsocketProgressMessage {
|
||||||
filename?: string
|
filename?: string
|
||||||
task_id?: string
|
task_id?: string
|
||||||
current_progress?: number
|
current_progress?: number
|
@ -1,343 +0,0 @@
|
|||||||
import {
|
|
||||||
HttpEventType,
|
|
||||||
HttpResponse,
|
|
||||||
provideHttpClient,
|
|
||||||
withInterceptorsFromDi,
|
|
||||||
} from '@angular/common/http'
|
|
||||||
import {
|
|
||||||
HttpTestingController,
|
|
||||||
provideHttpClientTesting,
|
|
||||||
} from '@angular/common/http/testing'
|
|
||||||
import { TestBed } from '@angular/core/testing'
|
|
||||||
import WS from 'jest-websocket-mock'
|
|
||||||
import { environment } from 'src/environments/environment'
|
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FILE_STATUS_MESSAGES,
|
|
||||||
FileStatusPhase,
|
|
||||||
} from './consumer-status.service'
|
|
||||||
import { DocumentService } from './rest/document.service'
|
|
||||||
import { SettingsService } from './settings.service'
|
|
||||||
|
|
||||||
describe('ConsumerStatusService', () => {
|
|
||||||
let httpTestingController: HttpTestingController
|
|
||||||
let consumerStatusService: ConsumerStatusService
|
|
||||||
let documentService: DocumentService
|
|
||||||
let settingsService: SettingsService
|
|
||||||
|
|
||||||
const server = new WS(
|
|
||||||
`${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`,
|
|
||||||
{ jsonProtocol: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [],
|
|
||||||
providers: [
|
|
||||||
ConsumerStatusService,
|
|
||||||
DocumentService,
|
|
||||||
SettingsService,
|
|
||||||
provideHttpClient(withInterceptorsFromDi()),
|
|
||||||
provideHttpClientTesting(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
httpTestingController = TestBed.inject(HttpTestingController)
|
|
||||||
settingsService = TestBed.inject(SettingsService)
|
|
||||||
settingsService.currentUser = {
|
|
||||||
id: 1,
|
|
||||||
username: 'testuser',
|
|
||||||
is_superuser: false,
|
|
||||||
}
|
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
|
||||||
documentService = TestBed.inject(DocumentService)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
httpTestingController.verify()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update status on websocket processing progress', () => {
|
|
||||||
const task_id = '1234'
|
|
||||||
const status = consumerStatusService.newFileUpload('file.pdf')
|
|
||||||
expect(status.getProgress()).toEqual(0)
|
|
||||||
|
|
||||||
consumerStatusService.connect()
|
|
||||||
|
|
||||||
consumerStatusService
|
|
||||||
.onDocumentConsumptionFinished()
|
|
||||||
.subscribe((filestatus) => {
|
|
||||||
expect(filestatus.phase).toEqual(FileStatusPhase.SUCCESS)
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.onDocumentDetected().subscribe((filestatus) => {
|
|
||||||
expect(filestatus.phase).toEqual(FileStatusPhase.STARTED)
|
|
||||||
})
|
|
||||||
|
|
||||||
server.send({
|
|
||||||
task_id,
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
document_id: 12,
|
|
||||||
status: 'WORKING',
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(status.getProgress()).toBeCloseTo(0.6) // (0.8 * 50/100) + .2
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([
|
|
||||||
status,
|
|
||||||
])
|
|
||||||
|
|
||||||
server.send({
|
|
||||||
task_id,
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 100,
|
|
||||||
max_progress: 100,
|
|
||||||
document_id: 12,
|
|
||||||
status: 'SUCCESS',
|
|
||||||
message: FILE_STATUS_MESSAGES.finished,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(status.getProgress()).toEqual(1)
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
0
|
|
||||||
)
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
|
||||||
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update status on websocket failed progress', () => {
|
|
||||||
const task_id = '1234'
|
|
||||||
const status = consumerStatusService.newFileUpload('file.pdf')
|
|
||||||
status.taskId = task_id
|
|
||||||
consumerStatusService.connect()
|
|
||||||
|
|
||||||
consumerStatusService
|
|
||||||
.onDocumentConsumptionFailed()
|
|
||||||
.subscribe((filestatus) => {
|
|
||||||
expect(filestatus.phase).toEqual(FileStatusPhase.FAILED)
|
|
||||||
})
|
|
||||||
|
|
||||||
server.send({
|
|
||||||
task_id,
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
document_id: 12,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([
|
|
||||||
status,
|
|
||||||
])
|
|
||||||
|
|
||||||
server.send({
|
|
||||||
task_id,
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
document_id: 12,
|
|
||||||
status: 'FAILED',
|
|
||||||
message: FILE_STATUS_MESSAGES.document_already_exists,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
0
|
|
||||||
)
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update status on upload progress', () => {
|
|
||||||
const task_id = '1234'
|
|
||||||
const status = consumerStatusService.newFileUpload('file.pdf')
|
|
||||||
|
|
||||||
documentService.uploadDocument({}).subscribe((event) => {
|
|
||||||
if (event.type === HttpEventType.Response) {
|
|
||||||
status.taskId = event.body['task_id']
|
|
||||||
status.message = $localize`Upload complete, waiting...`
|
|
||||||
} else if (event.type === HttpEventType.UploadProgress) {
|
|
||||||
status.updateProgress(
|
|
||||||
FileStatusPhase.UPLOADING,
|
|
||||||
event.loaded,
|
|
||||||
event.total
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const req = httpTestingController.expectOne(
|
|
||||||
`${environment.apiBaseUrl}documents/post_document/`
|
|
||||||
)
|
|
||||||
|
|
||||||
req.event(
|
|
||||||
new HttpResponse({
|
|
||||||
body: {
|
|
||||||
task_id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
req.event({
|
|
||||||
type: HttpEventType.UploadProgress,
|
|
||||||
loaded: 100,
|
|
||||||
total: 300,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(
|
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
|
||||||
).toEqual([status])
|
|
||||||
expect(consumerStatusService.getConsumerStatus()).toEqual([status])
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([
|
|
||||||
status,
|
|
||||||
])
|
|
||||||
|
|
||||||
req.event({
|
|
||||||
type: HttpEventType.UploadProgress,
|
|
||||||
loaded: 300,
|
|
||||||
total: 300,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(status.getProgress()).toEqual(0.2) // 0.2 * 300/300
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support dismiss completed', () => {
|
|
||||||
consumerStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
task_id: '1234',
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 100,
|
|
||||||
max_progress: 100,
|
|
||||||
document_id: 12,
|
|
||||||
status: 'SUCCESS',
|
|
||||||
message: 'finished',
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
|
||||||
consumerStatusService.dismissCompleted()
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(0)
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support dismiss', () => {
|
|
||||||
const task_id = '1234'
|
|
||||||
const status = consumerStatusService.newFileUpload('file.pdf')
|
|
||||||
status.taskId = task_id
|
|
||||||
status.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
|
||||||
|
|
||||||
const status2 = consumerStatusService.newFileUpload('file2.pdf')
|
|
||||||
status2.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
|
||||||
|
|
||||||
expect(
|
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
|
||||||
).toEqual([status, status2])
|
|
||||||
expect(consumerStatusService.getConsumerStatus()).toEqual([status, status2])
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toEqual([
|
|
||||||
status,
|
|
||||||
status2,
|
|
||||||
])
|
|
||||||
|
|
||||||
consumerStatusService.dismiss(status)
|
|
||||||
expect(consumerStatusService.getConsumerStatus()).toEqual([status2])
|
|
||||||
|
|
||||||
consumerStatusService.dismiss(status2)
|
|
||||||
expect(consumerStatusService.getConsumerStatus()).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support fail', () => {
|
|
||||||
const task_id = '1234'
|
|
||||||
const status = consumerStatusService.newFileUpload('file.pdf')
|
|
||||||
status.taskId = task_id
|
|
||||||
status.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
1
|
|
||||||
)
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(0)
|
|
||||||
consumerStatusService.fail(status, 'fail')
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
0
|
|
||||||
)
|
|
||||||
expect(consumerStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should notify of document created on status message without upload', () => {
|
|
||||||
let detected = false
|
|
||||||
consumerStatusService.onDocumentDetected().subscribe((filestatus) => {
|
|
||||||
expect(filestatus.phase).toEqual(FileStatusPhase.STARTED)
|
|
||||||
detected = true
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
task_id: '1234',
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 0,
|
|
||||||
max_progress: 100,
|
|
||||||
message: 'new_file',
|
|
||||||
status: 'STARTED',
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
expect(detected).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should notify of document in progress without upload', () => {
|
|
||||||
consumerStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
task_id: '1234',
|
|
||||||
filename: 'file.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
docuement_id: 12,
|
|
||||||
status: 'WORKING',
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
1
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not notify current user if document has different expected owner', () => {
|
|
||||||
consumerStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
task_id: '1234',
|
|
||||||
filename: 'file1.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
docuement_id: 12,
|
|
||||||
owner_id: 1,
|
|
||||||
status: 'WORKING',
|
|
||||||
})
|
|
||||||
|
|
||||||
server.send({
|
|
||||||
task_id: '5678',
|
|
||||||
filename: 'file2.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
docuement_id: 13,
|
|
||||||
owner_id: 2,
|
|
||||||
status: 'WORKING',
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
1
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should trigger deleted subject on document deleted', () => {
|
|
||||||
let deleted = false
|
|
||||||
consumerStatusService.onDocumentDeleted().subscribe(() => {
|
|
||||||
deleted = true
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
current_progress: 1,
|
|
||||||
max_progress: 1,
|
|
||||||
status: 'DELETED',
|
|
||||||
})
|
|
||||||
|
|
||||||
consumerStatusService.disconnect()
|
|
||||||
expect(deleted).toBeTruthy()
|
|
||||||
})
|
|
||||||
})
|
|
@ -9,11 +9,11 @@ import {
|
|||||||
} from '@angular/common/http/testing'
|
} from '@angular/common/http/testing'
|
||||||
import { TestBed } from '@angular/core/testing'
|
import { TestBed } from '@angular/core/testing'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatusPhase,
|
|
||||||
} from './consumer-status.service'
|
|
||||||
import { UploadDocumentsService } from './upload-documents.service'
|
import { UploadDocumentsService } from './upload-documents.service'
|
||||||
|
import {
|
||||||
|
FileStatusPhase,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from './websocket-status.service'
|
||||||
|
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -45,14 +45,14 @@ const fileList = {
|
|||||||
describe('UploadDocumentsService', () => {
|
describe('UploadDocumentsService', () => {
|
||||||
let httpTestingController: HttpTestingController
|
let httpTestingController: HttpTestingController
|
||||||
let uploadDocumentsService: UploadDocumentsService
|
let uploadDocumentsService: UploadDocumentsService
|
||||||
let consumerStatusService: ConsumerStatusService
|
let websocketStatusService: WebsocketStatusService
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
UploadDocumentsService,
|
UploadDocumentsService,
|
||||||
ConsumerStatusService,
|
WebsocketStatusService,
|
||||||
provideHttpClient(withInterceptorsFromDi()),
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
provideHttpClientTesting(),
|
provideHttpClientTesting(),
|
||||||
],
|
],
|
||||||
@ -60,7 +60,7 @@ describe('UploadDocumentsService', () => {
|
|||||||
|
|
||||||
httpTestingController = TestBed.inject(HttpTestingController)
|
httpTestingController = TestBed.inject(HttpTestingController)
|
||||||
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
uploadDocumentsService = TestBed.inject(UploadDocumentsService)
|
||||||
consumerStatusService = TestBed.inject(ConsumerStatusService)
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -80,11 +80,11 @@ describe('UploadDocumentsService', () => {
|
|||||||
it('updates progress during upload and failure', () => {
|
it('updates progress during upload and failure', () => {
|
||||||
uploadDocumentsService.uploadFiles(fileList)
|
uploadDocumentsService.uploadFiles(fileList)
|
||||||
|
|
||||||
expect(consumerStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
expect(
|
expect(
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
||||||
).toHaveLength(0)
|
).toHaveLength(0)
|
||||||
|
|
||||||
const req = httpTestingController.match(
|
const req = httpTestingController.match(
|
||||||
@ -98,7 +98,7 @@ describe('UploadDocumentsService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
||||||
).toHaveLength(1)
|
).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ describe('UploadDocumentsService', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
||||||
).toHaveLength(0)
|
).toHaveLength(0)
|
||||||
|
|
||||||
req[0].flush(
|
req[0].flush(
|
||||||
@ -122,7 +122,7 @@ describe('UploadDocumentsService', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
||||||
).toHaveLength(1)
|
).toHaveLength(1)
|
||||||
|
|
||||||
uploadDocumentsService.uploadFiles(fileList)
|
uploadDocumentsService.uploadFiles(fileList)
|
||||||
@ -140,7 +140,7 @@ describe('UploadDocumentsService', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
websocketStatusService.getConsumerStatus(FileStatusPhase.FAILED)
|
||||||
).toHaveLength(2)
|
).toHaveLength(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ import { HttpEventType } from '@angular/common/http'
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
|
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import {
|
|
||||||
ConsumerStatusService,
|
|
||||||
FileStatusPhase,
|
|
||||||
} from './consumer-status.service'
|
|
||||||
import { DocumentService } from './rest/document.service'
|
import { DocumentService } from './rest/document.service'
|
||||||
|
import {
|
||||||
|
FileStatusPhase,
|
||||||
|
WebsocketStatusService,
|
||||||
|
} from './websocket-status.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -16,7 +16,7 @@ export class UploadDocumentsService {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
private consumerStatusService: ConsumerStatusService
|
private websocketStatusService: WebsocketStatusService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onNgxFileDrop(files: NgxFileDropEntry[]) {
|
onNgxFileDrop(files: NgxFileDropEntry[]) {
|
||||||
@ -37,7 +37,7 @@ export class UploadDocumentsService {
|
|||||||
private uploadFile(file: File) {
|
private uploadFile(file: File) {
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
formData.append('document', file, file.name)
|
formData.append('document', file, file.name)
|
||||||
let status = this.consumerStatusService.newFileUpload(file.name)
|
let status = this.websocketStatusService.newFileUpload(file.name)
|
||||||
|
|
||||||
status.message = $localize`Connecting...`
|
status.message = $localize`Connecting...`
|
||||||
|
|
||||||
@ -61,11 +61,11 @@ export class UploadDocumentsService {
|
|||||||
error: (error) => {
|
error: (error) => {
|
||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 400: {
|
case 400: {
|
||||||
this.consumerStatusService.fail(status, error.error.document)
|
this.websocketStatusService.fail(status, error.error.document)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
this.consumerStatusService.fail(
|
this.websocketStatusService.fail(
|
||||||
status,
|
status,
|
||||||
$localize`HTTP error: ${error.status} ${error.statusText}`
|
$localize`HTTP error: ${error.status} ${error.statusText}`
|
||||||
)
|
)
|
||||||
|
375
src-ui/src/app/services/websocket-status.service.spec.ts
Normal file
375
src-ui/src/app/services/websocket-status.service.spec.ts
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
import {
|
||||||
|
HttpEventType,
|
||||||
|
HttpResponse,
|
||||||
|
provideHttpClient,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http'
|
||||||
|
import {
|
||||||
|
HttpTestingController,
|
||||||
|
provideHttpClientTesting,
|
||||||
|
} from '@angular/common/http/testing'
|
||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
import WS from 'jest-websocket-mock'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
import { DocumentService } from './rest/document.service'
|
||||||
|
import { SettingsService } from './settings.service'
|
||||||
|
import {
|
||||||
|
FILE_STATUS_MESSAGES,
|
||||||
|
FileStatusPhase,
|
||||||
|
WebsocketStatusService,
|
||||||
|
WebsocketStatusType,
|
||||||
|
} from './websocket-status.service'
|
||||||
|
|
||||||
|
describe('ConsumerStatusService', () => {
|
||||||
|
let httpTestingController: HttpTestingController
|
||||||
|
let websocketStatusService: WebsocketStatusService
|
||||||
|
let documentService: DocumentService
|
||||||
|
let settingsService: SettingsService
|
||||||
|
|
||||||
|
const server = new WS(
|
||||||
|
`${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`,
|
||||||
|
{ jsonProtocol: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
WebsocketStatusService,
|
||||||
|
DocumentService,
|
||||||
|
SettingsService,
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
httpTestingController = TestBed.inject(HttpTestingController)
|
||||||
|
settingsService = TestBed.inject(SettingsService)
|
||||||
|
settingsService.currentUser = {
|
||||||
|
id: 1,
|
||||||
|
username: 'testuser',
|
||||||
|
is_superuser: false,
|
||||||
|
}
|
||||||
|
websocketStatusService = TestBed.inject(WebsocketStatusService)
|
||||||
|
documentService = TestBed.inject(DocumentService)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
httpTestingController.verify()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update status on websocket processing progress', () => {
|
||||||
|
const task_id = '1234'
|
||||||
|
const status = websocketStatusService.newFileUpload('file.pdf')
|
||||||
|
expect(status.getProgress()).toEqual(0)
|
||||||
|
|
||||||
|
websocketStatusService.connect()
|
||||||
|
|
||||||
|
websocketStatusService
|
||||||
|
.onDocumentConsumptionFinished()
|
||||||
|
.subscribe((filestatus) => {
|
||||||
|
expect(filestatus.phase).toEqual(FileStatusPhase.SUCCESS)
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.onDocumentDetected().subscribe((filestatus) => {
|
||||||
|
expect(filestatus.phase).toEqual(FileStatusPhase.STARTED)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id,
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
document_id: 12,
|
||||||
|
status: 'WORKING',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(status.getProgress()).toBeCloseTo(0.6) // (0.8 * 50/100) + .2
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([
|
||||||
|
status,
|
||||||
|
])
|
||||||
|
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id,
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 100,
|
||||||
|
max_progress: 100,
|
||||||
|
document_id: 12,
|
||||||
|
status: 'SUCCESS',
|
||||||
|
message: FILE_STATUS_MESSAGES.finished,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(status.getProgress()).toEqual(1)
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
||||||
|
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update status on websocket failed progress', () => {
|
||||||
|
const task_id = '1234'
|
||||||
|
const status = websocketStatusService.newFileUpload('file.pdf')
|
||||||
|
status.taskId = task_id
|
||||||
|
websocketStatusService.connect()
|
||||||
|
|
||||||
|
websocketStatusService
|
||||||
|
.onDocumentConsumptionFailed()
|
||||||
|
.subscribe((filestatus) => {
|
||||||
|
expect(filestatus.phase).toEqual(FileStatusPhase.FAILED)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id,
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
document_id: 12,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([
|
||||||
|
status,
|
||||||
|
])
|
||||||
|
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id,
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
document_id: 12,
|
||||||
|
status: 'FAILED',
|
||||||
|
message: FILE_STATUS_MESSAGES.document_already_exists,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update status on upload progress', () => {
|
||||||
|
const task_id = '1234'
|
||||||
|
const status = websocketStatusService.newFileUpload('file.pdf')
|
||||||
|
|
||||||
|
documentService.uploadDocument({}).subscribe((event) => {
|
||||||
|
if (event.type === HttpEventType.Response) {
|
||||||
|
status.taskId = event.body['task_id']
|
||||||
|
status.message = $localize`Upload complete, waiting...`
|
||||||
|
} else if (event.type === HttpEventType.UploadProgress) {
|
||||||
|
status.updateProgress(
|
||||||
|
FileStatusPhase.UPLOADING,
|
||||||
|
event.loaded,
|
||||||
|
event.total
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}documents/post_document/`
|
||||||
|
)
|
||||||
|
|
||||||
|
req.event(
|
||||||
|
new HttpResponse({
|
||||||
|
body: {
|
||||||
|
task_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
req.event({
|
||||||
|
type: HttpEventType.UploadProgress,
|
||||||
|
loaded: 100,
|
||||||
|
total: 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(
|
||||||
|
websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
||||||
|
).toEqual([status])
|
||||||
|
expect(websocketStatusService.getConsumerStatus()).toEqual([status])
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([
|
||||||
|
status,
|
||||||
|
])
|
||||||
|
|
||||||
|
req.event({
|
||||||
|
type: HttpEventType.UploadProgress,
|
||||||
|
loaded: 300,
|
||||||
|
total: 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(status.getProgress()).toEqual(0.2) // 0.2 * 300/300
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support dismiss completed', () => {
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '1234',
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 100,
|
||||||
|
max_progress: 100,
|
||||||
|
document_id: 12,
|
||||||
|
status: 'SUCCESS',
|
||||||
|
message: 'finished',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
||||||
|
websocketStatusService.dismissCompleted()
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(0)
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support dismiss', () => {
|
||||||
|
const task_id = '1234'
|
||||||
|
const status = websocketStatusService.newFileUpload('file.pdf')
|
||||||
|
status.taskId = task_id
|
||||||
|
status.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
||||||
|
|
||||||
|
const status2 = websocketStatusService.newFileUpload('file2.pdf')
|
||||||
|
status2.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
websocketStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
|
||||||
|
).toEqual([status, status2])
|
||||||
|
expect(websocketStatusService.getConsumerStatus()).toEqual([
|
||||||
|
status,
|
||||||
|
status2,
|
||||||
|
])
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toEqual([
|
||||||
|
status,
|
||||||
|
status2,
|
||||||
|
])
|
||||||
|
|
||||||
|
websocketStatusService.dismiss(status)
|
||||||
|
expect(websocketStatusService.getConsumerStatus()).toEqual([status2])
|
||||||
|
|
||||||
|
websocketStatusService.dismiss(status2)
|
||||||
|
expect(websocketStatusService.getConsumerStatus()).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support fail', () => {
|
||||||
|
const task_id = '1234'
|
||||||
|
const status = websocketStatusService.newFileUpload('file.pdf')
|
||||||
|
status.taskId = task_id
|
||||||
|
status.updateProgress(FileStatusPhase.UPLOADING, 50, 100)
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(0)
|
||||||
|
websocketStatusService.fail(status, 'fail')
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
expect(websocketStatusService.getConsumerStatusCompleted()).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should notify of document created on status message without upload', () => {
|
||||||
|
let detected = false
|
||||||
|
websocketStatusService.onDocumentDetected().subscribe((filestatus) => {
|
||||||
|
expect(filestatus.phase).toEqual(FileStatusPhase.STARTED)
|
||||||
|
detected = true
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '1234',
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 0,
|
||||||
|
max_progress: 100,
|
||||||
|
message: 'new_file',
|
||||||
|
status: 'STARTED',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
expect(detected).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should notify of document in progress without upload', () => {
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '1234',
|
||||||
|
filename: 'file.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
docuement_id: 12,
|
||||||
|
status: 'WORKING',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not notify current user if document has different expected owner', () => {
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '1234',
|
||||||
|
filename: 'file1.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
docuement_id: 12,
|
||||||
|
owner_id: 1,
|
||||||
|
status: 'WORKING',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '5678',
|
||||||
|
filename: 'file2.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
docuement_id: 13,
|
||||||
|
owner_id: 2,
|
||||||
|
status: 'WORKING',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should trigger deleted subject on document deleted', () => {
|
||||||
|
let deleted = false
|
||||||
|
websocketStatusService.onDocumentDeleted().subscribe(() => {
|
||||||
|
deleted = true
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.DOCUMENTS_DELETED,
|
||||||
|
data: {
|
||||||
|
documents: [1, 2, 3],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
websocketStatusService.disconnect()
|
||||||
|
expect(deleted).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
@ -1,9 +1,15 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { WebsocketConsumerStatusMessage } from '../data/websocket-consumer-status-message'
|
import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message'
|
||||||
|
import { WebsocketProgressMessage } from '../data/websocket-progress-message'
|
||||||
import { SettingsService } from './settings.service'
|
import { SettingsService } from './settings.service'
|
||||||
|
|
||||||
|
export enum WebsocketStatusType {
|
||||||
|
STATUS_UPDATE = 'status_update',
|
||||||
|
DOCUMENTS_DELETED = 'documents_deleted',
|
||||||
|
}
|
||||||
|
|
||||||
// see ProgressStatusOptions in src/documents/plugins/helpers.py
|
// see ProgressStatusOptions in src/documents/plugins/helpers.py
|
||||||
export enum FileStatusPhase {
|
export enum FileStatusPhase {
|
||||||
STARTED = 0,
|
STARTED = 0,
|
||||||
@ -85,7 +91,7 @@ export class FileStatus {
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ConsumerStatusService {
|
export class WebsocketStatusService {
|
||||||
constructor(private settingsService: SettingsService) {}
|
constructor(private settingsService: SettingsService) {}
|
||||||
|
|
||||||
private statusWebSocket: WebSocket
|
private statusWebSocket: WebSocket
|
||||||
@ -146,61 +152,22 @@ export class ConsumerStatusService {
|
|||||||
this.statusWebSocket = new WebSocket(
|
this.statusWebSocket = new WebSocket(
|
||||||
`${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`
|
`${environment.webSocketProtocol}//${environment.webSocketHost}${environment.webSocketBaseUrl}status/`
|
||||||
)
|
)
|
||||||
this.statusWebSocket.onmessage = (ev) => {
|
this.statusWebSocket.onmessage = (ev: MessageEvent) => {
|
||||||
let statusMessage: WebsocketConsumerStatusMessage = JSON.parse(ev['data'])
|
const {
|
||||||
|
type,
|
||||||
|
data: messageData,
|
||||||
|
}: {
|
||||||
|
type: WebsocketStatusType
|
||||||
|
data: WebsocketProgressMessage | WebsocketDocumentsDeletedMessage
|
||||||
|
} = JSON.parse(ev.data)
|
||||||
|
|
||||||
// special case for documents deletion
|
switch (type) {
|
||||||
if (statusMessage.status === 'DELETED') {
|
case WebsocketStatusType.STATUS_UPDATE:
|
||||||
this.documentDeletedSubject.next(true)
|
this.handleProgressUpdate(messageData as WebsocketProgressMessage)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback if backend didn't restrict message
|
|
||||||
if (
|
|
||||||
statusMessage.owner_id &&
|
|
||||||
statusMessage.owner_id !== this.settingsService.currentUser?.id &&
|
|
||||||
!this.settingsService.currentUser?.is_superuser
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let statusMessageGet = this.get(
|
|
||||||
statusMessage.task_id,
|
|
||||||
statusMessage.filename
|
|
||||||
)
|
|
||||||
let status = statusMessageGet.status
|
|
||||||
let created = statusMessageGet.created
|
|
||||||
|
|
||||||
status.updateProgress(
|
|
||||||
FileStatusPhase.WORKING,
|
|
||||||
statusMessage.current_progress,
|
|
||||||
statusMessage.max_progress
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
statusMessage.message &&
|
|
||||||
statusMessage.message in FILE_STATUS_MESSAGES
|
|
||||||
) {
|
|
||||||
status.message = FILE_STATUS_MESSAGES[statusMessage.message]
|
|
||||||
} else if (statusMessage.message) {
|
|
||||||
status.message = statusMessage.message
|
|
||||||
}
|
|
||||||
status.documentId = statusMessage.document_id
|
|
||||||
|
|
||||||
if (statusMessage.status in FileStatusPhase) {
|
|
||||||
status.phase = FileStatusPhase[statusMessage.status]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (status.phase) {
|
|
||||||
case FileStatusPhase.STARTED:
|
|
||||||
if (created) this.documentDetectedSubject.next(status)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case FileStatusPhase.SUCCESS:
|
case WebsocketStatusType.DOCUMENTS_DELETED:
|
||||||
this.documentConsumptionFinishedSubject.next(status)
|
this.documentDeletedSubject.next(true)
|
||||||
break
|
|
||||||
|
|
||||||
case FileStatusPhase.FAILED:
|
|
||||||
this.documentConsumptionFailedSubject.next(status)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -209,6 +176,54 @@ export class ConsumerStatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleProgressUpdate(messageData: WebsocketProgressMessage) {
|
||||||
|
// fallback if backend didn't restrict message
|
||||||
|
if (
|
||||||
|
messageData.owner_id &&
|
||||||
|
messageData.owner_id !== this.settingsService.currentUser?.id &&
|
||||||
|
!this.settingsService.currentUser?.is_superuser
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let statusMessageGet = this.get(messageData.task_id, messageData.filename)
|
||||||
|
let status = statusMessageGet.status
|
||||||
|
let created = statusMessageGet.created
|
||||||
|
|
||||||
|
status.updateProgress(
|
||||||
|
FileStatusPhase.WORKING,
|
||||||
|
messageData.current_progress,
|
||||||
|
messageData.max_progress
|
||||||
|
)
|
||||||
|
if (messageData.message && messageData.message in FILE_STATUS_MESSAGES) {
|
||||||
|
status.message = FILE_STATUS_MESSAGES[messageData.message]
|
||||||
|
} else if (messageData.message) {
|
||||||
|
status.message = messageData.message
|
||||||
|
}
|
||||||
|
status.documentId = messageData.document_id
|
||||||
|
|
||||||
|
if (messageData.status in FileStatusPhase) {
|
||||||
|
status.phase = FileStatusPhase[messageData.status]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status.phase) {
|
||||||
|
case FileStatusPhase.STARTED:
|
||||||
|
if (created) this.documentDetectedSubject.next(status)
|
||||||
|
break
|
||||||
|
|
||||||
|
case FileStatusPhase.SUCCESS:
|
||||||
|
this.documentConsumptionFinishedSubject.next(status)
|
||||||
|
break
|
||||||
|
|
||||||
|
case FileStatusPhase.FAILED:
|
||||||
|
this.documentConsumptionFailedSubject.next(status)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fail(status: FileStatus, message: string) {
|
fail(status: FileStatus, message: string) {
|
||||||
status.message = message
|
status.message = message
|
||||||
status.phase = FileStatusPhase.FAILED
|
status.phase = FileStatusPhase.FAILED
|
@ -24,8 +24,7 @@ from documents.models import Document
|
|||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.permissions import set_permissions_for_object
|
from documents.permissions import set_permissions_for_object
|
||||||
from documents.plugins.helpers import ProgressManager
|
from documents.plugins.helpers import DocumentsStatusManager
|
||||||
from documents.plugins.helpers import ProgressStatusOptions
|
|
||||||
from documents.tasks import bulk_update_documents
|
from documents.tasks import bulk_update_documents
|
||||||
from documents.tasks import consume_file
|
from documents.tasks import consume_file
|
||||||
from documents.tasks import update_document_content_maybe_archive_file
|
from documents.tasks import update_document_content_maybe_archive_file
|
||||||
@ -222,13 +221,8 @@ def delete(doc_ids: list[int]) -> Literal["OK"]:
|
|||||||
for id in doc_ids:
|
for id in doc_ids:
|
||||||
index.remove_document_by_id(writer, id)
|
index.remove_document_by_id(writer, id)
|
||||||
|
|
||||||
status_mgr = ProgressManager()
|
status_mgr = DocumentsStatusManager()
|
||||||
status_mgr.send_progress(
|
status_mgr.send_documents_deleted(doc_ids)
|
||||||
status=ProgressStatusOptions.DELETED,
|
|
||||||
message="Documents deleted",
|
|
||||||
current_progress=1,
|
|
||||||
max_progress=1,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "Data too long for column" in str(e):
|
if "Data too long for column" in str(e):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
@ -13,19 +13,16 @@ class ProgressStatusOptions(str, enum.Enum):
|
|||||||
WORKING = "WORKING"
|
WORKING = "WORKING"
|
||||||
SUCCESS = "SUCCESS"
|
SUCCESS = "SUCCESS"
|
||||||
FAILED = "FAILED"
|
FAILED = "FAILED"
|
||||||
DELETED = "DELETED"
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressManager:
|
class BaseStatusManager:
|
||||||
"""
|
"""
|
||||||
Handles sending of progress information via the channel layer, with proper management
|
Handles sending of progress information via the channel layer, with proper management
|
||||||
of the open/close of the layer to ensure messages go out and everything is cleaned up
|
of the open/close of the layer to ensure messages go out and everything is cleaned up
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, filename: str | None = None, task_id: str | None = None) -> None:
|
def __init__(self) -> None:
|
||||||
self.filename = filename
|
|
||||||
self._channel: RedisPubSubChannelLayer | None = None
|
self._channel: RedisPubSubChannelLayer | None = None
|
||||||
self.task_id = task_id
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.open()
|
self.open()
|
||||||
@ -50,6 +47,24 @@ class ProgressManager:
|
|||||||
async_to_sync(self._channel.flush)
|
async_to_sync(self._channel.flush)
|
||||||
self._channel = None
|
self._channel = None
|
||||||
|
|
||||||
|
def send(self, payload: dict[str, str | int | None]) -> None:
|
||||||
|
# Ensure the layer is open
|
||||||
|
self.open()
|
||||||
|
|
||||||
|
# Just for IDEs
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert self._channel is not None
|
||||||
|
|
||||||
|
# Construct and send the update
|
||||||
|
async_to_sync(self._channel.group_send)("status_updates", payload)
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressManager(BaseStatusManager):
|
||||||
|
def __init__(self, filename: str | None = None, task_id: str | None = None) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.filename = filename
|
||||||
|
self.task_id = task_id
|
||||||
|
|
||||||
def send_progress(
|
def send_progress(
|
||||||
self,
|
self,
|
||||||
status: ProgressStatusOptions,
|
status: ProgressStatusOptions,
|
||||||
@ -58,13 +73,6 @@ class ProgressManager:
|
|||||||
max_progress: int,
|
max_progress: int,
|
||||||
extra_args: dict[str, str | int | None] | None = None,
|
extra_args: dict[str, str | int | None] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Ensure the layer is open
|
|
||||||
self.open()
|
|
||||||
|
|
||||||
# Just for IDEs
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
assert self._channel is not None
|
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"type": "status_update",
|
"type": "status_update",
|
||||||
"data": {
|
"data": {
|
||||||
@ -79,5 +87,16 @@ class ProgressManager:
|
|||||||
if extra_args is not None:
|
if extra_args is not None:
|
||||||
payload["data"].update(extra_args)
|
payload["data"].update(extra_args)
|
||||||
|
|
||||||
# Construct and send the update
|
self.send(payload)
|
||||||
async_to_sync(self._channel.group_send)("status_updates", payload)
|
|
||||||
|
|
||||||
|
class DocumentsStatusManager(BaseStatusManager):
|
||||||
|
def send_documents_deleted(self, documents: list[int]) -> None:
|
||||||
|
payload = {
|
||||||
|
"type": "documents_deleted",
|
||||||
|
"data": {
|
||||||
|
"documents": documents,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send(payload)
|
||||||
|
@ -41,4 +41,10 @@ class StatusConsumer(WebsocketConsumer):
|
|||||||
self.close()
|
self.close()
|
||||||
else:
|
else:
|
||||||
if self._is_owner_or_unowned(event["data"]):
|
if self._is_owner_or_unowned(event["data"]):
|
||||||
self.send(json.dumps(event["data"]))
|
self.send(json.dumps(event))
|
||||||
|
|
||||||
|
def documents_deleted(self, event):
|
||||||
|
if not self._authenticated():
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
self.send(json.dumps(event))
|
||||||
|
@ -40,12 +40,12 @@ class TestWebSockets(TestCase):
|
|||||||
connected, subprotocol = await communicator.connect()
|
connected, subprotocol = await communicator.connect()
|
||||||
self.assertTrue(connected)
|
self.assertTrue(connected)
|
||||||
|
|
||||||
message = {"task_id": "test"}
|
message = {"type": "status_update", "data": {"task_id": "test"}}
|
||||||
|
|
||||||
channel_layer = get_channel_layer()
|
channel_layer = get_channel_layer()
|
||||||
await channel_layer.group_send(
|
await channel_layer.group_send(
|
||||||
"status_updates",
|
"status_updates",
|
||||||
{"type": "status_update", "data": message},
|
message,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = await communicator.receive_json_from()
|
response = await communicator.receive_json_from()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user