From fd5a1cb14f5b9842d1c518c8dbc76d1544cec880 Mon Sep 17 00:00:00 2001 From: Polianin Nikita Date: Sat, 24 Aug 2024 04:24:44 +0300 Subject: [PATCH] fix: delete saving states Parallel use of the same Api Service instance is likely to replace requestData --- src/api/RequestBuilder.ts | 31 +++----- src/api/api.service.ts | 119 ++++++++++++++++--------------- src/api/v1/authApiService.ts | 31 ++++---- src/api/v1/discipline.service.ts | 7 +- src/api/v1/faculty.service.ts | 7 +- src/api/v1/group.service.ts | 7 +- src/api/v1/professor.service.ts | 7 +- src/api/v1/schedule.service.ts | 35 +++++---- src/api/v1/setup.service.ts | 77 +++++++++++--------- 9 files changed, 169 insertions(+), 152 deletions(-) diff --git a/src/api/RequestBuilder.ts b/src/api/RequestBuilder.ts index 74ffec3..3b7e5f4 100644 --- a/src/api/RequestBuilder.ts +++ b/src/api/RequestBuilder.ts @@ -1,9 +1,5 @@ import {HttpHeaders} from "@angular/common/http"; -export interface SetRequestBuilderAfterBuild { - setRequestBuilder(request: RequestData): void; -} - export interface RequestData { endpoint: string; queryParams: Record | null> | null; @@ -11,6 +7,7 @@ export interface RequestData { data: any; silenceMode: boolean; withCredentials: boolean; + needAuth: boolean; } export class RequestBuilder { @@ -20,10 +17,8 @@ export class RequestBuilder { private data: any = null; private silenceMode: boolean = false; private withCredentials: boolean = false; - private readonly object: any; - constructor(obj: any) { - this.object = obj; + constructor() { } public setEndpoint(endpoint: string): this { @@ -58,16 +53,16 @@ export class RequestBuilder { return this; } - public build(): Type { - (this.object as SetRequestBuilderAfterBuild).setRequestBuilder({ + public get build(): RequestData { + return { endpoint: this.endpoint, queryParams: this.queryParams, httpHeaders: this.httpHeaders, data: this.data, silenceMode: this.silenceMode, - withCredentials: this.withCredentials - }); - return this.object as Type; + withCredentials: this.withCredentials, + needAuth: false + }; } public getEndpoint(): string { @@ -97,16 +92,8 @@ export class RequestBuilder { httpHeaders: new HttpHeaders(), data: null, silenceMode: false, - withCredentials: false + withCredentials: false, + needAuth: false }; } - - public reset(): void { - this.endpoint = ''; - this.queryParams = null; - this.httpHeaders = new HttpHeaders(); - this.data = null; - this.silenceMode = false; - this.withCredentials = false; - } } diff --git a/src/api/api.service.ts b/src/api/api.service.ts index 442ba05..457ac00 100644 --- a/src/api/api.service.ts +++ b/src/api/api.service.ts @@ -1,10 +1,10 @@ -import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, tap, timer} from "rxjs"; +import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, timer} from "rxjs"; import {HttpClient, HttpErrorResponse} from "@angular/common/http"; import {NotifyColor, OpenNotifyService} from "@service/open-notify.service"; import {environment} from "@environment"; import {Router} from "@angular/router"; import {Injectable} from "@angular/core"; -import {RequestBuilder, RequestData, SetRequestBuilderAfterBuild} from "@api/RequestBuilder"; +import {RequestBuilder, RequestData} from "@api/RequestBuilder"; import {TokenRefreshService} from "@service/token-refresh.service"; import {AuthToken} from "@service/auth.service"; @@ -36,19 +36,16 @@ export enum AvailableVersion { } @Injectable() -export default abstract class ApiService implements SetRequestBuilderAfterBuild { +export default abstract class ApiService { constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router, protected tokenRefreshService: TokenRefreshService) { } private apiUrl = environment.apiUrl; + protected abstract basePath: string; protected abstract version: AvailableVersion; - private request: RequestData = RequestBuilder.getStandardRequestData(); - public static readonly tokenKey = 'auth_token'; - public setRequestBuilder(request: RequestData): void { - this.request = request; - } + public static readonly tokenKey = 'auth_token'; private static addQuery(endpoint: string, queryParams?: Record | null> | null): string { const url = new URL(endpoint); @@ -72,73 +69,81 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild return parts.map(part => part.replace(/(^\/+|\/+$)/g, '')).join('/'); } - protected get combinedUrl() { - return ApiService.addQuery(ApiService.combineUrls(this.apiUrl, AvailableVersion[this.version], this.basePath, this.request.endpoint), this.request.queryParams); + protected combinedUrl(request: RequestData) { + return ApiService.addQuery(ApiService.combineUrls(this.apiUrl, AvailableVersion[this.version], this.basePath, request.endpoint), request.queryParams); } - private makeHttpRequest(method: 'get' | 'post' | 'delete' | 'put'): Observable { - const doneEndpoint = this.combinedUrl; + private sendHttpRequest(method: 'get' | 'post' | 'delete' | 'put', request: RequestData): Observable { + const doneEndpoint = this.combinedUrl(request); - return this.tokenRefreshService.getTokenRefreshing$().pipe( - filter(isRefreshing => !isRefreshing), - switchMap(() => - this.http.request(method, doneEndpoint, { - withCredentials: this.request.withCredentials, - headers: this.request.httpHeaders, - body: this.request.data - }).pipe( - tap(_ => this.request = RequestBuilder.getStandardRequestData()), - retryWithInterval(), - catchError(error => { - if (!this.request.silenceMode) - this.handleError(error); + return this.http.request(method, doneEndpoint, { + withCredentials: request.withCredentials, + headers: request.httpHeaders, + body: request.data + }).pipe( + retryWithInterval(), + catchError(error => { + if (!request.silenceMode) + this.handleError(error); - this.request = RequestBuilder.getStandardRequestData(); - throw error; - }) - ) - ) + throw error; + }) ); } + private makeHttpRequest(method: 'get' | 'post' | 'delete' | 'put', request: RequestData): Observable { + + console.log(request); + if (request.needAuth) + return this.tokenRefreshService.getTokenRefreshing$().pipe( + filter(isRefreshing => !isRefreshing), + switchMap(() => { + console.log("I'm here!"); + const token = localStorage.getItem(ApiService.tokenKey); + + if (token) { + const authToken = AuthToken.httpHeader((JSON.parse(token) as AuthToken)); + authToken.keys().forEach(key => request.httpHeaders = request.httpHeaders.append(key, authToken.get(key) ?? '')); + } + + return this.sendHttpRequest(method, request); + }) + ); + + return this.sendHttpRequest(method, request); + } + + private getRequest(request: RequestData | string | null): RequestData { + if (request === null) + return this.createRequestBuilder().build; + if (typeof request === 'string') + return this.createRequestBuilder().setEndpoint(request as string).build; + + return request as RequestData; + } + public createRequestBuilder() { - this.request = RequestBuilder.getStandardRequestData(); - return new RequestBuilder(this); + return new RequestBuilder(); } - public get(endpoint: string = ''): Observable { - if (endpoint) - this.request.endpoint = endpoint; - return this.makeHttpRequest('get'); + public get(request: RequestData | string | null = null): Observable { + return this.makeHttpRequest('get', this.getRequest(request)); } - public post(endpoint: string = ''): Observable { - if (endpoint) - this.request.endpoint = endpoint; - return this.makeHttpRequest('post'); + public post(request: RequestData | string | null = null): Observable { + return this.makeHttpRequest('post', this.getRequest(request)); } - public put(endpoint: string = ''): Observable { - if (endpoint) - this.request.endpoint = endpoint; - return this.makeHttpRequest('put'); + public put(request: RequestData | string | null = null): Observable { + return this.makeHttpRequest('put', this.getRequest(request)); } - public delete(endpoint: string = ''): Observable { - if (endpoint) - this.request.endpoint = endpoint; - return this.makeHttpRequest('delete'); + public delete(request: RequestData | string | null = null): Observable { + return this.makeHttpRequest('delete', this.getRequest(request)); } - public addAuth() { - const token = localStorage.getItem(ApiService.tokenKey); - - if (!token) - return this; - - const authToken = AuthToken.httpHeader((JSON.parse(token) as AuthToken)); - authToken.keys().forEach(key => this.request.httpHeaders = this.request.httpHeaders.append(key, authToken.get(key) ?? '')); - + public addAuth(request: RequestData) { + request.needAuth = true; return this; } diff --git a/src/api/v1/authApiService.ts b/src/api/v1/authApiService.ts index 8266118..c47483a 100644 --- a/src/api/v1/authApiService.ts +++ b/src/api/v1/authApiService.ts @@ -12,26 +12,29 @@ export default class AuthApiService extends ApiService { public readonly version = AvailableVersion.v1; public login(login: LoginRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('Login') .setData(login) - .build() - .post() + .setWithCredentials() + .build; + + return this.post(request) .pipe( tap(response => { - AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, this.createRequestBuilder().setEndpoint('ReLogin').build().combinedUrl); - this.tokenRefreshService.startTokenRefresh(response.expiresIn); + AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, this.combinedUrl(this.createRequestBuilder().setEndpoint('ReLogin').build)); + this.tokenRefreshService.setRefreshTokenExpireMs(response.expiresIn); }) ); } public logout() { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setWithCredentials() .setEndpoint('Logout') - .build() - .addAuth() - .get() + .build; + + return this.addAuth(request) + .get(request) .pipe( tap(_ => { localStorage.removeItem(ApiService.tokenKey); @@ -40,11 +43,13 @@ export default class AuthApiService extends ApiService { } public getRole(isSilence: boolean = true) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setSilenceMode(isSilence) - .build() - .addAuth() - .get('GetRole') + .setEndpoint('GetRole') + .build; + + return this.addAuth(request) + .get(request) .pipe( catchError(_ => { return of(null); diff --git a/src/api/v1/discipline.service.ts b/src/api/v1/discipline.service.ts index e929f71..3585fdc 100644 --- a/src/api/v1/discipline.service.ts +++ b/src/api/v1/discipline.service.ts @@ -8,10 +8,11 @@ export class DisciplineService extends ApiService { public readonly version = AvailableVersion.v1; public getDisciplines(page: number | null = null, pageSize: number | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setQueryParams({page: page, pageSize: pageSize}) - .build() - .get(); + .build; + + return this.get(request); } public getById(id: number) { diff --git a/src/api/v1/faculty.service.ts b/src/api/v1/faculty.service.ts index 0ce1902..013a085 100644 --- a/src/api/v1/faculty.service.ts +++ b/src/api/v1/faculty.service.ts @@ -9,10 +9,11 @@ export class FacultyService extends ApiService { public readonly version = AvailableVersion.v1; public getFaculties(page: number | null = null, pageSize: number | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setQueryParams({page: page, pageSize: pageSize}) - .build() - .get(); + .build; + + return this.get(request); } public getById(id: number) { diff --git a/src/api/v1/group.service.ts b/src/api/v1/group.service.ts index 4c634be..b38e19d 100644 --- a/src/api/v1/group.service.ts +++ b/src/api/v1/group.service.ts @@ -9,10 +9,11 @@ export class GroupService extends ApiService { public readonly version = AvailableVersion.v1; public getGroups(page: number | null = null, pageSize: number | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setQueryParams({page: page, pageSize: pageSize}) - .build() - .get(); + .build; + + return this.get(request); } public getById(id: number) { diff --git a/src/api/v1/professor.service.ts b/src/api/v1/professor.service.ts index 5c3717f..dfaed67 100644 --- a/src/api/v1/professor.service.ts +++ b/src/api/v1/professor.service.ts @@ -8,10 +8,11 @@ export class ProfessorService extends ApiService { public readonly version = AvailableVersion.v1; public getProfessors(page: number | null = null, pageSize: number | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setQueryParams({page: page, pageSize: pageSize}) - .build() - .get(); + .build; + + return this.get(request); } public getById(id: number) { diff --git a/src/api/v1/schedule.service.ts b/src/api/v1/schedule.service.ts index ffb6a3e..ba35a54 100644 --- a/src/api/v1/schedule.service.ts +++ b/src/api/v1/schedule.service.ts @@ -20,41 +20,46 @@ export class ScheduleService extends ApiService { } public postSchedule(data: ScheduleRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setData(data) - .build() - .post(); + .build; + + return this.post(request); } public getByGroup(id: number, isEven: boolean | null = null, disciplines: Array | null = null, professors: Array | null = null, lectureHalls: Array | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('GetByGroup/' + id.toString()) .setQueryParams({isEven: isEven, disciplines: disciplines, professors: professors, lectureHalls: lectureHalls}) - .build() - .get(); + .build; + + return this.get(request); } public getByProfessor(id: number, isEven: boolean | null = null, disciplines: Array | null = null, groups: Array | null = null, lectureHalls: Array | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('GetByProfessor/' + id.toString()) .setQueryParams({isEven: isEven, disciplines: disciplines, groups: groups, lectureHalls: lectureHalls}) - .build() - .get(); + .build; + + return this.get(request); } public getByLectureHall(id: number, isEven: boolean | null = null, disciplines: Array | null = null, groups: Array | null = null, professors: Array | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('GetByLectureHall/' + id.toString()) .setQueryParams({isEven: isEven, disciplines: disciplines, groups: groups, professors: professors}) - .build() - .get(); + .build; + + return this.get(request); } public getByDiscipline(id: number, isEven: boolean | null = null, groups: Array | null = null, professors: Array | null = null, lectureHalls: Array | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('GetByDiscipline/' + id.toString()) .setQueryParams({isEven: isEven, groups: groups, professors: professors, lectureHalls: lectureHalls}) - .build() - .get(); + .build; + + return this.get(request); } } diff --git a/src/api/v1/setup.service.ts b/src/api/v1/setup.service.ts index f581c7e..b2755a1 100644 --- a/src/api/v1/setup.service.ts +++ b/src/api/v1/setup.service.ts @@ -14,101 +14,112 @@ export default class SetupService extends ApiService { public readonly version = AvailableVersion.v1; public checkToken(token: string) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('CheckToken') .setQueryParams({token: token}) - .build() - .get(); + .build; + + return this.get(request); } public setPsql(data: DatabaseRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetPsql') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setMysql(data: DatabaseRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetMysql') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setSqlite(path: string | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetSqlite') .setQueryParams({path: path}) .setWithCredentials() - .build() - .get(); + .build; + + return this.get(request); } public setRedis(data: CacheRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetRedis') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setMemcached() { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetMemcached') .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public createAdmin(data: CreateUserRequest) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('CreateAdmin') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setLogging(data: LoggingRequest | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetLogging') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setEmail(data: EmailRequest | null = null) { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetEmail') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public setSchedule(data: ScheduleConfigurationRequest) { data.startTerm = new DateOnly(data.startTerm).toString(); - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('SetSchedule') .setData(data) .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public submit() { - return this.createRequestBuilder() + let request = this.createRequestBuilder() .setEndpoint('Submit') .setWithCredentials() - .build() - .post(); + .build; + + return this.post(request); } public isConfigured() {