From 613a7429b80863c4886e98247cdfbead4145b66e Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Fri, 26 Jul 2024 12:22:53 +0300 Subject: [PATCH] RED-9747 add initial ws entity log refresh --- apps/red-ui/src/app/app.module.ts | 11 +-- .../src/app/guards/if-logged-in.guard.ts | 1 + .../file-preview-screen.component.ts | 68 +++++++++++++------ .../services/annotation-actions.service.ts | 17 +++-- .../services/file-data.service.ts | 56 +++++++++++++-- .../app/services/files/entity-log.service.ts | 17 ++++- .../src/app/services/web-socket.service.ts | 60 ++++++++++++++++ libs/red-domain/src/index.ts | 1 + .../src/lib/web-socket/analysis-event.ts | 16 +++++ libs/red-domain/src/lib/web-socket/index.ts | 2 + libs/red-domain/src/lib/web-socket/topics.ts | 3 + package.json | 7 +- yarn.lock | 18 +++-- 13 files changed, 230 insertions(+), 47 deletions(-) create mode 100644 apps/red-ui/src/app/services/web-socket.service.ts create mode 100644 libs/red-domain/src/lib/web-socket/analysis-event.ts create mode 100644 libs/red-domain/src/lib/web-socket/index.ts create mode 100644 libs/red-domain/src/lib/web-socket/topics.ts diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 52a8534ed..a65da1856 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -135,20 +135,20 @@ export const appModuleFactory = (config: AppConfig) => { features: { ANNOTATIONS: { color: 'aqua', - enabled: true, + enabled: false, level: NgxLoggerLevel.DEBUG, }, FILTERS: { enabled: false, }, TENANTS: { - enabled: true, + enabled: false, }, ROUTES: { - enabled: true, + enabled: false, }, PDF: { - enabled: true, + enabled: false, }, FILE: { enabled: false, @@ -171,6 +171,9 @@ export const appModuleFactory = (config: AppConfig) => { DOSSIERS_CHANGES: { enabled: false, }, + GUARDS: { + enabled: false, + }, }, } as ILoggerConfig, }, diff --git a/apps/red-ui/src/app/guards/if-logged-in.guard.ts b/apps/red-ui/src/app/guards/if-logged-in.guard.ts index caf15c448..52ac6bb1c 100644 --- a/apps/red-ui/src/app/guards/if-logged-in.guard.ts +++ b/apps/red-ui/src/app/guards/if-logged-in.guard.ts @@ -51,6 +51,7 @@ export function ifLoggedIn(): AsyncGuard { const jwtToken = jwtDecode(token) as JwtToken; const authTime = (jwtToken.auth_time || jwtToken.iat).toString(); localStorage.setItem('authTime', authTime); + localStorage.setItem('token', token); } } diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts index 565b97eec..14de0b2c1 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts @@ -1,6 +1,8 @@ -import { ActivatedRouteSnapshot, NavigationExtras, Router, RouterLink } from '@angular/router'; +import { NgIf } from '@angular/common'; import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { ActivatedRouteSnapshot, NavigationExtras, Router, RouterLink } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { InitialsAvatarComponent } from '@common-ui/users'; import { ComponentCanDeactivate } from '@guards/can-deactivate.guard'; import { CircleButtonComponent, @@ -18,10 +20,11 @@ import { Toaster, } from '@iqser/common-ui'; import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering'; -import { AutoUnsubscribe, Bind, bool, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils'; +import { AutoUnsubscribe, Bind, bool, List, log, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { ManualRedactionEntryTypes, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; -import { Dictionary, File, ViewModes } from '@red/domain'; +import { TranslateModule } from '@ngx-translate/core'; +import { AnalyseStatuses, AnalysisEvent, Dictionary, File, ViewModes, WsTopics } from '@red/domain'; import { ConfigService } from '@services/config.service'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { DossiersService } from '@services/dossiers/dossiers.service'; @@ -29,10 +32,13 @@ import { FilesMapService } from '@services/files/files-map.service'; import { FilesService } from '@services/files/files.service'; import { PermissionsService } from '@services/permissions.service'; import { ReanalysisService } from '@services/reanalysis.service'; +import { WebSocketService } from '@services/web-socket.service'; +import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component'; +import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component'; import { Roles } from '@users/roles'; import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service'; import { NGXLogger } from 'ngx-logger'; -import { combineLatest, first, firstValueFrom, Observable, of, pairwise } from 'rxjs'; +import { combineLatest, first, firstValueFrom, Observable, of, pairwise, Subscription } from 'rxjs'; import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators'; import { byId, byPage, handleFilterDelta, hasChanges } from '../../utils'; import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service'; @@ -43,34 +49,29 @@ import { PdfViewer } from '../pdf-viewer/services/pdf-viewer.service'; import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service'; import { ViewerHeaderService } from '../pdf-viewer/services/viewer-header.service'; import { ROTATION_ACTION_BUTTONS, ViewerEvents } from '../pdf-viewer/utils/constants'; +import { FileActionsComponent } from '../shared-dossiers/components/file-actions/file-actions.component'; +import { FileHeaderComponent } from './components/file-header/file-header.component'; +import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component'; +import { StructuredComponentManagementComponent } from './components/structured-component-management/structured-component-management.component'; +import { UserManagementComponent } from './components/user-management/user-management.component'; +import { ViewSwitchComponent } from './components/view-switch/view-switch.component'; import { AddHintDialogComponent } from './dialogs/add-hint-dialog/add-hint-dialog.component'; import { AddAnnotationDialogComponent } from './dialogs/docu-mine/add-annotation-dialog/add-annotation-dialog.component'; import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component'; import { filePreviewScreenProviders } from './file-preview-providers'; import { AnnotationProcessingService } from './services/annotation-processing.service'; import { AnnotationsListingService } from './services/annotations-listing.service'; +import { DocumentInfoService } from './services/document-info.service'; import { FileDataService } from './services/file-data.service'; import { FilePreviewDialogService } from './services/file-preview-dialog.service'; import { FilePreviewStateService } from './services/file-preview-state.service'; import { ManualRedactionService } from './services/manual-redaction.service'; +import { MultiSelectService } from './services/multi-select.service'; import { PdfProxyService } from './services/pdf-proxy.service'; import { SkippedService } from './services/skipped.service'; import { StampService } from './services/stamp.service'; import { ViewModeService } from './services/view-mode.service'; import { RedactTextData } from './utils/dialog-types'; -import { MultiSelectService } from './services/multi-select.service'; -import { NgIf } from '@angular/common'; -import { ViewSwitchComponent } from './components/view-switch/view-switch.component'; -import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component'; -import { UserManagementComponent } from './components/user-management/user-management.component'; -import { TranslateModule } from '@ngx-translate/core'; -import { InitialsAvatarComponent } from '@common-ui/users'; -import { FileActionsComponent } from '../shared-dossiers/components/file-actions/file-actions.component'; -import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component'; -import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component'; -import { FileHeaderComponent } from './components/file-header/file-header.component'; -import { StructuredComponentManagementComponent } from './components/structured-component-management/structured-component-management.component'; -import { DocumentInfoService } from './services/document-info.service'; @Component({ templateUrl: './file-preview-screen.component.html', @@ -96,17 +97,19 @@ import { DocumentInfoService } from './services/document-info.service'; ], }) export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate { - readonly circleButtonTypes = CircleButtonTypes; - readonly roles = Roles; - readonly fileId = this.state.fileId; - readonly dossierId = this.state.dossierId; @ViewChild('annotationFilterTemplate', { read: TemplateRef, static: false, }) private readonly _filterTemplate: TemplateRef; #loadAllAnnotationsEnabled = false; + readonly #wsConnection$: Observable; + #wsConnectionSub: Subscription; protected readonly isDocumine = getConfig().IS_DOCUMINE; + readonly circleButtonTypes = CircleButtonTypes; + readonly roles = Roles; + readonly fileId = this.state.fileId; + readonly dossierId = this.state.dossierId; constructor( readonly pdf: PdfViewer, @@ -145,13 +148,31 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _multiSelectService: MultiSelectService, private readonly _documentInfoService: DocumentInfoService, + private readonly _webSocketService: WebSocketService, ) { super(); effect(() => { const file = this.state.file(); - this._fileDataService.loadAnnotations(file).then(); + console.log('FILE CHANGED'); + // this._fileDataService.loadAnnotations(file).then(); }); + this.#wsConnection$ = this._webSocketService.listen(WsTopics.ANALYSIS).pipe( + log('[WS] Analysis events'), + filter(event => event.analyseStatus === AnalyseStatuses.FINISHED), + switchMap(event => this._fileDataService.updateAnnotations(this.state.file(), event.analysisNumber)), + log('[CONNNEECCCCCTIIONSSS] Annotations updated'), + ); + + const file = this.state.file(); + console.log(file); + console.log(this._fileDataService.annotations()); + if (this._fileDataService.annotations().length) { + firstValueFrom(this._fileDataService.updateAnnotations(file, file.numberOfAnalyses)).then(); + } else { + this._fileDataService.loadAnnotations(file).then(); + } + effect( () => { if (this._documentViewer.loaded()) { @@ -296,11 +317,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni super.ngOnDetach(); this.pdf.instance.UI.hotkeys.off('esc'); this._changeRef.markForCheck(); + this.#wsConnectionSub.unsubscribe(); } ngOnDestroy() { this.pdf.instance.UI.hotkeys.off('esc'); super.ngOnDestroy(); + this.#wsConnectionSub.unsubscribe(); } @Bind() @@ -339,6 +362,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } async ngOnInit(): Promise { + this.#wsConnectionSub = this.#wsConnection$.subscribe(); this.#updateViewerPosition(); const file = this.state.file(); diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts index 7673762e6..2568abb13 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts @@ -244,7 +244,6 @@ export class AnnotationActionsService { const text = annotation.AREA ? annotation.value : isImageText; const isApprover = this._permissionsService.isApprover(dossier); const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId); - const isUnprocessed = annotation.pending; const data: ResizeRedactionData = { redaction: annotation, @@ -275,7 +274,7 @@ export class AnnotationActionsService { await this.cancelResize(annotation); - const { fileId, dossierId, file } = this._state; + const { fileId, dossierId } = this._state; const request = this._manualRedactionService.resize([resizeRequest], dossierId, fileId, includeUnprocessed); return this.#processObsAndEmit(request); } @@ -321,7 +320,7 @@ export class AnnotationActionsService { } async #processObsAndEmit(obs: Observable) { - await firstValueFrom(obs).finally(() => this._fileDataService.annotationsChanged()); + await firstValueFrom(obs.pipe(log('==>>[[[CHANGES]]]'))).finally(() => this._fileDataService.annotationsChanged()); } #getFalsePositiveText(annotation: AnnotationWrapper) { @@ -443,9 +442,15 @@ export class AnnotationActionsService { // todo: might not be correct, probably shouldn't get to this point if they are not all the same const isHint = redactions.every(r => r.isHint); const { dossierId, fileId } = this._state; - this.#processObsAndEmit( - this._manualRedactionService.removeRedaction(body, dossierId, fileId, removeFromDictionary, isHint, includeUnprocessed), - ).then(); + const req$ = this._manualRedactionService.removeRedaction( + body, + dossierId, + fileId, + removeFromDictionary, + isHint, + includeUnprocessed, + ); + this.#processObsAndEmit(req$).then(() => this._fileDataService.removeAnnotations(redactions.map(r => r.id))); } #getRemoveRedactionDialog(data: RemoveRedactionData) { diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts index 0906fb42d..12d75122b 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts @@ -1,6 +1,8 @@ -import { effect, inject, Injectable, Signal, signal } from '@angular/core'; +import { effect, inject, Injectable, Signal, signal, WritableSignal } from '@angular/core'; import { toObservable } from '@angular/core/rxjs-interop'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { TenantsService } from '@common-ui/tenants'; +import { log } from '@common-ui/utils'; import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { @@ -27,6 +29,7 @@ import { UserPreferenceService } from '@users/user-preference.service'; import dayjs from 'dayjs'; import { NGXLogger } from 'ngx-logger'; import { firstValueFrom, Observable } from 'rxjs'; +import { switchMap, tap } from 'rxjs/operators'; import { FilePreviewStateService } from './file-preview-state.service'; import { MultiSelectService } from './multi-select.service'; import { ViewModeService } from './view-mode.service'; @@ -43,13 +46,14 @@ export function chronologicallyBy(property: (x: T) => string) { @Injectable() export class FileDataService extends EntitiesService { - readonly #annotations = signal([]); + readonly #annotations: WritableSignal; readonly #earmarks = signal>(new Map()); #originalViewedPages: ViewedPage[] = []; readonly #isDocumine = getConfig().IS_DOCUMINE; readonly #logger = inject(NGXLogger); readonly #toaster = inject(Toaster); readonly #isIqserDevMode = inject(UserPreferenceService).isIqserDevMode; + readonly #tenantsService = inject(TenantsService); protected readonly _entityClass = AnnotationWrapper; missingTypes = new Set(); readonly earmarks: Signal>; @@ -70,9 +74,17 @@ export class FileDataService extends EntitiesService(JSON.parse(localStorage.getItem(localStorageKey) || '[]')); this.annotations$ = toObservable(this.#annotations); this.annotations = this.#annotations.asReadonly(); this.earmarks = this.#earmarks.asReadonly(); + + effect(() => { + localStorage.setItem(localStorageKey, JSON.stringify(this.#annotations())); + console.log('FileDataService#annotations', this.#annotations()); + }); + effect(() => { const viewMode = this._viewModeService.viewMode(); const earmarks = ([] as AnnotationWrapper[]).concat(...this.#earmarks().values()); @@ -81,6 +93,10 @@ export class FileDataService extends EntitiesService old.filter(annotation => !id.includes(annotation.id))); + } + setEntities(entities: AnnotationWrapper[]): void { // this is a light version of setEntities to skip looping too much // used mostly for earmarks (which are usually a lot) @@ -119,7 +135,8 @@ export class FileDataService extends EntitiesService { + const notUpdated = old.filter(oldAnnotation => { + return !annotations.some(newAnnotation => newAnnotation.id === oldAnnotation.id); + }); + return [...notUpdated, ...annotations].sort((a, b) => a.positions[0].page - b.positions[0].page); + }); + } + + async processEntityLog(entityLog: IEntityLog) { + let annotations = await this.#convertData(entityLog); this.#checkMissingTypes(); - annotations = this.#isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive); - this.#annotations.set(annotations); + + return this.#isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive); + } + + updateAnnotations(file: File, analysisNumber: number) { + const delta$ = this._entityLogService.getDelta(file.dossierId, file.fileId, analysisNumber); + return delta$.pipe( + log('[REDACTION_LOG] Delta loaded'), + switchMap(delta => this.processEntityLog(delta)), + tap(annotations => { + this.#annotations.update(old => { + const notUpdated = old.filter(oldAnnotation => { + return !oldAnnotation.pending && !annotations.some(newAnnotation => newAnnotation.id === oldAnnotation.id); + }); + return [...notUpdated, ...annotations].sort((a, b) => a.positions[0].page - b.positions[0].page); + }); + }), + tap(() => this.#logger.info('[REDACTION_LOG] Annotations updated', this.#annotations())), + ); } #checkMissingTypes() { diff --git a/apps/red-ui/src/app/services/files/entity-log.service.ts b/apps/red-ui/src/app/services/files/entity-log.service.ts index dbc05ea4e..91339fd68 100644 --- a/apps/red-ui/src/app/services/files/entity-log.service.ts +++ b/apps/red-ui/src/app/services/files/entity-log.service.ts @@ -1,15 +1,15 @@ import { inject, Injectable } from '@angular/core'; -import { GenericService, isIqserDevMode, Toaster } from '@iqser/common-ui'; +import { GenericService, Toaster } from '@iqser/common-ui'; import { EntryStates, IEntityLog, IEntityLogEntry, ISectionGrid } from '@red/domain'; import { firstValueFrom, of } from 'rxjs'; -import { catchError } from 'rxjs/operators'; +import { catchError, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root', }) export class EntityLogService extends GenericService { - protected readonly _defaultModelPath = ''; readonly #toaster = inject(Toaster); + protected readonly _defaultModelPath = ''; async getEntityLog(dossierId: string, fileId: string) { const queryParams = [{ key: 'includeUnprocessed', value: true }]; @@ -20,6 +20,17 @@ export class EntityLogService extends GenericService { return entityLog; } + getDelta(dossierId: string, fileId: string, analysisNumber: number) { + const req$ = this._getOne([dossierId, fileId, analysisNumber.toString()], 'entityLog'); + return req$.pipe( + map(entityLog => { + entityLog.entityLogEntry = this.#filterInvalidEntries(entityLog.entityLogEntry); + return entityLog; + }), + catchError(() => of({} as IEntityLog)), + ); + } + getSectionGrid(dossierId: string, fileId: string) { return this._getOne([dossierId, fileId], 'sectionGrid'); } diff --git a/apps/red-ui/src/app/services/web-socket.service.ts b/apps/red-ui/src/app/services/web-socket.service.ts new file mode 100644 index 000000000..feb2a015d --- /dev/null +++ b/apps/red-ui/src/app/services/web-socket.service.ts @@ -0,0 +1,60 @@ +import { inject, Injectable } from '@angular/core'; +import { TenantsService } from '@common-ui/tenants'; +import { log } from '@common-ui/utils'; +import { getConfig } from '@iqser/common-ui'; +import { IMessage, IWatchParams, RxStomp } from '@stomp/rx-stomp'; +import { StompHeaders } from '@stomp/stompjs'; + +import { NGXLogger } from 'ngx-logger'; +import { Observable } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root', +}) +export class WebSocketService extends RxStomp { + readonly #logger = inject(NGXLogger); + readonly #config = getConfig(); + readonly #tenantService = inject(TenantsService); + + constructor() { + super(); + setTimeout(() => this.connect(), 1000); + } + + watch(opts: IWatchParams): Observable; + watch(destination: string, headers?: StompHeaders): Observable; + watch(opts: string | IWatchParams, headers?: StompHeaders): Observable { + if (typeof opts === 'string') { + return super.watch('/topic/' + this.#tenantService.activeTenantId + '/' + opts, headers); + } + + return super.watch(opts); + } + + listen(topic: string): Observable { + return this.watch(topic).pipe(map(msg => JSON.parse(msg.body))); + } + + private connect() { + const headers = { Authorization: 'Bearer ' + localStorage.getItem('token') }; + this.configure({ + debug: (msg: string) => this.#logger.debug(msg), + brokerURL: this.#config.API_URL + '/redaction-gateway-v1/websocket', + connectHeaders: headers, + }); + this.connectionState$.pipe(log('[WS] Connection state')).subscribe(); + this.webSocketErrors$.pipe(log('[WS] Errors')).subscribe(); + this.stompErrors$ + .pipe( + tap(frame => { + console.error(frame); + console.error('Broker reported error: ' + frame.headers['message']); + console.error('Additional details: ' + frame.body); + }), + ) + .subscribe(); + + this.activate(); + } +} diff --git a/libs/red-domain/src/index.ts b/libs/red-domain/src/index.ts index 34cacab3f..5459a3e63 100644 --- a/libs/red-domain/src/index.ts +++ b/libs/red-domain/src/index.ts @@ -31,3 +31,4 @@ export * from './lib/colors'; export * from './lib/component-log'; export * from './lib/component-mappings'; export * from './lib/component-definitions'; +export * from './lib/web-socket'; diff --git a/libs/red-domain/src/lib/web-socket/analysis-event.ts b/libs/red-domain/src/lib/web-socket/analysis-event.ts new file mode 100644 index 000000000..c60f416a9 --- /dev/null +++ b/libs/red-domain/src/lib/web-socket/analysis-event.ts @@ -0,0 +1,16 @@ +export const AnalyseStatuses = { + PROCESSING: 'PROCESSING', + FINISHED: 'FINISHED', +} as const; + +export type AnalyseStatus = keyof typeof AnalyseStatuses; + +export interface AnalysisEvent { + analyseStatus: AnalyseStatus; + analysisNumber: number; + dossierId: string; + fileId: string; + numberOfOCRedPages: number; + numberOfPagesToOCR: number; + timestamp: string; +} diff --git a/libs/red-domain/src/lib/web-socket/index.ts b/libs/red-domain/src/lib/web-socket/index.ts new file mode 100644 index 000000000..6a0f0ec49 --- /dev/null +++ b/libs/red-domain/src/lib/web-socket/index.ts @@ -0,0 +1,2 @@ +export * from './analysis-event'; +export * from './topics'; diff --git a/libs/red-domain/src/lib/web-socket/topics.ts b/libs/red-domain/src/lib/web-socket/topics.ts new file mode 100644 index 000000000..ed9721867 --- /dev/null +++ b/libs/red-domain/src/lib/web-socket/topics.ts @@ -0,0 +1,3 @@ +export const WsTopics = { + ANALYSIS: 'analysis-events', +} as const; diff --git a/package.json b/package.json index 1ffc8b852..f2cadfa69 100644 --- a/package.json +++ b/package.json @@ -35,12 +35,14 @@ "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", "@pdftron/webviewer": "10.10.1", + "@stomp/rx-stomp": "^2.0.0", + "@stomp/stompjs": "^7.0.0", "chart.js": "4.4.3", "dayjs": "1.11.11", "file-saver": "^2.0.5", "jszip": "^3.10.1", "jwt-decode": "^4.0.0", - "keycloak-angular": "15.1.0", + "keycloak-angular": "16.0.1", "keycloak-js": "23.0.1", "lodash-es": "^4.17.21", "monaco-editor": "0.49.0", @@ -100,5 +102,6 @@ "webpack": "5.92.0", "webpack-bundle-analyzer": "4.10.2", "xliff": "^6.2.1" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/yarn.lock b/yarn.lock index 00c5c6c4a..f7e8bddd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3396,6 +3396,16 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@stomp/rx-stomp@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@stomp/rx-stomp/-/rx-stomp-2.0.0.tgz#5d75c87db280d2af9da7fccd3478c682df312065" + integrity sha512-3UxTxAA3NWGnwFfIvN8AigJ7BxGXG0u5IK8K12mQ9cCMuaT/MM7xlyZnuV8sDbHiqqLlbwA1wk1fDfUyOTIeug== + +"@stomp/stompjs@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@stomp/stompjs/-/stompjs-7.0.0.tgz#46b5c454a9dc8262e0b20f3b3dbacaa113993077" + integrity sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw== + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -7442,10 +7452,10 @@ karma-source-map-support@1.4.0: dependencies: source-map-support "^0.5.5" -keycloak-angular@15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/keycloak-angular/-/keycloak-angular-15.1.0.tgz#56d25025ace2596ea8265e7158b66b2fb20054d1" - integrity sha512-9Wz1jEalUXeq3v88MMYEcFnF2GwUht1slMbDau8lpNEe0Wp9xcv5/NpMUP0RjsHKmNg8cX47BUsxL27Ypy7pmA== +keycloak-angular@16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/keycloak-angular/-/keycloak-angular-16.0.1.tgz#9dd30e36d5320db35cf1bdb681be5552ba1104ce" + integrity sha512-ytkL32R/tfHEyZ3txQtgH1y0WofW/D36zTbo2agDCYUtZETq0wAQ3E/4bVDUAr6ZKwotgAnIyOORfErnvDkXng== dependencies: tslib "^2.3.1"