diff --git a/client/src/app/core/core-services/action.service.ts b/client/src/app/core/core-services/action.service.ts index 295b649014..7355fad03c 100644 --- a/client/src/app/core/core-services/action.service.ts +++ b/client/src/app/core/core-services/action.service.ts @@ -2,9 +2,30 @@ import { Injectable } from '@angular/core'; import { HttpService } from './http.service'; -export interface RequestInfo { +export interface ActionRequest { action: string; - data: any; + data: any[]; +} + +interface ActionResponse { + success: true; + message: string; + results?: ((T | null)[] | null)[]; +} +function isActionResponse(obj: any): obj is ActionResponse { + const response = obj as ActionResponse; + return !!obj && response.success === true && !!response.message; +} + +interface ActionError { + success: false; + message: string; + error_index?: number; +} + +function isActionError(obj: any): obj is ActionError { + const response = obj as ActionError; + return !!obj && response.success === false && !!response.message; } @Injectable({ @@ -15,15 +36,39 @@ export class ActionService { private constructor(private http: HttpService) {} - public async sendRequest(action: string, data: any): Promise { - return this._sendRequest({ action, data: [data] }); + public async sendRequest(action: string, data: any): Promise { + const results = await this._sendRequest({ action, data: [data] }); + if (!results) { + return null; + } + if (results.length !== 1) { + throw new Error('Inner resultlength is not 1 from the action service'); + } + return results[0]; } - public async sendBulkRequest(action: string, data: any[]): Promise { - return this._sendRequest({ action, data }); + public async sendBulkRequest(action: string, data: any[]): Promise { + const results = await this._sendRequest({ action, data }); + if (results.length !== data.length) { + throw new Error(`Inner resultlength is not ${data.length} from the action service`); + } + return results; } - private async _sendRequest(request: RequestInfo): Promise { - return await this.http.post(this.ACTION_URL, [request]); + private async _sendRequest(request: ActionRequest): Promise { + const response = await this.http.post(this.ACTION_URL, [request]); + if (isActionError(response)) { + throw response.message; + } else if (isActionResponse(response)) { + const results = response.results; + if (!results) { + return null; + } + if (results.length !== 1) { + throw new Error('Resultlength is not 1 from the action service'); + } + return results[0]; + } + throw new Error('Unknown return type from action service'); } } diff --git a/client/src/app/core/core-services/http.service.ts b/client/src/app/core/core-services/http.service.ts index 2d311a0e16..7c05cf62a0 100644 --- a/client/src/app/core/core-services/http.service.ts +++ b/client/src/app/core/core-services/http.service.ts @@ -10,21 +10,7 @@ import { HttpOptions } from '../definitions/http-options'; import { formatQueryParams, QueryParams } from '../definitions/query-params'; import { toBase64 } from '../to-base64'; -export interface ErrorDetailResponse { - detail: string | string[]; - args?: string[]; -} - -function isErrorDetailResponse(obj: any): obj is ErrorDetailResponse { - return ( - obj && - typeof obj === 'object' && - (typeof obj.detail === 'string' || obj.detail instanceof Array) && - (!obj.args || obj.args instanceof Array) - ); -} - -export interface ErrorMessageResponse { +interface ErrorMessageResponse { message: string; success: boolean; } @@ -142,27 +128,16 @@ export class HttpService { return error; } - if (e.status === 405) { - // this should only happen, if the url is wrong -> a bug. - error += this.translate.instant( - 'The requested method is not allowed. Please contact your system administrator.' - ); - } else if (!e.error) { + if (!e.error) { error += this.translate.instant("The server didn't respond."); } else if (typeof e.error === 'object') { - if (isErrorDetailResponse(e.error)) { - error += this.processErrorDetailResponse(e.error); - } else if (isErrorMessageResponse(e.error)) { + if (isErrorMessageResponse(e.error)) { error += e.error.message; } else { const errorList = Object.keys(e.error).map(key => { const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1); const message = e.error[key]; - if (typeof message === 'string') { - return `${this.translate.instant(capitalizedKey)}: ${message}`; - } else { - return `${this.translate.instant(capitalizedKey)}: ${this.processErrorDetailResponse(message)}`; - } + return `${this.translate.instant(capitalizedKey)}: ${message}`; }); error = errorList.join(', '); } @@ -179,29 +154,6 @@ export class HttpService { return error; } - /** - * Errors from the servers may be string or array of strings. This function joins the strings together, - * if an array is send. - * @param str a string or a string array to join together. - * @returns Error text(s) as single string - */ - private processErrorDetailResponse(response: ErrorDetailResponse): string { - let message: string; - if (response.detail instanceof Array) { - message = response.detail.join(' '); - } else { - message = response.detail; - } - message = this.translate.instant(message); - - if (response.args && response.args.length > 0) { - for (let i = 0; i < response.args.length; i++) { - message = message.replace(`{${i}}`, response.args[i].toString()); - } - } - return message; - } - /** * Executes a get on a path with a certain object * @param path The path to send the request to. diff --git a/client/src/app/core/repositories/base-repository.ts b/client/src/app/core/repositories/base-repository.ts index 7b93a441b0..a3b496b7c5 100644 --- a/client/src/app/core/repositories/base-repository.ts +++ b/client/src/app/core/repositories/base-repository.ts @@ -340,11 +340,21 @@ export abstract class BaseRepository { - return this.actions.sendRequest(action, payload).catch(this.raiseError); + protected async sendActionToBackend(action: string, payload: any): Promise { + try { + return await this.actions.sendRequest(action, payload); + } catch (e) { + this.raiseError(e); + throw e; + } } - protected sendBulkActionToBackend(action: string, payload: any[]): Promise { - return this.actions.sendBulkRequest(action, payload).catch(this.raiseError); + protected async sendBulkActionToBackend(action: string, payload: any[]): Promise { + try { + return await this.actions.sendBulkRequest(action, payload); + } catch (e) { + this.raiseError(e); + throw e; + } } } diff --git a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts index 94910225db..9172dc396d 100644 --- a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts +++ b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts @@ -515,10 +515,8 @@ export class AssignmentDetailComponent extends BaseModelContextComponent impleme */ public async createAssignment(): Promise { try { - /*const response = */ await this.assignmentRepo.create(this.assignmentForm.value); - console.error('TODO: wait for returned id and navigate to it'); - this.router.navigate([`./assignments/`]); - // this.router.navigate([`./assignments/${response.id}`]); + const response = await this.assignmentRepo.create(this.assignmentForm.value); + this.router.navigate([`./assignments/${response.id}`]); } catch (e) { this.raiseError(e); }