diff --git a/src/main/java/edu/byu/cs/controller/AdminController.java b/src/main/java/edu/byu/cs/controller/AdminController.java index 43507e84..6add0432 100755 --- a/src/main/java/edu/byu/cs/controller/AdminController.java +++ b/src/main/java/edu/byu/cs/controller/AdminController.java @@ -45,57 +45,6 @@ public class AdminController { }; - public static final Route userPatch = (req, res) -> { - String netId = req.params(":netId"); - - UserDao userDao = DaoService.getUserDao(); - User user; - try { - user = userDao.getUser(netId); - } catch (DataAccessException e) { - LOGGER.error("Error getting user", e); - halt(500); - return null; - } - - if (user == null) { - halt(404, "user not found"); - return null; - } - - try { - String firstName = req.queryParams("firstName"); - if (firstName != null) - userDao.setFirstName(user.netId(), firstName); - - String lastName = req.queryParams("lastName"); - if (lastName != null) - userDao.setLastName(user.netId(), lastName); - - String repoUrl = req.queryParams("repoUrl"); - if (repoUrl != null) - userDao.setRepoUrl(user.netId(), repoUrl); - - String role = req.queryParams("role"); - if (role != null) { - try { - userDao.setRole(user.netId(), User.Role.valueOf(role.toUpperCase())); - } catch (IllegalArgumentException e) { - halt(400, "invalid role. must be one of: STUDENT, ADMIN"); - return null; - } - } - } catch (DataAccessException e) { - LOGGER.error("Error updating user", e); - halt(500); - return null; - } - - res.status(204); - - return ""; - }; - public static final Route testModeGet = (req, res) -> { User latestTestStudent; try { diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index 18da1260..1b4585ee 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -82,8 +82,6 @@ public static int setupEndpoints(int port) { get("/users", usersGet); - patch("/user/:netId", userPatch); - post("/submit", adminRepoSubmitPost); path("/submissions", () -> { diff --git a/src/main/resources/frontend/src/App.vue b/src/main/resources/frontend/src/App.vue index 65e6aef4..a4c0594b 100644 --- a/src/main/resources/frontend/src/App.vue +++ b/src/main/resources/frontend/src/App.vue @@ -11,6 +11,7 @@ import BannerMessage from '@/components/BannerMessage.vue' import PopUp from '@/components/PopUp.vue' import RepoEditor from '@/components/RepoEditor.vue' import AboutPage from '@/components/AboutPage.vue' +import { ServerError } from '@/network/ServerError' const greeting = computed(() => { if (useAuthStore().isLoggedIn) { @@ -23,9 +24,13 @@ const logOut = async () => { await logoutPost() useAuthStore().user = null } catch (e) { - alert(e) + if (e instanceof ServerError){ + alert(e.message) + } else { + alert(e) + } } - router.push({name: "login"}) + await router.push({ name: "login" }) } onMounted( async () => { diff --git a/src/main/resources/frontend/src/components/BannerMessage.vue b/src/main/resources/frontend/src/components/BannerMessage.vue index 2a8afb81..edaf106f 100644 --- a/src/main/resources/frontend/src/components/BannerMessage.vue +++ b/src/main/resources/frontend/src/components/BannerMessage.vue @@ -1,5 +1,5 @@ diff --git a/src/main/resources/frontend/src/components/searchableList/ItemType.ts b/src/main/resources/frontend/src/components/searchableList/ItemType.ts deleted file mode 100644 index 6cc4c83d..00000000 --- a/src/main/resources/frontend/src/components/searchableList/ItemType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ItemType = { - label: string; - item: any; -} diff --git a/src/main/resources/frontend/src/components/searchableList/SearchableList.vue b/src/main/resources/frontend/src/components/searchableList/SearchableList.vue deleted file mode 100644 index 2d978b55..00000000 --- a/src/main/resources/frontend/src/components/searchableList/SearchableList.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - - - isInputFocused = true" - @blur="() => isInputFocused = false" - /> - - isHoverOverList = true" - @mouseleave="() => isHoverOverList = false"> - - {{ item.label }} - - - - - - - diff --git a/src/main/resources/frontend/src/network/ServerCommunicator.ts b/src/main/resources/frontend/src/network/ServerCommunicator.ts new file mode 100644 index 00000000..2946e030 --- /dev/null +++ b/src/main/resources/frontend/src/network/ServerCommunicator.ts @@ -0,0 +1,287 @@ +import { useAuthStore } from '@/stores/auth' +import { ServerError } from '@/network/ServerError' +import { useAppConfigStore } from '@/stores/appConfig' + +/** + * Utility for making authenticated HTTP requests to the server with automatic error handling + * and response parsing. + * + * @example + * // GET request expecting a User response + * const user = await ServerCommunicator.getRequest('/api/user'); + * + * // POST request with body, not expecting response + * await ServerCommunicator.postRequest('/api/logs', { event: 'action' }, false); + */ +export const ServerCommunicator = { + getRequest: getRequest, + getRequestGuaranteed: getRequestGuaranteed, + postRequest: postRequest, + patchRequest: patchRequest, + doUnprocessedRequest: doUnprocessedRequest +} + +/** + * Makes a GET request to the specified endpoint with a guaranteed response. + * This will not throw an error if the server returns an error, but will print it + * to the console. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {T} errorResponse - The object the method call should return if the server + * returns nothing or responds with a non-2XX code + * @returns {Promise} Promise that resolves to the response data of type T + */ +async function getRequestGuaranteed(endpoint: string, errorResponse: T): Promise { + try { + return await getRequest(endpoint, true) + } catch (e) { + return errorResponse + } +} +/** + * Makes a GET request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + */ +function getRequest(endpoint: string, expectResponse: false): Promise; +/** + * Makes a GET request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + */ +function getRequest(endpoint: string, expectResponse?: true): Promise; +async function getRequest( + endpoint: string, + expectResponse: boolean = true +): Promise { + if (expectResponse) { + return await doRequest("GET", endpoint, null, true); + } + return await doRequest("GET", endpoint, null, false); +} + +/** + * Makes a POST request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + * + * @example + * // With response + * const user = await postRequest('/api/users', { name: 'John' }); + * + * // Without response + * await postRequest('/api/logs', { event: 'action' }, false); + */ +function postRequest(endpoint: string, bodyObject: Object | null, expectResponse: false): Promise; +/** + * Makes a POST request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + * + * @example + * // With response + * const user = await postRequest('/api/users', { name: 'John' }); + * + * // Without response + * await postRequest('/api/logs', { event: 'action' }, false); + */ +function postRequest(endpoint: string, bodyObject?: Object | null, expectResponse?: true): Promise; +async function postRequest( + endpoint: string, + bodyObject: Object | null = null, + expectResponse: boolean = true +): Promise { + if (expectResponse) { + return await doRequest("POST", endpoint, bodyObject, true); + } + return await doRequest("POST", endpoint, bodyObject, false); + +} + +/** + * Makes a PATCH request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + * + * @example + * // With response + * const user = await patchRequest('/api/users/123', { name: 'John' }); + * + * // Without response + * await patchRequest('/api/users/123/status', { status: 'active' }, false); + */ +function patchRequest(endpoint: string, bodyObject: Object | null, expectResponse: false): Promise; +/** + * Makes a PATCH request to the specified endpoint. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} when expectResponse is true but no response is received + * + * @example + * // With response + * const user = await patchRequest('/api/users/123', { name: 'John' }); + * + * // Without response + * await patchRequest('/api/users/123/status', { status: 'active' }, false); + */ +function patchRequest(endpoint: string, bodyObject?: Object | null, expectResponse?: true): Promise; +async function patchRequest( + endpoint: string, + bodyObject: Object | null = null, + expectResponse: boolean = true +): Promise { + if (expectResponse) { + return doRequest("PATCH", endpoint, bodyObject, true); + } + return doRequest("PATCH", endpoint, bodyObject, false); +} + +/** + * Internal method to make an HTTP request. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} method - The HTTP method to use + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} When expectResponse is true but no response is received + * @internal + */ +function doRequest( + method: string, + endpoint: string, + bodyObject: Object | null, + expectResponse: false +): Promise; +/** + * Internal method to make an HTTP request. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} method - The HTTP method to use + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} When expectResponse is true but no response is received + * @internal + */ +function doRequest( + method: string, + endpoint: string, + bodyObject?: Object | null, + expectResponse?: true +): Promise; +/** + * Internal method to make an HTTP request. + * @template T - The type of the expected response (when expectResponse is true) + * @param {string} method - The HTTP method to use + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @param {boolean} [expectResponse=true] - Whether to expect and parse a response + * @returns {Promise} Promise that resolves to: + * - The response data of type T when expectResponse is true + * - null when expectResponse is false + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + * @throws {Error} When expectResponse is true but no response is received + * @internal + */ +async function doRequest( + method: string, + endpoint: string, + bodyObject: Object | null = null, + expectResponse: boolean = true +): Promise { + const response = await doUnprocessedRequest(method, endpoint, bodyObject); + + if (!expectResponse) { + return null; + } + + const text = await response.text() + if (text) { + return JSON.parse(text) as T + } + + if (bodyObject) { + console.error("Body request:", bodyObject) + } + console.error("Response: ", response) + throw new Error(`Expected a response from ${method} call to ${endpoint} but got none`) +} + +/** + * Makes a raw HTTP request to the server with authentication. + * @param {string} method - The HTTP method to use + * @param {string} endpoint - The API endpoint to call + * @param {Object | null} [bodyObject=null] - The request body object to send (will be sent as JSON) + * @returns {Promise} A promise that resolves to the raw fetch response object + * @throws {ServerError} When the request fails (meaning the server returned a code other than 2XX) + */ +async function doUnprocessedRequest( + method: string, + endpoint: string, + bodyObject: Object | null = null +): Promise { + const authToken = useAuthStore().token ?? "" + + const response = await fetch(useAppConfigStore().backendUrl + endpoint, { + method: method, + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + 'Authorization': authToken + }, + body: bodyObject ? JSON.stringify(bodyObject) : null + }); + + if (!response.ok) { + console.error(`A ${response.status} error occurred while making a ${method} request to ${endpoint}`) + console.error(response) + throw new ServerError(endpoint, await response.text(), response.status, response.statusText) + } + return response +} diff --git a/src/main/resources/frontend/src/network/ServerError.ts b/src/main/resources/frontend/src/network/ServerError.ts new file mode 100644 index 00000000..81ad25c2 --- /dev/null +++ b/src/main/resources/frontend/src/network/ServerError.ts @@ -0,0 +1,25 @@ +export class ServerError extends Error { + readonly endpoint: string + readonly status: number + readonly statusText: string + constructor( + endpoint: string, + message: string, + status: number, + statusText: string, + ) { + super(message); + this.name = "ServerError" + this.endpoint = endpoint + this.status = status + this.statusText = statusText + } + + isBadRequest(): boolean { return this.status === 400; } + isUnauthorized(): boolean { return this.status === 401; } + isForbidden(): boolean { return this.status === 403; } + isNotFound(): boolean { return this.status === 404; } + isHonorCodeViolation(): boolean { return this.status === 418; } + isUnprocessableEntity(): boolean { return this.status === 422; } + isInternalServerError(): boolean { return this.status === 500; } +} \ No newline at end of file diff --git a/src/main/resources/frontend/src/services/adminService.ts b/src/main/resources/frontend/src/services/adminService.ts index 362f7030..e2a15d8c 100755 --- a/src/main/resources/frontend/src/services/adminService.ts +++ b/src/main/resources/frontend/src/services/adminService.ts @@ -1,117 +1,31 @@ -import {useAppConfigStore} from "@/stores/appConfig"; import type {CanvasSection, Phase, Submission, User } from '@/types/types' import type {Option} from "@/views/AdminView/Analytics.vue"; +import { ServerCommunicator } from '@/network/ServerCommunicator' export const usersGet = async (): Promise => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/users', { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - return []; - } + return await ServerCommunicator.getRequestGuaranteed('/api/admin/users', []) } export const submissionsForUserGet = async (netId: string): Promise => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/submissions/student/' + netId, { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - return []; - } + return await ServerCommunicator.getRequestGuaranteed('/api/admin/submissions/student/' + netId, []) } export const approveSubmissionPost = async (netId: string, phase: Phase, penalize: boolean) => { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/submissions/approve', { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - netId, - phase, - penalize, - }) - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } -} - -interface UserPatch { - netId: string, - firstName?: string, - lastName?: string, - repoUrl?: string, - role?: string - -} - -/** - * this sends to /api/admin/user/{netId} - * - * query params can be zero or more of the following: firstName, lastName, repoUrl, role. these are added only if needed to the url - * @param user - */ -export const userPatch = async (user: UserPatch)=> { - const paramsString = new URLSearchParams(); - - if (user.firstName) - paramsString.append('firstName', user.firstName); - if (user.lastName) - paramsString.append('lastName', user.lastName); - if (user.repoUrl) - paramsString.append('repoUrl', user.repoUrl); - if (user.role) - paramsString.append('role', user.role); - - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/user/' + user.netId + '?' + paramsString.toString(), { - method: 'PATCH', - credentials: 'include' - }); - - } catch (e) { - console.error('Failed to update user: ', e); - } + await ServerCommunicator.postRequest('/api/admin/submissions/approve', + { + netId, + phase, + penalize, + }) } export const submissionsLatestGet = async (batchSize?: number): Promise => { batchSize = batchSize ? batchSize : -1 - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/submissions/latest/' + batchSize, { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - return []; - } + return await ServerCommunicator.getRequestGuaranteed('/api/admin/submissions/latest/' + batchSize, []) } export const testStudentModeGet = async (): Promise => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/test_mode', { - method: 'GET', - credentials: 'include' - }); - - return null; - } catch (e) { - console.error('Failed to activate test mode: ', e); - return null; - } + return await ServerCommunicator.getRequestGuaranteed('/api/admin/test_mode', null) } type QueueStatusResponse = { @@ -119,56 +33,29 @@ type QueueStatusResponse = { inQueue: string[] } export const getQueueStatus = async (): Promise => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/submissions/active', { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - console.error('Failed to get queue status: ', e); - return { - currentlyGrading: [], - inQueue: [] - }; - } + return await ServerCommunicator.getRequestGuaranteed( + '/api/admin/submissions/active', { + currentlyGrading: [], + inQueue: [] + }) } export const commitAnalyticsGet = async (option: Option): Promise => { try { - return (await fetch(useAppConfigStore().backendUrl + '/api/admin/analytics/commit/' + option, { - method: 'GET', - credentials: 'include' - })).text() + return (await ServerCommunicator.doUnprocessedRequest("GET", '/api/admin/analytics/commit/' + option)).text() } catch (e) { - console.error('Failed to get data: ', e) return '' } } export const honorCheckerZipGet = async (section: number): Promise => { try { - return (await fetch(useAppConfigStore().backendUrl + '/api/admin/honorChecker/zip/' + section, { - method: 'GET', - credentials: 'include' - })).blob() + return (await ServerCommunicator.doUnprocessedRequest("GET", '/api/admin/honorChecker/zip/' + section)).blob() } catch (e) { - console.error('Failed to get data: ', e) return new Blob() } } export const sectionsGet = async (): Promise => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/sections', { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - console.error('Failed to get data: ', e) - return []; - } + return await ServerCommunicator.getRequestGuaranteed('/api/admin/sections', []) } diff --git a/src/main/resources/frontend/src/services/authService.ts b/src/main/resources/frontend/src/services/authService.ts index fb88e191..20194402 100644 --- a/src/main/resources/frontend/src/services/authService.ts +++ b/src/main/resources/frontend/src/services/authService.ts @@ -1,5 +1,5 @@ -import {useAppConfigStore} from "@/stores/appConfig"; import { useAuthStore } from '@/stores/auth' +import { ServerCommunicator } from '@/network/ServerCommunicator' type MeResponse = { netId: string, @@ -9,16 +9,7 @@ type MeResponse = { role: 'STUDENT' | 'ADMIN' } export const meGet = async () => { - try { - const response = await fetch(useAppConfigStore().backendUrl + '/api/me', { - method: 'GET', - credentials: 'include' - }); - - return await response.json() as MeResponse | null; - } catch (e) { - return null; - } + return await ServerCommunicator.getRequestGuaranteed('/api/me', null) } export const loadUser = async () => { @@ -30,16 +21,5 @@ export const loadUser = async () => { } export const logoutPost = async () => { - const response = await fetch(useAppConfigStore().backendUrl + "/auth/logout", { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } + await ServerCommunicator.postRequest( "/auth/logout", null, false) } diff --git a/src/main/resources/frontend/src/services/configService.ts b/src/main/resources/frontend/src/services/configService.ts index 212e392f..605b266f 100644 --- a/src/main/resources/frontend/src/services/configService.ts +++ b/src/main/resources/frontend/src/services/configService.ts @@ -1,25 +1,16 @@ import { type Config, useAppConfigStore } from '@/stores/appConfig' import {Phase, type RubricInfo, type RubricType} from '@/types/types' import { useAuthStore } from '@/stores/auth' +import { ServerCommunicator } from '@/network/ServerCommunicator' export const getConfig = async ():Promise => { - let path = "/api" + let endpoint = "/api" if (useAuthStore().user?.role == 'ADMIN') { - path += "/admin" + endpoint += "/admin" } - path += "/config" + endpoint += "/config" - try { - const response = await fetch(useAppConfigStore().backendUrl + path, { - method: 'GET', - credentials: 'include' - }); - - return await response.json(); - } catch (e) { - console.error('Failed to get configuration: ', e); - throw "Failed to get configuration" - } + return await ServerCommunicator.getRequest(endpoint) } export const setBanner = async (message: String, link: String, color: String, expirationTimestamp: String): Promise => { @@ -37,7 +28,7 @@ export const setLivePhases = async (phases: Array): Promise => { } export const setCanvasCourseIds = async (): Promise => { - await doSetConfigItem("GET", "/api/admin/config/courseIds", null); + await doSetConfigItem("GET", "/api/admin/config/courseIds", {}); } const convertRubricInfoToObj = (rubricInfo: Map>): object => { @@ -61,25 +52,11 @@ export const setCourseIds = async ( await doSetConfigItem("POST", "/api/admin/config/courseIds", body); } -const doSetConfigItem = async (method: string, path: string, body: Object | null): Promise => { - const baseOptions: RequestInit = { - method: method, - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - } - }; - const fetchOptions: RequestInit = (method !== "GET") - ? { - ...baseOptions, - body: JSON.stringify(body) - } : baseOptions; - - const response = await fetch(useAppConfigStore().backendUrl + path, fetchOptions); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } - await useAppConfigStore().updateConfig(); +const doSetConfigItem = async (method: string, path: string, body: Object): Promise => { + if (method == "GET") { + await ServerCommunicator.getRequest(path, false) + } else { + await ServerCommunicator.postRequest(path, body, false) + } + await useAppConfigStore().updateConfig(); } diff --git a/src/main/resources/frontend/src/services/submissionService.ts b/src/main/resources/frontend/src/services/submissionService.ts index d1c9193b..9445bb95 100644 --- a/src/main/resources/frontend/src/services/submissionService.ts +++ b/src/main/resources/frontend/src/services/submissionService.ts @@ -1,105 +1,34 @@ import type {Submission} from "@/types/types"; import { Phase } from "@/types/types"; -import {useAppConfigStore} from "@/stores/appConfig"; +import { ServerCommunicator } from '@/network/ServerCommunicator' export const submissionsGet = async (phase: Phase | null): Promise => { - let url = useAppConfigStore().backendUrl + '/api/submission' + (phase === null ? "" : "/" + Phase[phase]) - const response = await fetch(url, { - method: 'GET', - credentials: 'include' - }); - - if (!response.ok) { - console.error(response); - return []; - } - - return await response.json() as Submission[]; + const endpoint: string = '/api/submission' + (phase === null ? "" : "/" + Phase[phase]) + return await ServerCommunicator.getRequestGuaranteed(endpoint, []) }; export const lastSubmissionGet = async (): Promise => { - const response = await fetch(useAppConfigStore().backendUrl + '/api/latest', { - method: 'GET', - credentials: 'include' - }); - - if (!response.ok) { - console.error(response); - return null; - } - - return await response.json() as Submission; + return await ServerCommunicator.getRequestGuaranteed("/api/latest", null) }; export const submissionPost = async (phase: Phase): Promise => { - const response = await fetch(useAppConfigStore().backendUrl + '/api/submit', { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - "phase": Phase[phase], - }) - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } + await ServerCommunicator.postRequest("/api/submit", { "phase": Phase[phase] }, false) } export const adminSubmissionPost = async (phase: Phase, repoUrl: String): Promise => { - const response = await fetch(useAppConfigStore().backendUrl + '/api/admin/submit', { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - "phase": Phase[phase], - "repoUrl": repoUrl - }) - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } + await ServerCommunicator.postRequest("/api/admin/submit", { + "phase": Phase[phase], + "repoUrl": repoUrl + }, false) } type SubmitGetResponse = { inQueue: boolean, } export const submitGet = async (): Promise => { - const response = await fetch(useAppConfigStore().backendUrl + '/api/submit', { - method: 'GET', - credentials: 'include' - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } - - const body = await response.json() as SubmitGetResponse; - - return body.inQueue; + return (await ServerCommunicator.getRequest("/api/submit")).inQueue } export const reRunSubmissionsPost = async () => { - const response = await fetch(useAppConfigStore().backendUrl + "/api/admin/submissions/rerun", { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } else { - return true; - } + await ServerCommunicator.postRequest("/api/admin/submissions/rerun") } diff --git a/src/main/resources/frontend/src/services/userService.ts b/src/main/resources/frontend/src/services/userService.ts index a26a9595..a9b88591 100644 --- a/src/main/resources/frontend/src/services/userService.ts +++ b/src/main/resources/frontend/src/services/userService.ts @@ -1,43 +1,20 @@ -import {useAppConfigStore} from "@/stores/appConfig"; import type { RepoUpdate } from '@/types/types' +import { ServerCommunicator } from '@/network/ServerCommunicator' export const repoHistoryGet = async (netId: String): Promise => { - let url = useAppConfigStore().backendUrl + '/api/admin/repo/history?netId=' + netId - const response = await fetch(url, { - method: 'GET', - credentials: 'include' - }); - - if (!response.ok) { - console.error(response); - return []; - } - - return await response.json() as RepoUpdate[]; + return await ServerCommunicator.getRequestGuaranteed('/api/admin/repo/history?netId=' + netId, []) }; -export const studentUpdateRepoPatch = async (repoUrl: String): Promise => { +export const studentUpdateRepoPatch = async (repoUrl: string): Promise => { await updateRepoPatch(repoUrl, '/api/repo') } -export const adminUpdateRepoPatch = async (repoUrl: String, netId: String): Promise => { +export const adminUpdateRepoPatch = async (repoUrl: string, netId: String): Promise => { await updateRepoPatch(repoUrl, "/api/admin/repo/" + netId) } -const updateRepoPatch = async (repoUrl: String, endpoint: String): Promise => { - const response = await fetch(useAppConfigStore().backendUrl + endpoint, { - method: 'PATCH', - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - "repoUrl": repoUrl - }) - }); - - if (!response.ok) { - console.error(response); - throw new Error(await response.text()); - } +const updateRepoPatch = async (repoUrl: string, endpoint: string): Promise => { + await ServerCommunicator.patchRequest(endpoint, { + "repoUrl": repoUrl + }, false) } diff --git a/src/main/resources/frontend/src/views/StudentView/ResultsPreview.vue b/src/main/resources/frontend/src/views/StudentView/ResultsPreview.vue index ce980b4c..3c0a7153 100644 --- a/src/main/resources/frontend/src/views/StudentView/ResultsPreview.vue +++ b/src/main/resources/frontend/src/views/StudentView/ResultsPreview.vue @@ -1,9 +1,8 @@