Skip to content

Commit ea3af3c

Browse files
authored
fix: handle missing additional info (#122)
* fix: handle missing additional info * chore: remove rxjs dependency * fix: build paths
1 parent 6afb88f commit ea3af3c

File tree

12 files changed

+61
-78
lines changed

12 files changed

+61
-78
lines changed

package-lock.json

Lines changed: 1 addition & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@
5050
]
5151
},
5252
"dependencies": {
53-
"argument-contracts": "^1.2.3",
54-
"rxjs": "^7.1.0"
53+
"argument-contracts": "^1.2.3"
5554
},
5655
"devDependencies": {
5756
"@commitlint/cli": "^17.6.1",

spec/files/native/post-native-crash.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { BugSplatApiClient } from '@common';
22
import { CrashApiClient } from '@crash';
33
import { CrashPostClient, CrashType } from '@post';
44
import { VersionsApiClient } from '@versions';
5-
import { firstValueFrom, timer } from 'rxjs';
65
import { PostCrashResponse } from 'src/post/post-crash-response';
76
import { createUploadableFile } from '../create-bugsplat-file';
87
import { createSymbolFile } from '../create-symbol-file';
8+
import { delay } from '../../../src/common/delay';
99

1010
export async function postNativeCrashAndSymbols(
1111
authenticatedClient: BugSplatApiClient,
@@ -34,7 +34,7 @@ export async function postNativeCrash(
3434
): Promise<PostCrashResponse> {
3535
const crashFile = await createUploadableFile('./spec/files/native/myConsoleCrasher.zip');
3636
const crashPostClient = new CrashPostClient(database);
37-
await firstValueFrom(timer(2000)); // Prevent rate-limiting
37+
await delay(2000); // Prevent rate-limiting
3838
const postCrashResult = await crashPostClient.postCrash(
3939
application,
4040
version,
@@ -75,7 +75,7 @@ export async function postNativeCrashAndWaitForCrashToProcess(
7575
if (stackKeyId > 0) {
7676
break;
7777
}
78-
await firstValueFrom(timer(3000));
78+
await delay(3000);
7979
}
8080

8181
return {

src/common/delay.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export async function delay(millis: number): Promise<void> {
2+
return new Promise(resolve => setTimeout(resolve, millis));
3+
}

src/crash/additional-info/additional-info.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ describe('AdditionalInfo', () => {
8181

8282
expect(result.threads).toEqual([]);
8383
});
84+
85+
it('should not throw if exception is undefined', () => {
86+
apiResponse.process.exception = undefined;
87+
88+
expect(() => AdditionalInfo.fromRawResponse(apiResponse)).not.toThrow();
89+
});
8490
});
8591
});
8692

src/crash/additional-info/additional-info.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,26 @@ import { ModuleResponseObject } from '../module/module';
44
const ATTRIBUTES = '@attributes';
55

66
export interface AdditionalInfoConstructorOptions {
7-
os: string;
8-
debuggerOutput: string;
9-
registers: Array<Register>;
10-
modules: Array<Module>;
11-
threads: Array<ThreadCollection>;
7+
os?: string;
8+
debuggerOutput?: string;
9+
registers?: Array<Register>;
10+
modules?: Array<Module>;
11+
threads?: Array<ThreadCollection>;
1212
}
1313

1414
export interface AdditionalInfoResponseObject {
1515
os: string;
1616
process: {
17-
DebugOutput: {
17+
DebugOutput?: {
1818
DbgEngOutput: string;
1919
},
20-
exception: {
20+
exception?: {
2121
registers: Record<string, string>;
2222
},
23-
threads: {
23+
threads?: {
2424
thread: ThreadResponseObject | Array<ThreadResponseObject>;
2525
},
26-
modules: {
26+
modules?: {
2727
module: Array<ModuleResponseObject>;
2828
}
2929
};
@@ -45,7 +45,7 @@ export interface ThreadResponseObject {
4545
frame: FrameResponseObject | Array<FrameResponseObject>
4646
}
4747

48-
export class AdditionalInfo implements AdditionalInfoConstructorOptions {
48+
export class AdditionalInfo implements Required<AdditionalInfoConstructorOptions> {
4949
os: string;
5050
debuggerOutput: string;
5151
registers: Array<Register>;
@@ -78,19 +78,19 @@ export class AdditionalInfo implements AdditionalInfoConstructorOptions {
7878
const os = response.os || '';
7979

8080
const debuggerOutput = !isEmpty(response.process.DebugOutput)
81-
? response.process.DebugOutput.DbgEngOutput
81+
? response.process.DebugOutput?.DbgEngOutput
8282
: '';
8383

84-
const registers = !isEmpty(response.process.exception.registers)
85-
? createRegistersArray(response.process.exception.registers)
84+
const registers = !isEmpty(response.process.exception?.registers)
85+
? createRegistersArray(response.process.exception?.registers)
8686
: [];
8787

8888
const threads = !isEmpty(response.process.threads)
89-
? createThreadCollectionArray(response.process.threads.thread)
89+
? createThreadCollectionArray(response.process.threads?.thread)
9090
: [];
9191

9292
const modules = !isEmpty(response.process.modules)
93-
? createModulesArray(response.process.modules.module)
93+
? createModulesArray(response.process.modules?.module)
9494
: [];
9595

9696
return new AdditionalInfo({
@@ -103,7 +103,7 @@ export class AdditionalInfo implements AdditionalInfoConstructorOptions {
103103
}
104104
}
105105

106-
function createModulesArray(modules: Array<ModuleResponseObject>): Array<Module> {
106+
function createModulesArray(modules?: Array<ModuleResponseObject>): Array<Module> {
107107
if (!modules) {
108108
modules = [];
109109
}
@@ -115,11 +115,15 @@ function createModulesArray(modules: Array<ModuleResponseObject>): Array<Module>
115115
return modules.map(module => Module.fromResponseObject(module));
116116
}
117117

118-
function createRegistersArray(registers: Record<string, string>): Array<Register> {
118+
function createRegistersArray(registers?: Record<string, string>): Array<Register> {
119+
if (!registers) {
120+
return [];
121+
}
122+
119123
return Register.fromResponseObject(registers);
120124
}
121125

122-
function createThreadCollectionArray(threads: ThreadResponseObject | Array<ThreadResponseObject>): Array<ThreadCollection> {
126+
function createThreadCollectionArray(threads?: ThreadResponseObject | Array<ThreadResponseObject>): Array<ThreadCollection> {
123127
if (!threads) {
124128
threads = [];
125129
}

src/post/crash-post-client.e2e.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { CrashPostClient, CrashType } from '@post';
22
import { config } from '@spec/config';
33
import { createUploadableFile } from '@spec/files/create-bugsplat-file';
4-
import { firstValueFrom, timer } from 'rxjs';
4+
import { delay } from '../common/delay';
55

66
describe('CrashPostClient', () => {
7-
beforeEach(async () => firstValueFrom(timer(2000))); // Prevent rate-limiting
8-
7+
beforeEach(async () => delay(2000)); // Prevent rate-limiting
8+
99
describe('postCrash', () => {
1010
it('should post crash to BugSplat and return 200', async () => {
1111
const application = 'myConsoleCrasher';

src/summary/summary-api-client/summary-api-client.e2e.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { BugSplatApiClient } from '@common';
22
import { config } from '@spec/config';
33
import { postNativeCrashAndSymbols } from '@spec/files/native/post-native-crash';
4-
import { firstValueFrom, timer } from 'rxjs';
54
import { SummaryApiClient } from './summary-api-client';
5+
import { delay } from '../../common/delay';
66

77
describe('SummaryApiClient', () => {
88
let summaryClient: SummaryApiClient;
@@ -25,7 +25,7 @@ describe('SummaryApiClient', () => {
2525

2626
describe('getSummary', () => {
2727
it('should return 200 and array of stack keys', async () => {
28-
const stackKey = 'myConsoleCrasher!MemoryException(150)';
28+
const stackKey = 'myConsoleCrasher!MemoryException(150)';
2929
const database = config.database;
3030
const applications = [application];
3131
const versions = [version];
@@ -45,9 +45,9 @@ describe('SummaryApiClient', () => {
4545
break;
4646
}
4747

48-
await firstValueFrom(timer(2000));
48+
await delay(2000);
4949
}
50-
50+
5151
const row = result.rows.find(row => row.stackKey === stackKey);
5252
expect(result.rows).toBeTruthy();
5353
expect(result.rows.length).toEqual(pageSize);

src/symbols/symbols-api-client/symbols-api-client.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { ApiClient } from '@common';
22
import { createFakeBugSplatApiClient } from '@spec/fakes/common/bugsplat-api-client';
33
import { createFakeFormData } from '@spec/fakes/common/form-data';
44
import { createFakeResponseBody } from '@spec/fakes/common/response';
5-
import { of } from 'rxjs';
65
import { SymbolsApiClient } from './symbols-api-client';
76

87
describe('SymbolsApiClient', () => {
@@ -35,7 +34,7 @@ describe('SymbolsApiClient', () => {
3534
symbolsApiClient = new SymbolsApiClient(apiClient);
3635

3736
fakeTimer = jasmine.createSpy('timer');
38-
fakeTimer.and.returnValue(of(0));
37+
fakeTimer.and.resolveTo(0);
3938
fakeUploadResponse = createFakeResponseBody(200, { Status: 'Success' });
4039
(symbolsApiClient as any)._s3ApiClient = jasmine.createSpyObj('S3ApiClient', ['uploadFileToPresignedUrl']);
4140
(symbolsApiClient as any)._s3ApiClient.uploadFileToPresignedUrl.and.resolveTo(fakeUploadResponse);

src/symbols/symbols-api-client/symbols-api-client.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { ApiClient, BugSplatResponse, GZippedSymbolFile, S3ApiClient } from '@common';
2-
import { lastValueFrom, timer } from 'rxjs';
2+
import { delay } from '../../common/delay';
33

44
export class SymbolsApiClient {
55
private readonly uploadUrl = '/symsrv/uploadUrl';
66
private readonly uploadCompleteUrl = '/symsrv/uploadComplete';
77
private _s3ApiClient = new S3ApiClient();
8-
private _timer = timer;
8+
private _timer = delay;
99

1010
constructor(private _client: ApiClient) { }
1111

@@ -15,7 +15,7 @@ export class SymbolsApiClient {
1515
database: string,
1616
application: string,
1717
version: string,
18-
files: Array<GZippedSymbolFile>
18+
files: Array<GZippedSymbolFile>
1919
): Promise<Array<BugSplatResponse>> {
2020
const promises = files
2121
.map(async (file) => {
@@ -40,21 +40,21 @@ export class SymbolsApiClient {
4040
const additionalHeaders = {
4141
'content-encoding': 'gzip'
4242
};
43-
43+
4444
const uploadResponse = await this._s3ApiClient.uploadFileToPresignedUrl(presignedUrl, file, additionalHeaders);
45-
45+
4646
await this.postUploadComplete(
4747
database,
4848
application,
4949
version,
5050
file
5151
);
52-
53-
await lastValueFrom(this._timer(1000));
52+
53+
await this._timer(1000);
5454

5555
return uploadResponse;
5656
});
57-
57+
5858
return Promise.all(promises);
5959
}
6060

0 commit comments

Comments
 (0)