From 6977112a36c160fb7827641d7b7fbf6dcd97b8c8 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Tue, 23 Jan 2024 16:27:44 +0100 Subject: [PATCH 01/10] datasheet icon, server call and generate api --- .../administration/ExternalToolSection.vue | 6 ++ .../administration/ExternalToolToolbar.vue | 21 ++++- src/locales/de.json | 1 + src/serverApi/v3/api.ts | 81 +++++++++++++++++++ src/store/school-external-tools.ts | 27 +++++++ 5 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/components/administration/ExternalToolSection.vue b/src/components/administration/ExternalToolSection.vue index ec6ad50f13..65371c2476 100644 --- a/src/components/administration/ExternalToolSection.vue +++ b/src/components/administration/ExternalToolSection.vue @@ -34,6 +34,7 @@ @@ -180,6 +181,10 @@ export default defineComponent({ }); }; + const createDatasheet = async (item: SchoolExternalToolItem) => { + await schoolExternalToolsModule.createDatasheet(item.id); + }; + const onDeleteTool = async () => { if (itemToDelete.value) { await schoolExternalToolsModule.deleteSchoolExternalTool( @@ -232,6 +237,7 @@ export default defineComponent({ isLoading, editTool, onDeleteTool, + createDatasheet, isDeleteDialogOpen, openDeleteDialog, onCloseDeleteDialog, diff --git a/src/components/administration/ExternalToolToolbar.vue b/src/components/administration/ExternalToolToolbar.vue index 05728565bb..bf3a35693a 100644 --- a/src/components/administration/ExternalToolToolbar.vue +++ b/src/components/administration/ExternalToolToolbar.vue @@ -12,6 +12,18 @@ {{ mdiPencilOutline }} + + {{ mdiFileDocumentOutline }} + import { defineComponent } from "vue"; -import { mdiPencilOutline, mdiTrashCanOutline } from "@mdi/js"; +import { + mdiFileDocumentOutline, + mdiPencilOutline, + mdiTrashCanOutline, +} from "@mdi/js"; export default defineComponent({ name: "ExternalToolToolbar", - emits: ["delete", "edit"], + emits: ["delete", "edit", "datasheet"], setup() { return { mdiPencilOutline, mdiTrashCanOutline, + mdiFileDocumentOutline, }; }, }); diff --git a/src/locales/de.json b/src/locales/de.json index ba0d2f8cca..4049de0d45 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -180,6 +180,7 @@ "components.administration.externalToolsSection.description": "Die schulspezifischen Parameter für das externe Tool werden hier konfiguriert. Nach dem Speichern der Konfiguration ist das Tool innerhalb der Schule verfügbar.

\nWeitere Informationen sind in unserem Hilfebereich zu externen Tools zu finden.", "components.administration.externalToolsSection.table.header.status": "Status", "components.administration.externalToolsSection.action.add": "Externes Tool hinzufügen", + "components.administration.externalToolsSection.action.createDatasheet": "Datenblatt erstellen", "components.administration.externalToolsSection.action.edit": "Tool bearbeiten", "components.administration.externalToolsSection.action.delete": "Tool löschen", "components.administration.externalToolsSection.dialog.title": "Externes Tool entfernen?", diff --git a/src/serverApi/v3/api.ts b/src/serverApi/v3/api.ts index 26b8fb62e2..3d16c7233e 100644 --- a/src/serverApi/v3/api.ts +++ b/src/serverApi/v3/api.ts @@ -15026,6 +15026,44 @@ export const ToolApiAxiosParamCreator = function (configuration?: Configuration) + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Returns a pdf of the external tool information + * @param {string} externalToolId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + toolControllerGetDatasheet: async (externalToolId: string, options: any = {}): Promise => { + // verify required parameter 'externalToolId' is not null or undefined + assertParamExists('toolControllerGetDatasheet', 'externalToolId', externalToolId) + const localVarPath = `/tools/external-tools/{externalToolId}/datasheet` + .replace(`{${"externalToolId"}}`, encodeURIComponent(String(externalToolId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + setSearchParams(localVarUrlObj, localVarQueryParameter, options.query); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -15710,6 +15748,17 @@ export const ToolApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.toolControllerFindExternalTool(name, clientId, skip, limit, sortOrder, sortBy, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary Returns a pdf of the external tool information + * @param {string} externalToolId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async toolControllerGetDatasheet(externalToolId: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.toolControllerGetDatasheet(externalToolId, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @summary Returns an ExternalTool for the given id @@ -16003,6 +16052,16 @@ export const ToolApiFactory = function (configuration?: Configuration, basePath? toolControllerFindExternalTool(name?: string, clientId?: string, skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'id' | 'name', options?: any): AxiosPromise { return localVarFp.toolControllerFindExternalTool(name, clientId, skip, limit, sortOrder, sortBy, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary Returns a pdf of the external tool information + * @param {string} externalToolId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + toolControllerGetDatasheet(externalToolId: string, options?: any): AxiosPromise { + return localVarFp.toolControllerGetDatasheet(externalToolId, options).then((request) => request(axios, basePath)); + }, /** * * @summary Returns an ExternalTool for the given id @@ -16282,6 +16341,16 @@ export interface ToolApiInterface { */ toolControllerFindExternalTool(name?: string, clientId?: string, skip?: number, limit?: number, sortOrder?: 'asc' | 'desc', sortBy?: 'id' | 'name', options?: any): AxiosPromise; + /** + * + * @summary Returns a pdf of the external tool information + * @param {string} externalToolId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ToolApiInterface + */ + toolControllerGetDatasheet(externalToolId: string, options?: any): AxiosPromise; + /** * * @summary Returns an ExternalTool for the given id @@ -16587,6 +16656,18 @@ export class ToolApi extends BaseAPI implements ToolApiInterface { return ToolApiFp(this.configuration).toolControllerFindExternalTool(name, clientId, skip, limit, sortOrder, sortBy, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary Returns a pdf of the external tool information + * @param {string} externalToolId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ToolApi + */ + public toolControllerGetDatasheet(externalToolId: string, options?: any) { + return ToolApiFp(this.configuration).toolControllerGetDatasheet(externalToolId, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary Returns an ExternalTool for the given id diff --git a/src/store/school-external-tools.ts b/src/store/school-external-tools.ts index 0643f1a946..4772357965 100644 --- a/src/store/school-external-tools.ts +++ b/src/store/school-external-tools.ts @@ -297,4 +297,31 @@ export default class SchoolExternalToolsModule extends VuexModule { this.setLoading(false); } + + @Action + async createDatasheet(schoolExternalToolId: string): Promise { + this.setLoading(true); + this.resetBusinessError(); + + try { + const schoolExternalTool: SchoolExternalTool | undefined = + await this.loadSchoolExternalTool(schoolExternalToolId); + + if (schoolExternalTool) { + await this.toolApi.toolControllerGetDatasheet( + schoolExternalTool.toolId + ); + } + } catch (error: unknown) { + const apiError = mapAxiosErrorToResponseError(error); + + this.setBusinessError({ + error: apiError, + statusCode: apiError.code, + message: apiError.message, + }); + } + + this.setLoading(false); + } } From fd3c2fbb483504757ba554a7946fcca29821d9d0 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 24 Jan 2024 12:30:43 +0100 Subject: [PATCH 02/10] unit tests --- .../ExternalToolSection.unit.ts | 3 + .../administration/ExternalToolSection.vue | 2 +- .../ExternalToolToolbar.unit.ts | 12 +++- .../administration/ExternalToolToolbar.vue | 2 +- src/store/school-external-tools.unit.ts | 62 +++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/components/administration/ExternalToolSection.unit.ts b/src/components/administration/ExternalToolSection.unit.ts index 5761e0ac91..d815e0a196 100644 --- a/src/components/administration/ExternalToolSection.unit.ts +++ b/src/components/administration/ExternalToolSection.unit.ts @@ -231,6 +231,9 @@ describe("ExternalToolSection", () => { expect( firstRowButtons.at(1).classes().includes("v-btn--icon") ).toBeTruthy(); + expect( + firstRowButtons.at(2).classes().includes("v-btn--icon") + ).toBeTruthy(); }); it("a dialog should be displayed with click on delete", async () => { diff --git a/src/components/administration/ExternalToolSection.vue b/src/components/administration/ExternalToolSection.vue index 65371c2476..5d8de703b5 100644 --- a/src/components/administration/ExternalToolSection.vue +++ b/src/components/administration/ExternalToolSection.vue @@ -32,9 +32,9 @@ diff --git a/src/components/administration/ExternalToolToolbar.unit.ts b/src/components/administration/ExternalToolToolbar.unit.ts index b0e3c754ad..1d900d833a 100644 --- a/src/components/administration/ExternalToolToolbar.unit.ts +++ b/src/components/administration/ExternalToolToolbar.unit.ts @@ -31,9 +31,10 @@ describe("ExternalToolToolbar", () => { const actions = wrapper.findAll("button"); - expect(actions.length).toEqual(2); + expect(actions.length).toEqual(3); expect(actions.at(0).classes().includes("v-btn--icon")).toBeTruthy(); expect(actions.at(1).classes().includes("v-btn--icon")).toBeTruthy(); + expect(actions.at(2).classes().includes("v-btn--icon")).toBeTruthy(); }); describe("when action button is clicked", () => { @@ -46,6 +47,15 @@ describe("ExternalToolToolbar", () => { expect(wrapper.emitted("edit")).toHaveLength(1); }); + it("should emit datasheet event", async () => { + setup(); + + const datasheetButton = wrapper.find("[data-testId=datasheetAction]"); + await datasheetButton.trigger("click"); + + expect(wrapper.emitted("datasheet")).toHaveLength(1); + }); + it("should emit delete event", async () => { setup(); diff --git a/src/components/administration/ExternalToolToolbar.vue b/src/components/administration/ExternalToolToolbar.vue index bf3a35693a..2f01f33593 100644 --- a/src/components/administration/ExternalToolToolbar.vue +++ b/src/components/administration/ExternalToolToolbar.vue @@ -49,7 +49,7 @@ import { export default defineComponent({ name: "ExternalToolToolbar", - emits: ["delete", "edit", "datasheet"], + emits: ["edit", "datasheet", "delete"], setup() { return { mdiPencilOutline, diff --git a/src/store/school-external-tools.unit.ts b/src/store/school-external-tools.unit.ts index 35d978c901..19d93ef684 100644 --- a/src/store/school-external-tools.unit.ts +++ b/src/store/school-external-tools.unit.ts @@ -621,5 +621,67 @@ describe("SchoolExternalToolsModule", () => { }); }); }); + + describe("createDatasheet is called", () => { + describe("when it successfully calls the api", () => { + const setup = () => { + const schoolExternalToolResponse = + schoolExternalToolResponseFactory.build(); + + apiMock.toolSchoolControllerGetSchoolExternalTool.mockResolvedValue( + mockApiResponse({ data: schoolExternalToolResponse }) + ); + + return { + schoolExternalToolResponse, + }; + }; + + it("should get a schoolExternalTool", async () => { + setup(); + + await module.createDatasheet("schoolExternalToolId"); + + expect( + apiMock.toolSchoolControllerGetSchoolExternalTool + ).toHaveBeenCalledWith("schoolExternalToolId"); + }); + + it("should call the toolApi.toolControllerGetDatasheet", async () => { + const { schoolExternalToolResponse } = setup(); + + await module.createDatasheet("schoolExternalToolId"); + + expect(apiMock.toolControllerGetDatasheet).toHaveBeenCalledWith( + schoolExternalToolResponse.toolId + ); + }); + }); + + describe("when an error occurs", () => { + const setup = () => { + const error = axiosErrorFactory.build(); + const apiError = mapAxiosErrorToResponseError(error); + + apiMock.toolControllerGetDatasheet.mockRejectedValue(error); + + return { + apiError, + }; + }; + + it("should set the businessError", async () => { + const { apiError } = setup(); + + await module.createDatasheet("schoolExternalToolId"); + + expect(module.getBusinessError).toEqual({ + error: apiError, + statusCode: apiError.code, + message: apiError.message, + }); + }); + }); + }); }); }); From 5258a961f295a21e477a8d3a020f3060afbcb1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= Date: Fri, 26 Jan 2024 15:42:55 +0100 Subject: [PATCH 03/10] datasheet button --- .../ExternalToolSection.unit.ts | 15 ++-- .../administration/ExternalToolSection.vue | 14 ++-- .../administration/ExternalToolToolbar.vue | 13 ++- .../external-tool-section-utils.composable.ts | 5 +- ...rnal-tool-section-utils.composable.unit.ts | 43 +++++----- .../school-external-tool-item.ts | 3 + src/locales/de.json | 2 +- src/locales/en.json | 1 + src/locales/es.json | 1 + src/locales/uk.json | 1 + src/store/school-external-tools.ts | 31 +------ src/store/school-external-tools.unit.ts | 80 +++---------------- 12 files changed, 73 insertions(+), 136 deletions(-) diff --git a/src/components/administration/ExternalToolSection.unit.ts b/src/components/administration/ExternalToolSection.unit.ts index d815e0a196..6537e80810 100644 --- a/src/components/administration/ExternalToolSection.unit.ts +++ b/src/components/administration/ExternalToolSection.unit.ts @@ -10,18 +10,18 @@ import { } from "@/utils/inject"; import { createModuleMocks } from "@/utils/mock-store-module"; import createComponentMocks from "@@/tests/test-utils/componentMocks"; -import { i18nMock } from "@@/tests/test-utils/i18nMock"; -import { mdiAlert, mdiCheckCircle } from "@mdi/js"; -import { mount, Wrapper } from "@vue/test-utils"; -import Vue, { ref } from "vue"; -import ExternalToolSection from "./ExternalToolSection.vue"; -import { createMock, DeepMocked } from "@golevelup/ts-jest"; -import { useSchoolExternalToolUsage } from "@data-external-tool"; import { schoolExternalToolFactory, schoolExternalToolMetadataFactory, schoolToolConfigurationStatusFactory, } from "@@/tests/test-utils/factory"; +import { i18nMock } from "@@/tests/test-utils/i18nMock"; +import { useSchoolExternalToolUsage } from "@data-external-tool"; +import { createMock, DeepMocked } from "@golevelup/ts-jest"; +import { mdiAlert, mdiCheckCircle } from "@mdi/js"; +import { mount, Wrapper } from "@vue/test-utils"; +import Vue, { ref } from "vue"; +import ExternalToolSection from "./ExternalToolSection.vue"; jest.mock("@data-external-tool"); @@ -149,6 +149,7 @@ describe("ExternalToolSection", () => { }, ], }); + return { wrapper, schoolExternalToolsModule, diff --git a/src/components/administration/ExternalToolSection.vue b/src/components/administration/ExternalToolSection.vue index 5d8de703b5..fd7d84efb4 100644 --- a/src/components/administration/ExternalToolSection.vue +++ b/src/components/administration/ExternalToolSection.vue @@ -33,7 +33,7 @@ @@ -106,7 +106,6 @@