From 3f05b8221a4c815eb698b44f2d8232bc89d6e057 Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Fri, 15 Mar 2024 17:21:33 +0100 Subject: [PATCH] fix(frontend): Remove trailing `/` from backend API requests A trailing slash leads to an unnecessary redirect, triggered by the backend. In addition, it's harder readable when a `/` is part of a base URL, which is then used in template literals, e.g. `${baseURL}/${resource}` is better to read than `${baseURL}${resource}`. The redirect behaviour lead to occasional request failues and some parts of the frontend stopped working when running behind a reverse proxy with TLS edge termination. --- .../projects/models/service/model.service.ts | 16 +++++------ .../service/project-user.service.ts | 27 ++++++++++--------- .../services/load-files/load-files.service.ts | 8 +++--- .../src/app/services/user/user.service.ts | 27 ++++++++++--------- .../service/t4c-repos/t4c-repo.service.ts | 12 ++++----- 5 files changed, 48 insertions(+), 42 deletions(-) diff --git a/frontend/src/app/projects/models/service/model.service.ts b/frontend/src/app/projects/models/service/model.service.ts index 6a92590ab..23cab28d2 100644 --- a/frontend/src/app/projects/models/service/model.service.ts +++ b/frontend/src/app/projects/models/service/model.service.ts @@ -26,7 +26,7 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class ModelService { - base_url = environment.backend_url + '/projects/'; + base_url = environment.backend_url + '/projects'; constructor(private http: HttpClient) {} @@ -37,11 +37,11 @@ export class ModelService { public readonly models$ = this._models.asObservable(); backendURLFactory(projectSlug: string, modelSlug: string) { - return `${this.base_url}${projectSlug}/models/${modelSlug}`; + return `${this.base_url}/${projectSlug}/models/${modelSlug}`; } loadModels(projectSlug: string): void { - this.http.get(`${this.base_url}${projectSlug}/models`).subscribe({ + this.http.get(`${this.base_url}/${projectSlug}/models`).subscribe({ next: (models) => this._models.next(models), error: () => this._models.next(undefined), }); @@ -49,7 +49,7 @@ export class ModelService { loadModelbySlug(modelSlug: string, projectSlug: string): void { this.http - .get(`${this.base_url}${projectSlug}/models/${modelSlug}/`) + .get(`${this.base_url}/${projectSlug}/models/${modelSlug}`) .subscribe({ next: (model) => this._model.next(model), error: () => this._model.next(undefined), @@ -58,7 +58,7 @@ export class ModelService { createModel(projectSlug: string, model: NewModel): Observable { return this.http - .post(`${this.base_url}${projectSlug}/models`, model) + .post(`${this.base_url}/${projectSlug}/models`, model) .pipe( tap({ next: (model) => { @@ -77,7 +77,7 @@ export class ModelService { nature_id: number, ): Observable { return this.http - .patch(`${this.base_url}${projectSlug}/models/${modelSlug}/`, { + .patch(`${this.base_url}/${projectSlug}/models/${modelSlug}`, { version_id, nature_id, }) @@ -98,7 +98,7 @@ export class ModelService { patchData: PatchModel, ): Observable { return this.http.patch( - `${this.base_url}${projectSlug}/models/${modelSlug}/`, + `${this.base_url}/${projectSlug}/models/${modelSlug}`, patchData, ); } @@ -138,7 +138,7 @@ export class ModelService { deleteModel(projectSlug: string, modelSlug: string): Observable { return this.http - .delete(`${this.base_url}${projectSlug}/models/${modelSlug}`) + .delete(`${this.base_url}/${projectSlug}/models/${modelSlug}`) .pipe( tap(() => { this.loadModels(projectSlug); diff --git a/frontend/src/app/projects/project-detail/project-users/service/project-user.service.ts b/frontend/src/app/projects/project-detail/project-users/service/project-user.service.ts index 4da105f8f..41698fb84 100644 --- a/frontend/src/app/projects/project-detail/project-users/service/project-user.service.ts +++ b/frontend/src/app/projects/project-detail/project-users/service/project-user.service.ts @@ -31,7 +31,7 @@ export class ProjectUserService { this.loadProjectUsersOnProjectChange(); this.loadProjectUserOnProjectChange(); } - BACKEND_URL_PREFIX = environment.backend_url + '/projects/'; + BACKEND_URL_PREFIX = environment.backend_url + '/projects'; PERMISSIONS = { read: 'read only', write: 'read & write' }; ROLES = { user: 'User', manager: 'Manager' }; @@ -108,7 +108,7 @@ export class ProjectUserService { filter(Boolean), switchMap((project) => this.http.get( - this.BACKEND_URL_PREFIX + project.slug + '/users/current', + `${this.BACKEND_URL_PREFIX}/${project.slug}/users/current`, ), ), ) @@ -141,7 +141,7 @@ export class ProjectUserService { loadProjectUsers(projectSlug: string): void { this._projectUsers.next(undefined); this.http - .get(this.BACKEND_URL_PREFIX + projectSlug + '/users') + .get(`${this.BACKEND_URL_PREFIX}/${projectSlug}/users`) .pipe( tap((projectUsers) => { this._projectUsers.next(projectUsers); @@ -158,7 +158,7 @@ export class ProjectUserService { reason: string, ): Observable { return this.http - .post(this.BACKEND_URL_PREFIX + projectSlug + '/users', { + .post(`${this.BACKEND_URL_PREFIX}/${projectSlug}/users`, { username, role, permission, @@ -178,32 +178,35 @@ export class ProjectUserService { reason: string, ): Observable { return this.http - .patch(this.BACKEND_URL_PREFIX + projectSlug + '/users/' + userID, { - role, - reason, - }) + .patch( + `${this.BACKEND_URL_PREFIX}/${projectSlug}/users/${userID}`, + { + role, + reason, + }, + ) .pipe(tap(() => this.loadProjectUsers(projectSlug))); } changePermissionOfProjectUser( - project_slug: string, + projectSlug: string, userID: number, permission: ProjectUserPermission, reason: string, ): Observable { return this.http.patch( - this.BACKEND_URL_PREFIX + project_slug + '/users/' + userID, + `${this.BACKEND_URL_PREFIX}/${projectSlug}/users/${userID}`, { permission, reason }, ); } deleteUserFromProject( - project_slug: string, + projectSlug: string, userID: number, reason: string, ): Observable { return this.http.delete( - this.BACKEND_URL_PREFIX + project_slug + '/users/' + userID, + `${this.BACKEND_URL_PREFIX}/${projectSlug}/users/${userID}`, { body: reason }, ); } diff --git a/frontend/src/app/services/load-files/load-files.service.ts b/frontend/src/app/services/load-files/load-files.service.ts index 0e21af0b4..fc476f718 100644 --- a/frontend/src/app/services/load-files/load-files.service.ts +++ b/frontend/src/app/services/load-files/load-files.service.ts @@ -13,13 +13,13 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class LoadFilesService { - BACKEND_URL_PREFIX = environment.backend_url + '/sessions/'; + BACKEND_URL_PREFIX = environment.backend_url + '/sessions'; constructor(private http: HttpClient) {} upload(id: string, files: FormData): Observable> { return this.http.post( - this.BACKEND_URL_PREFIX + id + '/files', + `${this.BACKEND_URL_PREFIX}/${id}/files`, files, { reportProgress: true, @@ -30,12 +30,12 @@ export class LoadFilesService { getCurrentFiles(id: string, showHiddenFiles: boolean): Observable { return this.http.get( - this.BACKEND_URL_PREFIX + id + '/files?show_hidden=' + showHiddenFiles, + `${this.BACKEND_URL_PREFIX}/${id}/files?show_hidden=${showHiddenFiles}`, ); } download(id: string, filename: string): Observable { - return this.http.get(`${this.BACKEND_URL_PREFIX}${id}/files/download`, { + return this.http.get(`${this.BACKEND_URL_PREFIX}/${id}/files/download`, { params: { filename: filename }, responseType: 'blob', }); diff --git a/frontend/src/app/services/user/user.service.ts b/frontend/src/app/services/user/user.service.ts index c41335b01..866ad038b 100644 --- a/frontend/src/app/services/user/user.service.ts +++ b/frontend/src/app/services/user/user.service.ts @@ -18,7 +18,7 @@ import { AuthService } from '../auth/auth.service'; export class UserService { user: User | undefined = undefined; - BACKEND_URL_PREFIX = environment.backend_url + '/users/'; + BACKEND_URL_PREFIX = environment.backend_url + '/users'; constructor( private http: HttpClient, @@ -48,19 +48,19 @@ export class UserService { } getUser(user: User): Observable { - return this.http.get(this.BACKEND_URL_PREFIX + user.id); + return this.http.get(`${this.BACKEND_URL_PREFIX}/${user.id}`); } getUserById(userId: number): Observable { - return this.http.get(this.BACKEND_URL_PREFIX + userId); + return this.http.get(`${this.BACKEND_URL_PREFIX}/${userId}`); } getCurrentUser(): Observable { - return this.http.get(this.BACKEND_URL_PREFIX + 'current'); + return this.http.get(`${this.BACKEND_URL_PREFIX}/current`); } deleteUser(user: User): Observable { - return this.http.delete(this.BACKEND_URL_PREFIX + user.id); + return this.http.delete(`${this.BACKEND_URL_PREFIX}/${user.id}`); } getUsers(): Observable { @@ -69,13 +69,13 @@ export class UserService { getOwnActiveSessions(): Observable> { return this.http.get( - this.BACKEND_URL_PREFIX + 'current/sessions', + `${this.BACKEND_URL_PREFIX}/current/sessions`, ); } getUserEvents(userId: number): Observable { return this.http.get( - this.BACKEND_URL_PREFIX + userId + '/events', + `${this.BACKEND_URL_PREFIX}/${userId}/events`, ); } @@ -84,10 +84,13 @@ export class UserService { role: UserRole, reason: string, ): Observable { - return this.http.patch(this.BACKEND_URL_PREFIX + user.id + '/roles', { - role, - reason, - }); + return this.http.patch( + `${this.BACKEND_URL_PREFIX}/${user.id}/roles`, + { + role, + reason, + }, + ); } validateUserRole(requiredRole: UserRole): boolean { @@ -104,7 +107,7 @@ export class UserService { loadCommonProjects(userId: number): Observable { return this.http.get( - `${this.BACKEND_URL_PREFIX}${userId}/common-projects`, + `${this.BACKEND_URL_PREFIX}/${userId}/common-projects`, ); } } diff --git a/frontend/src/app/settings/modelsources/t4c-settings/service/t4c-repos/t4c-repo.service.ts b/frontend/src/app/settings/modelsources/t4c-settings/service/t4c-repos/t4c-repo.service.ts index 1b8551c5e..809fa3f2d 100644 --- a/frontend/src/app/settings/modelsources/t4c-settings/service/t4c-repos/t4c-repo.service.ts +++ b/frontend/src/app/settings/modelsources/t4c-settings/service/t4c-repos/t4c-repo.service.ts @@ -41,7 +41,7 @@ export class T4CRepoService { this.http .get< T4CServerRepository[] - >(`${this.t4cInstanceService.urlFactory(instanceId)}/repositories/`) + >(`${this.t4cInstanceService.urlFactory(instanceId)}/repositories`) .subscribe({ next: (repositories) => this._repositories.next(repositories), error: () => this._repositories.next(undefined), @@ -62,7 +62,7 @@ export class T4CRepoService { ): Observable { return this.http .post( - `${this.t4cInstanceService.urlFactory(instanceId)}/repositories/`, + `${this.t4cInstanceService.urlFactory(instanceId)}/repositories`, repository, ) .pipe(tap(() => this.loadRepositories(instanceId))); @@ -71,14 +71,14 @@ export class T4CRepoService { startRepository(instanceId: number, repositoryId: number): Observable { this.publishRepositoriesWithChangedStatus(repositoryId, 'LOADING'); return this.http - .post(`${this.urlFactory(instanceId, repositoryId)}/start/`, {}) + .post(`${this.urlFactory(instanceId, repositoryId)}/start`, {}) .pipe(tap(() => this.loadRepositories(instanceId))); } stopRepository(instanceId: number, repositoryId: number): Observable { this.publishRepositoriesWithChangedStatus(repositoryId, 'LOADING'); return this.http - .post(`${this.urlFactory(instanceId, repositoryId)}/stop/`, {}) + .post(`${this.urlFactory(instanceId, repositoryId)}/stop`, {}) .pipe(tap(() => this.loadRepositories(instanceId))); } @@ -88,13 +88,13 @@ export class T4CRepoService { ): Observable { this.publishRepositoriesWithChangedStatus(repositoryId, 'LOADING'); return this.http - .post(`${this.urlFactory(instanceId, repositoryId)}/recreate/`, {}) + .post(`${this.urlFactory(instanceId, repositoryId)}/recreate`, {}) .pipe(tap(() => this.loadRepositories(instanceId))); } deleteRepository(instanceId: number, repositoryId: number): Observable { return this.http - .delete(`${this.urlFactory(instanceId, repositoryId)}/`) + .delete(`${this.urlFactory(instanceId, repositoryId)}`) .pipe(tap(() => this.loadRepositories(instanceId))); }