From 8c72123c55e1efb447fac33a5726b19f797016df Mon Sep 17 00:00:00 2001 From: Bobby Galli Date: Wed, 16 Oct 2024 22:05:53 +0000 Subject: [PATCH] feat: delete crashes (#131) * feat: delete crashes * fix: post notes --- .../table-data-client/table-data-client.ts | 51 +++++--- .../crashes-api-client.spec.ts | 2 +- .../crashes-api-client/crashes-api-client.ts | 113 +++++++++++------- 3 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/common/data/table-data/table-data-client/table-data-client.ts b/src/common/data/table-data/table-data-client/table-data-client.ts index 87c93e5..ea91dd2 100644 --- a/src/common/data/table-data/table-data-client/table-data-client.ts +++ b/src/common/data/table-data/table-data-client/table-data-client.ts @@ -1,12 +1,19 @@ -import { ApiClient, TableDataRequest, BugSplatResponse, TableDataResponse } from '@common'; +import { + ApiClient, + TableDataRequest, + BugSplatResponse, + TableDataResponse, +} from '@common'; import { TableDataFormDataBuilder } from '../table-data-form-data-builder/table-data-form-data-builder'; export class TableDataClient { - - constructor(private _apiClient: ApiClient, private _url: string) { } + constructor(private _apiClient: ApiClient, private _url: string) {} // We use POST to get data in most cases because it supports longer queries - async postGetData | undefined)>(request: TableDataRequest, formParts: Record = {}): Promise>> { + async postGetData | undefined>( + request: TableDataRequest, + formParts: Record = {} + ): Promise>> { const factory = () => this._apiClient.createFormData(); const formData = new TableDataFormDataBuilder(factory, formParts) .withDatabase(request.database) @@ -23,12 +30,14 @@ export class TableDataClient { cache: 'no-cache', credentials: 'include', redirect: 'follow', - duplex: 'half' + duplex: 'half', } as RequestInit; return this.makeRequest(this._url, requestInit); } - async getData | undefined)>(request: TableDataRequest): Promise>> { + async getData | undefined>( + request: TableDataRequest + ): Promise>> { const factory = () => this._apiClient.createFormData(); const formData = new TableDataFormDataBuilder(factory) .withDatabase(request.database) @@ -43,17 +52,30 @@ export class TableDataClient { method: 'GET', cache: 'no-cache', credentials: 'include', - redirect: 'follow' + redirect: 'follow', } as RequestInit; const queryParams = new URLSearchParams(formData).toString(); return this.makeRequest(`${this._url}?${queryParams}`, requestInit); } - private async makeRequest(url: string, init: RequestInit): Promise>> { - const response = await this._apiClient.fetch>>(url, init); + private async makeRequest( + url: string, + init: RequestInit + ): Promise>> { + const response = await this._apiClient.fetch< + RawResponse> | TableDataResponse + >(url, init); const responseData = await response.json(); - const rows = responseData ? responseData[0]?.Rows : []; - const pageData = responseData ? responseData[0]?.PageData : {}; + + // Handle legacy and new API responses until we can upgrade legacy APIs + const rows = + (responseData as TableDataResponse)?.rows ?? + responseData?.[0]?.Rows ?? + []; + const pageData = + (responseData as TableDataResponse)?.pageData ?? + responseData?.[0]?.PageData ?? + {}; const status = response.status; const body = response.body; @@ -64,13 +86,12 @@ export class TableDataClient { status, body, json, - text + text, }; } } // https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#key-remapping-via-as export type RawResponse = Array<{ - [Property in keyof T as Capitalize]: T[Property] -}> - + [Property in keyof T as Capitalize]: T[Property]; +}>; diff --git a/src/crashes/crashes-api-client/crashes-api-client.spec.ts b/src/crashes/crashes-api-client/crashes-api-client.spec.ts index d88f230..c7f69cb 100644 --- a/src/crashes/crashes-api-client/crashes-api-client.spec.ts +++ b/src/crashes/crashes-api-client/crashes-api-client.spec.ts @@ -76,7 +76,7 @@ describe('CrashesApiClient', () => { }); it('should call fetch with correct route', () => { - expect(apiClient.fetch).toHaveBeenCalledWith('/allcrash?data', jasmine.anything()); + expect(apiClient.fetch).toHaveBeenCalledWith('/browse/allcrash.php', jasmine.anything()); }); it('should call fetch with requestInit containing formData', () => { diff --git a/src/crashes/crashes-api-client/crashes-api-client.ts b/src/crashes/crashes-api-client/crashes-api-client.ts index 22e8b91..9ca8083 100644 --- a/src/crashes/crashes-api-client/crashes-api-client.ts +++ b/src/crashes/crashes-api-client/crashes-api-client.ts @@ -1,47 +1,76 @@ -import { ApiClient, BugSplatResponse, TableDataClient, TableDataRequest, TableDataResponse } from '@common'; +import { + ApiClient, + BugSplatResponse, + TableDataClient, + TableDataRequest, + TableDataResponse, +} from '@common'; import { CrashesApiRow, CrashesPageData } from '@crashes'; import { CrashesApiResponseRow } from '../crashes-api-row/crashes-api-row'; export class CrashesApiClient { + private _tableDataClient: TableDataClient; - private _tableDataClient: TableDataClient; - - constructor(private _client: ApiClient) { - this._tableDataClient = new TableDataClient(this._client, '/allcrash?data'); - } - - async getCrashes(request: TableDataRequest): Promise> { - const response = await this._tableDataClient.postGetData(request); - const json = await response.json(); - const pageData = json.pageData; - const rows = json.rows.map(row => new CrashesApiRow(row)); - - return { - rows, - pageData - }; - } - - postNotes( - database: string, - id: number, - notes: string - ): Promise { - const formData = this._client.createFormData(); - formData.append('update', 'true'); - formData.append('database', database); - formData.append('id', `${id}`); - formData.append('Comments', notes); - - const request = { - method: 'POST', - body: formData, - cache: 'no-cache', - credentials: 'include', - redirect: 'follow', - duplex: 'half' - } as RequestInit; - - return this._client.fetch('/allcrash?data', request); - } -} \ No newline at end of file + constructor(private _client: ApiClient) { + this._tableDataClient = new TableDataClient( + this._client, + '/api/crashes.php' + ); + } + + async deleteCrashes( + database: string, + ids: number[] + ): Promise { + const urlParams = new URLSearchParams({ database, ids: ids.join(',') }); + const request = { + method: 'DELETE', + cache: 'no-cache', + credentials: 'include', + redirect: 'follow', + duplex: 'half', + } as RequestInit; + return this._client.fetch(`/api/crashes.php?${urlParams}`, request); + } + + async getCrashes( + request: TableDataRequest + ): Promise> { + const response = await this._tableDataClient.postGetData< + CrashesApiResponseRow, + CrashesPageData + >(request); + const json = await response.json(); + const pageData = json.pageData; + const rows = json.rows.map((row) => new CrashesApiRow(row)); + + return { + rows, + pageData, + }; + } + + // TODO BG we should move this to an api/crash/notes endpoint and reture allcrash + postNotes( + database: string, + id: number, + notes: string + ): Promise { + const formData = this._client.createFormData(); + formData.append('update', 'true'); + formData.append('database', database); + formData.append('id', `${id}`); + formData.append('Comments', notes); + + const request = { + method: 'POST', + body: formData, + cache: 'no-cache', + credentials: 'include', + redirect: 'follow', + duplex: 'half', + } as RequestInit; + + return this._client.fetch('/browse/allcrash.php', request); + } +}