Skip to content

Commit 831c0db

Browse files
authored
BC-10862 - Add error handling for forbidden collabora access (#3974)
1 parent 4534b66 commit 831c0db

File tree

2 files changed

+118
-6
lines changed

2 files changed

+118
-6
lines changed

src/modules/page/collabora/Collabora.page.unit.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import CollaboraPage from "./Collabora.page.vue";
22
import * as serverApi from "@/serverApi/v3/api";
3+
import { HttpStatusCode } from "@/store/types/http-status-code.enum";
34
import { EditorMode } from "@/types/file/File";
45
import { buildPageTitle } from "@/utils/pageTitle";
56
import {
67
authorizedCollaboraDocumentUrlResponseFactory,
8+
axiosErrorFactory,
79
createTestAppStoreWithUser,
810
expectNotification,
911
fileElementResponseFactory,
@@ -415,6 +417,96 @@ describe("Collabora.page", () => {
415417
});
416418
});
417419

420+
describe("when getAuthorizedCollaboraDocumentUrl rejects with 403 Forbidden", () => {
421+
const setup = () => {
422+
const editorMode = EditorMode.EDIT;
423+
424+
createTestAppStoreWithUser("user-id");
425+
426+
const fileStorageApiMock = createMock<ReturnType<typeof FileStorageApi.useFileStorageApi>>();
427+
vi.spyOn(FileStorageApi, "useFileStorageApi").mockReturnValueOnce(fileStorageApiMock);
428+
const axiosError = axiosErrorFactory.withStatusCode(HttpStatusCode.Forbidden).build();
429+
fileStorageApiMock.getAuthorizedCollaboraDocumentUrl.mockRejectedValueOnce(axiosError);
430+
431+
const fileRecord = fileRecordFactory.build();
432+
fileStorageApiMock.getFileRecordById.mockReturnValueOnce(fileRecord);
433+
434+
const boardApi = createMock<serverApi.BoardElementApiInterface>();
435+
vi.spyOn(serverApi, "BoardElementApiFactory").mockReturnValueOnce(boardApi);
436+
437+
const expectedTitle = "standalone-file.pdf - Instance Title";
438+
mockBuildPageTitle.mockReturnValueOnce(expectedTitle);
439+
440+
mount(CollaboraPage, {
441+
global: {
442+
plugins: [createTestingVuetify(), createTestingI18n()],
443+
},
444+
propsData: {
445+
fileRecordId: fileRecord.id,
446+
editorMode,
447+
},
448+
});
449+
450+
return {
451+
fileRecord,
452+
expectedTitle,
453+
};
454+
};
455+
456+
it("should call handleApplicationError with correct parameters", async () => {
457+
setup();
458+
459+
await flushPromises();
460+
461+
expect(useAppStore().handleApplicationError).toHaveBeenCalledWith(HttpStatusCode.Forbidden);
462+
});
463+
});
464+
465+
describe("when getAuthorizedCollaboraDocumentUrl rejects with other error", () => {
466+
const setup = () => {
467+
const editorMode = EditorMode.EDIT;
468+
469+
createTestAppStoreWithUser("user-id");
470+
471+
const fileStorageApiMock = createMock<ReturnType<typeof FileStorageApi.useFileStorageApi>>();
472+
vi.spyOn(FileStorageApi, "useFileStorageApi").mockReturnValueOnce(fileStorageApiMock);
473+
const axiosError = axiosErrorFactory.withStatusCode(HttpStatusCode.InternalServerError).build();
474+
fileStorageApiMock.getAuthorizedCollaboraDocumentUrl.mockRejectedValueOnce(axiosError);
475+
476+
const fileRecord = fileRecordFactory.build();
477+
fileStorageApiMock.getFileRecordById.mockReturnValueOnce(fileRecord);
478+
479+
const boardApi = createMock<serverApi.BoardElementApiInterface>();
480+
vi.spyOn(serverApi, "BoardElementApiFactory").mockReturnValueOnce(boardApi);
481+
482+
const expectedTitle = "standalone-file.pdf - Instance Title";
483+
mockBuildPageTitle.mockReturnValueOnce(expectedTitle);
484+
485+
mount(CollaboraPage, {
486+
global: {
487+
plugins: [createTestingVuetify(), createTestingI18n()],
488+
},
489+
propsData: {
490+
fileRecordId: fileRecord.id,
491+
editorMode,
492+
},
493+
});
494+
495+
return {
496+
fileRecord,
497+
expectedTitle,
498+
};
499+
};
500+
501+
it("should call handleApplicationError with correct parameters", async () => {
502+
setup();
503+
504+
await flushPromises();
505+
506+
expect(useAppStore().handleApplicationError).toHaveBeenCalledWith(HttpStatusCode.InternalServerError);
507+
});
508+
});
509+
418510
describe("when getElementWithParentHierarchy rejects", () => {
419511
const setup = () => {
420512
const editorMode = EditorMode.EDIT;

src/modules/page/collabora/Collabora.page.vue

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
<script setup lang="ts">
1313
import { useCollaboraPostMessageApi } from "./CollaboraPostMessageApi.composable";
1414
import { EditorMode } from "@/types/file/File";
15+
import { mapAxiosErrorToResponseError } from "@/utils/api";
1516
import { buildPageTitle } from "@/utils/pageTitle";
16-
import { useAppStoreRefs } from "@data-app";
17+
import { useAppStore, useAppStoreRefs } from "@data-app";
1718
import { useBoardApi } from "@data-board";
1819
import { useFileStorageApi } from "@data-file";
1920
import { useTitle } from "@vueuse/core";
@@ -32,6 +33,7 @@ const { setupPostMessageAPI } = useCollaboraPostMessageApi();
3233
const { getElementWithParentHierarchyCall } = useBoardApi();
3334
3435
const { user, locale } = useAppStoreRefs();
36+
const { handleApplicationError } = useAppStore();
3537
3638
const userName = computed(() => {
3739
const firstName = user.value?.firstName;
@@ -55,11 +57,9 @@ onMounted(async () => {
5557
});
5658
5759
const setCollaboraUrl = async () => {
58-
const responseCollaboraUrl = await getAuthorizedCollaboraDocumentUrl(
59-
props.fileRecordId,
60-
props.editorMode ?? EditorMode.VIEW,
61-
userName.value
62-
);
60+
const responseCollaboraUrl = await tryGetCollaboraUrl();
61+
62+
if (!responseCollaboraUrl) return;
6363
6464
const collaboraUrl = new URL(responseCollaboraUrl);
6565
collaboraUrl.searchParams.set("lang", locale.value);
@@ -83,6 +83,26 @@ const setPageTitle = async () => {
8383
useTitle(pageTitle);
8484
};
8585
86+
const tryGetCollaboraUrl = async (): Promise<string | undefined> => {
87+
try {
88+
const collaboraUrl = await getAuthorizedCollaboraDocumentUrl(
89+
props.fileRecordId,
90+
props.editorMode ?? EditorMode.VIEW,
91+
userName.value
92+
);
93+
94+
return collaboraUrl;
95+
} catch (error) {
96+
handleError(error);
97+
}
98+
};
99+
100+
const handleError = (error: unknown) => {
101+
const responseError = mapAxiosErrorToResponseError(error);
102+
103+
handleApplicationError(responseError.code);
104+
};
105+
86106
const getFileRecord = async (fileId: string) => {
87107
let fileRecord = getFileRecordById(fileId);
88108

0 commit comments

Comments
 (0)