diff --git a/src/app/core/database/pouch-database.ts b/src/app/core/database/pouch-database.ts index 325a799c08..f7cf401540 100644 --- a/src/app/core/database/pouch-database.ts +++ b/src/app/core/database/pouch-database.ts @@ -387,7 +387,9 @@ export class PouchDatabase extends Database { newObject._rev = existingObject._rev; return this.put(newObject); } else { - existingError.message = existingError.message + " (unable to resolve)"; + existingError.message = `${ + existingError.message + } (unable to resolve) ID: ${JSON.stringify(newObject)}`; throw new DatabaseException(existingError); } } diff --git a/src/app/core/support/support/support.component.html b/src/app/core/support/support/support.component.html index 00ed333a2e..6d2e14d6cb 100644 --- a/src/app/core/support/support/support.component.html +++ b/src/app/core/support/support/support.component.html @@ -41,6 +41,10 @@

Technical User Support Details

Storage usage {{ storageInfo || 'No information' }} + + Database documents + {{ dbInfo }} + Service Worker {{ swStatus }} diff --git a/src/app/core/support/support/support.component.spec.ts b/src/app/core/support/support/support.component.spec.ts index e0d159c081..191c4c732f 100644 --- a/src/app/core/support/support/support.component.spec.ts +++ b/src/app/core/support/support/support.component.spec.ts @@ -3,6 +3,7 @@ import { fakeAsync, TestBed, tick, + waitForAsync, } from "@angular/core/testing"; import { SupportComponent } from "./support.component"; @@ -10,7 +11,6 @@ import { SessionService } from "../../session/session-service/session.service"; import { BehaviorSubject, of } from "rxjs"; import { SyncState } from "../../session/session-states/sync-state.enum"; import { SwUpdate } from "@angular/service-worker"; -import { Database } from "../../database/database"; import { LOCATION_TOKEN, WINDOW_TOKEN } from "../../../utils/di-tokens"; import { TEST_USER } from "../../../utils/mocked-testing.module"; import { RemoteSession } from "../../session/session-service/remote-session"; @@ -20,6 +20,7 @@ import { SyncedSessionService } from "../../session/session-service/synced-sessi import { MatDialogModule } from "@angular/material/dialog"; import { HttpClientTestingModule } from "@angular/common/http/testing"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { PouchDatabase } from "../../database/pouch-database"; describe("SupportComponent", () => { let component: SupportComponent; @@ -27,7 +28,7 @@ describe("SupportComponent", () => { const testUser = { name: TEST_USER, roles: [] }; let mockSessionService: jasmine.SpyObj; const mockSW = { isEnabled: false }; - let mockDB: jasmine.SpyObj; + let mockDB: jasmine.SpyObj; const mockWindow = { navigator: { userAgent: "mock user agent", @@ -42,7 +43,10 @@ describe("SupportComponent", () => { syncState: new BehaviorSubject(SyncState.UNSYNCED), }); mockSessionService.getCurrentUser.and.returnValue(testUser); - mockDB = jasmine.createSpyObj(["destroy"]); + mockDB = jasmine.createSpyObj(["destroy", "getPouchDB"]); + mockDB.getPouchDB.and.returnValue({ + info: () => Promise.resolve({ doc_count: 1, update_seq: 2 }), + } as any); mockLocation = jasmine.createSpyObj(["reload"]); await TestBed.configureTestingModule({ imports: [ @@ -54,18 +58,18 @@ describe("SupportComponent", () => { providers: [ { provide: SessionService, useValue: mockSessionService }, { provide: SwUpdate, useValue: mockSW }, - { provide: Database, useValue: mockDB }, + { provide: PouchDatabase, useValue: mockDB }, { provide: WINDOW_TOKEN, useValue: mockWindow }, { provide: LOCATION_TOKEN, useValue: mockLocation }, ], }).compileComponents(); }); - beforeEach(() => { + beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(SupportComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); + })); it("should create", () => { expect(component).toBeTruthy(); @@ -78,15 +82,16 @@ describe("SupportComponent", () => { expect(component.lastRemoteLogin).toBe("never"); expect(component.swStatus).toBe("not enabled"); expect(component.userAgent).toBe("mock user agent"); + expect(component.dbInfo).toBe("1 (update sequence 2)"); }); - it("should correctly read sync and remote login status from local storage", () => { + it("should correctly read sync and remote login status from local storage", async () => { const lastSync = new Date("2022-01-01").toISOString(); localStorage.setItem(SyncedSessionService.LAST_SYNC_KEY, lastSync); const lastRemoteLogin = new Date("2022-01-02").toISOString(); localStorage.setItem(RemoteSession.LAST_LOGIN_KEY, lastRemoteLogin); - component.ngOnInit(); + await component.ngOnInit(); expect(component.lastSync).toBe(lastSync); expect(component.lastRemoteLogin).toBe(lastRemoteLogin); diff --git a/src/app/core/support/support/support.component.ts b/src/app/core/support/support/support.component.ts index 2e6765e541..0682b9045c 100644 --- a/src/app/core/support/support/support.component.ts +++ b/src/app/core/support/support/support.component.ts @@ -3,7 +3,6 @@ import { SessionService } from "../../session/session-service/session.service"; import { LOCATION_TOKEN, WINDOW_TOKEN } from "../../../utils/di-tokens"; import { SyncState } from "../../session/session-states/sync-state.enum"; import { SwUpdate } from "@angular/service-worker"; -import { Database } from "../../database/database"; import * as Sentry from "@sentry/browser"; import { RemoteSession } from "../../session/session-service/remote-session"; import { ConfirmationDialogService } from "../../confirmation-dialog/confirmation-dialog.service"; @@ -14,16 +13,14 @@ import { AuthUser } from "../../session/session-service/auth-user"; import { firstValueFrom } from "rxjs"; import { MatExpansionModule } from "@angular/material/expansion"; import { MatButtonModule } from "@angular/material/button"; +import { PouchDatabase } from "../../database/pouch-database"; @Component({ selector: "app-support", templateUrl: "./support.component.html", styleUrls: ["./support.component.scss"], - imports: [ - MatExpansionModule, - MatButtonModule - ], - standalone: true + imports: [MatExpansionModule, MatButtonModule], + standalone: true, }) export class SupportComponent implements OnInit { currentUser: AuthUser; @@ -35,18 +32,19 @@ export class SupportComponent implements OnInit { swLog = "not available"; userAgent = this.window.navigator.userAgent; appVersion: string; + dbInfo: string; constructor( private sessionService: SessionService, private sw: SwUpdate, - private database: Database, + private database: PouchDatabase, private confirmationDialog: ConfirmationDialogService, private http: HttpClient, @Inject(WINDOW_TOKEN) private window: Window, @Inject(LOCATION_TOKEN) private location: Location ) {} - ngOnInit(): void { + ngOnInit() { this.currentUser = this.sessionService.getCurrentUser(); this.appVersion = environment.appVersion; this.initCurrentSyncState(); @@ -54,6 +52,7 @@ export class SupportComponent implements OnInit { this.initLastRemoteLogin(); this.initStorageInfo(); this.initSwStatus(); + return this.initDbInfo(); } private initCurrentSyncState() { @@ -103,6 +102,16 @@ export class SupportComponent implements OnInit { .then((res) => (this.swLog = res)); } + private initDbInfo() { + return this.database + .getPouchDB() + .info() + .then( + (res) => + (this.dbInfo = `${res.doc_count} (update sequence ${res.update_seq})`) + ); + } + sendReport() { // This is sent even without submitting the crash report. Sentry.captureMessage("report information", { @@ -116,6 +125,7 @@ export class SupportComponent implements OnInit { userAgent: this.userAgent, swLog: this.swLog, storageInfo: this.storageInfo, + dbInfo: this.dbInfo, }, }); Sentry.showReportDialog({