diff --git a/src/symbols/exists.ts b/src/symbols/exists.ts deleted file mode 100644 index 13a2a4e..0000000 --- a/src/symbols/exists.ts +++ /dev/null @@ -1,11 +0,0 @@ -import fs from 'fs'; - -export function exists(file: string): boolean { - try { - fs.accessSync(file); - } catch { - return false; - } - - return true; -} \ No newline at end of file diff --git a/src/symbols/s3-api-client/s3-api-client.spec.ts b/src/symbols/s3-api-client/s3-api-client.spec.ts index 8923b02..f150428 100644 --- a/src/symbols/s3-api-client/s3-api-client.spec.ts +++ b/src/symbols/s3-api-client/s3-api-client.spec.ts @@ -1,46 +1,34 @@ -import * as ExistsModule from '../exists'; import { S3ApiClient } from './s3-api-client'; describe('S3ApiClient', () => { describe('uploadFileToPresignedUrl', () => { - let s3ApiClient; - let fakeReadStream; + let s3ApiClient: S3ApiClient; let fakeSuccessResponse; beforeEach(() => { - fakeReadStream = '🦦'; fakeSuccessResponse = { status: 200 }; - spyOn(ExistsModule, 'exists').and.returnValue(true); s3ApiClient = new S3ApiClient(); - s3ApiClient._fs = jasmine.createSpyObj('fs', ['createReadStream']); - s3ApiClient._fs.createReadStream.and.returnValue(fakeReadStream); - s3ApiClient._fetch = jasmine.createSpy(); - s3ApiClient._fetch.and.resolveTo(fakeSuccessResponse); - }); - - it('should throw if file does not exist', async () => { - const path = '/file/not/found'; - (ExistsModule.exists).and.returnValue(false); - await expectAsync(s3ApiClient.uploadFileToPresignedUrl('url', path)).toBeRejectedWithError(`File does not exist at path: ${path}!`); + (s3ApiClient as any)._fetch = jasmine.createSpy(); + (s3ApiClient as any)._fetch.and.resolveTo(fakeSuccessResponse); }); it('should call fetch with presignedUrl', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; - await s3ApiClient.uploadFileToPresignedUrl(url, path); + await s3ApiClient.uploadFileToPresignedUrl(url, file, 1); - expect(s3ApiClient._fetch).toHaveBeenCalledWith(url, jasmine.anything()); + expect((s3ApiClient as any)._fetch).toHaveBeenCalledWith(url, jasmine.anything()); }); it('should call fetch with init containing method and headers', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - await s3ApiClient.uploadFileToPresignedUrl(url, path, size); + await s3ApiClient.uploadFileToPresignedUrl(url, file, size); - expect(s3ApiClient._fetch).toHaveBeenCalledWith( + expect((s3ApiClient as any)._fetch).toHaveBeenCalledWith( jasmine.anything(), jasmine.objectContaining({ method: 'PUT', @@ -48,17 +36,17 @@ describe('S3ApiClient', () => { 'content-type': 'application/octet-stream', 'content-length': `${size}` }, - body: fakeReadStream + body: file }) ); }); it('should return response', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - const response = await s3ApiClient.uploadFileToPresignedUrl(url, path, size); + const response = await s3ApiClient.uploadFileToPresignedUrl(url, file, size); expect(response).toEqual(fakeSuccessResponse); }); @@ -66,11 +54,11 @@ describe('S3ApiClient', () => { describe('error', () => { it('should throw if response status is not 200', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - s3ApiClient._fetch.and.resolveTo({ status: 500 }); + (s3ApiClient as any)._fetch.and.resolveTo({ status: 500 }); - await expectAsync(s3ApiClient.uploadFileToPresignedUrl(url, path, size)).toBeRejectedWithError(`Error uploading ${path} to presignedUrl`); + await expectAsync(s3ApiClient.uploadFileToPresignedUrl(url, file, size)).toBeRejectedWithError(`Error uploading to presignedUrl ${url}`); }); }); }); diff --git a/src/symbols/s3-api-client/s3-api-client.ts b/src/symbols/s3-api-client/s3-api-client.ts index fe680e8..006a828 100644 --- a/src/symbols/s3-api-client/s3-api-client.ts +++ b/src/symbols/s3-api-client/s3-api-client.ts @@ -1,28 +1,21 @@ import fetchPonyfill from 'fetch-ponyfill'; -import fs from 'fs'; -import { exists } from '../exists'; +import type fs from 'fs'; export class S3ApiClient { private _fetch = fetchPonyfill().fetch; - private _fs = fs; - - async uploadFileToPresignedUrl(presignedUrl: string, file: string, size: number): Promise { - if (!exists(file)) { - throw new Error(`File does not exist at path: ${file}!`); - } - + async uploadFileToPresignedUrl(presignedUrl: string, file: File | fs.ReadStream, size: number): Promise { const response = await this._fetch(presignedUrl, { method: 'PUT', headers: { 'content-type': 'application/octet-stream', 'content-length': `${size}` }, - body: this._fs.createReadStream(file), + body: file as BodyInit }); if (response.status !== 200) { - throw new Error(`Error uploading ${file} to presignedUrl`); + throw new Error(`Error uploading to presignedUrl ${presignedUrl}`); } return response; diff --git a/src/symbols/symbols-api-client/symbols-api-client.e2e.ts b/src/symbols/symbols-api-client/symbols-api-client.e2e.ts index b08c461..aa30449 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.e2e.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.e2e.ts @@ -1,3 +1,5 @@ +import fs from "fs"; +import path from 'path'; import { config } from "../../../spec/config"; import { BugSplatApiClient } from "../../common"; import { SymbolsApiClient } from "./symbols-api-client"; @@ -32,7 +34,14 @@ describe('SymbolsApiClient', () => { describe('post', () => { it('should return 200 for post with valid database, application, version and files', async () => { - const file = './dist/cjs/index.js.map'; + const filePath = './dist/cjs/index.js.map'; + const name = path.basename(filePath); + const size = fs.statSync(filePath).size; + const file = { + name, + size, + file: fs.createReadStream(filePath) + }; const response = await client.post( database, application, diff --git a/src/symbols/symbols-api-client/symbols-api-client.spec.ts b/src/symbols/symbols-api-client/symbols-api-client.spec.ts index a30190e..c8e6d74 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.spec.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.spec.ts @@ -3,7 +3,6 @@ import path from 'path'; import { createFakeBugSplatApiClient } from '../../../spec/fakes/common/bugsplat-api-client'; import { createFakeFormData } from '../../../spec/fakes/common/form-data'; import { createFakeResponseBody } from '../../../spec/fakes/common/response'; -import * as ExistsModule from '../exists'; import { SymbolsApiClient } from './symbols-api-client'; import * as S3ApiClientModule from '../s3-api-client/s3-api-client'; import { of } from 'rxjs'; @@ -87,22 +86,17 @@ describe('SymbolsApiClient', () => { let fakeS3ApiClient; let files; let result; - let stat; - let size; let timer; beforeEach(async () => { - files = ['file/📄']; - size = 1337; - stat = jasmine.createSpy(); - stat.and.returnValue({ size }); + files = [{ + name: '📄.sym', + size: 1337 + }]; timer = jasmine.createSpy(); timer.and.returnValue(of(0)); - (symbolsApiClient)._stat = stat; (symbolsApiClient)._timer = timer; - spyOn(ExistsModule, 'exists').and.returnValue(true); - fakeS3ApiClient = jasmine.createSpyObj('S3ApiClient', ['uploadFileToPresignedUrl']); fakeS3ApiClient.uploadFileToPresignedUrl.and.resolveTo(fakeSuccessResponse); spyOn(S3ApiClientModule, 'S3ApiClient').and.returnValue(fakeS3ApiClient); @@ -115,23 +109,12 @@ describe('SymbolsApiClient', () => { ); }); - it('should throw if file does not exist', async () => { - (ExistsModule.exists).and.returnValue(false); - - await expectAsync(symbolsApiClient.post( - database, - application, - version, - files - )).toBeRejectedWithError(`File does not exist at path: ${files[0]}!`); - }); - it('should append dbName, appName, appVersion, size and symFileName to FormData', () => { expect(fakeFormData.append).toHaveBeenCalledWith('dbName', database); expect(fakeFormData.append).toHaveBeenCalledWith('appName', application); expect(fakeFormData.append).toHaveBeenCalledWith('appVersion', version); - expect(fakeFormData.append).toHaveBeenCalledWith('size', size.toString()); - expect(fakeFormData.append).toHaveBeenCalledWith('symFileName', path.basename(files[0])); + expect(fakeFormData.append).toHaveBeenCalledWith('size', files[0].size.toString()); + expect(fakeFormData.append).toHaveBeenCalledWith('symFileName', path.basename(files[0].name)); }); it('should call fetch with correct route', () => { @@ -153,7 +136,7 @@ describe('SymbolsApiClient', () => { }); it('should call uploadFileToPresignedUrl with url, file and size', () => { - expect(fakeS3ApiClient.uploadFileToPresignedUrl).toHaveBeenCalledWith(url, files[0], size); + expect(fakeS3ApiClient.uploadFileToPresignedUrl).toHaveBeenCalledWith(url, files[0].file, files[0].size); }); it('should sleep between requests', () => { @@ -176,7 +159,7 @@ describe('SymbolsApiClient', () => { application, version, files - )).toBeRejectedWithError(`Error getting presignedUrl for ${path.basename(files[0])}`); + )).toBeRejectedWithError(`Error getting presignedUrl for ${files[0].name}`); }); it('should throw if response json Status is \'Failed\'', async () => { diff --git a/src/symbols/symbols-api-client/symbols-api-client.ts b/src/symbols/symbols-api-client/symbols-api-client.ts index 565af5a..04cd75b 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.ts @@ -1,16 +1,12 @@ -import fs from 'fs'; -import path from 'path'; +import type fs from 'fs'; import { lastValueFrom, timer } from 'rxjs'; -import util from 'util'; import { ApiClient, BugSplatResponse } from '../../common'; -import { exists } from '../exists'; import { S3ApiClient } from '../s3-api-client/s3-api-client'; -const stat = util.promisify(fs.stat); export class SymbolsApiClient { private readonly route = '/api/symbols'; - private _stat = stat; + private _timer = timer; constructor(private _client: ApiClient) { } @@ -45,17 +41,12 @@ export class SymbolsApiClient { database: string, application: string, version: string, - files: Array + files: Array<{ name: string, size: number, file: File | fs.ReadStream }> ): Promise> { const promises = files .map(async (file) => { - if (!exists(file)) { - throw new Error(`File does not exist at path: ${file}!`); - } - - const stats = await this._stat(file); - const size = stats.size; - const name = path.basename(file); + const name = file.name; + const size = file.size; const presignedUrl = await this.getPresignedUrl( database, application, @@ -65,7 +56,7 @@ export class SymbolsApiClient { ); const s3Client = new S3ApiClient(); - const response = s3Client.uploadFileToPresignedUrl(presignedUrl, file, size); + const response = s3Client.uploadFileToPresignedUrl(presignedUrl, file.file, size); await lastValueFrom(this._timer(1000)); return response;