Skip to content

Commit 22983b0

Browse files
committed
Throw instead of swallow exceptions to allow withOnlineGuard to handle
1 parent e5b950e commit 22983b0

File tree

4 files changed

+50
-43
lines changed

4 files changed

+50
-43
lines changed

src/services/OnlineStatus.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { lookup } from 'node:dns/promises';
22
import { MessageType } from 'vscode-languageserver-protocol';
33
import { ClientMessage } from '../telemetry/ClientMessage';
4-
import { LoggerFactory } from '../telemetry/LoggerFactory';
54
import { Closeable } from '../utils/Closeable';
65

76
export class OnlineStatus implements Closeable {
8-
private static readonly log = LoggerFactory.getLogger(OnlineStatus);
9-
107
private _isOnline: boolean = false;
118
private notifiedOnce: boolean = false;
129
private readonly timeout: NodeJS.Timeout;
@@ -22,8 +19,13 @@ export class OnlineStatus implements Closeable {
2219
);
2320
}
2421

25-
get isOnline() {
26-
return this._isOnline;
22+
public async checkNow(): Promise<boolean> {
23+
try {
24+
await lookup('google.com');
25+
return true;
26+
} catch {
27+
return false;
28+
}
2729
}
2830

2931
private async hasInternet() {

src/utils/OnlineFeatureGuard.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ export class OnlineFeatureGuard {
1313
private readonly awsCredentials: AwsCredentials,
1414
) {}
1515

16-
check(prerequisites: OnlinePrerequisites): void {
17-
if (prerequisites.requiresInternet && !this.onlineStatus.isOnline) {
18-
throw createOnlineFeatureError(
19-
OnlineFeatureErrorCode.NoInternet,
20-
'Internet connection required for this operation. Please check your network connection.',
21-
{ retryable: true, requiresReauth: false },
22-
);
16+
async check(prerequisites: OnlinePrerequisites): Promise<void> {
17+
if (prerequisites.requiresInternet) {
18+
const isOnline = await this.onlineStatus.checkNow();
19+
if (!isOnline) {
20+
throw createOnlineFeatureError(
21+
OnlineFeatureErrorCode.NoInternet,
22+
'Internet connection required for this operation. Please check your network connection.',
23+
{ retryable: true, requiresReauth: false },
24+
);
25+
}
2326
}
2427

2528
if (prerequisites.requiresAuth && !this.awsCredentials.credentialsAvailable()) {

src/utils/OnlineFeatureWrapper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function withOnlineGuard<T extends Handler>(
1919
prerequisites: Partial<OnlinePrerequisites> = {},
2020
): T {
2121
return (async (...args: any[]) => {
22-
guard.check({ ...DEFAULT_PREREQUISITES, ...prerequisites });
22+
await guard.check({ ...DEFAULT_PREREQUISITES, ...prerequisites });
2323
try {
2424
return await handler(...args);
2525
} catch (error) {

tst/unit/utils/OnlineFeatureGuard.test.ts

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,60 @@
11
import { describe, expect, it } from 'vitest';
2+
import { AwsCredentials } from '../../../src/auth/AwsCredentials';
3+
import { OnlineStatus } from '../../../src/services/OnlineStatus';
24
import { OnlineFeatureErrorCode } from '../../../src/utils/OnlineFeatureError';
35
import { OnlineFeatureGuard } from '../../../src/utils/OnlineFeatureGuard';
46

57
describe('OnlineFeatureGuard', () => {
6-
it('should pass when both internet and auth are available', () => {
7-
const onlineStatus = { isOnline: true };
8-
const awsCredentials = { credentialsAvailable: () => true };
9-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
8+
it('should pass when both internet and auth are available', async () => {
9+
const onlineStatus = { checkNow: () => Promise.resolve(true) } as OnlineStatus;
10+
const awsCredentials = { credentialsAvailable: () => true } as AwsCredentials;
11+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
1012

11-
expect(() => guard.check({ requiresInternet: true, requiresAuth: true })).not.toThrow();
13+
await expect(guard.check({ requiresInternet: true, requiresAuth: true })).resolves.not.toThrow();
1214
});
1315

14-
it('should throw NoInternet when internet is required but not available', () => {
15-
const onlineStatus = { isOnline: false };
16-
const awsCredentials = { credentialsAvailable: () => true };
17-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
16+
it('should throw NoInternet when internet is required but not available', async () => {
17+
const onlineStatus = { checkNow: () => Promise.resolve(false) } as OnlineStatus;
18+
const awsCredentials = { credentialsAvailable: () => true } as AwsCredentials;
19+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
1820

19-
expect(() => guard.check({ requiresInternet: true, requiresAuth: true })).toThrow(
21+
await expect(guard.check({ requiresInternet: true, requiresAuth: true })).rejects.toThrow(
2022
expect.objectContaining({ code: OnlineFeatureErrorCode.NoInternet }),
2123
);
2224
});
2325

24-
it('should throw NoAuthentication when auth is required but not available', () => {
25-
const onlineStatus = { isOnline: true };
26-
const awsCredentials = { credentialsAvailable: () => false };
27-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
26+
it('should throw NoAuthentication when auth is required but not available', async () => {
27+
const onlineStatus = { checkNow: () => Promise.resolve(true) } as OnlineStatus;
28+
const awsCredentials = { credentialsAvailable: () => false } as AwsCredentials;
29+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
2830

29-
expect(() => guard.check({ requiresInternet: true, requiresAuth: true })).toThrow(
31+
await expect(guard.check({ requiresInternet: true, requiresAuth: true })).rejects.toThrow(
3032
expect.objectContaining({ code: OnlineFeatureErrorCode.NoAuthentication }),
3133
);
3234
});
3335

34-
it('should pass when internet is not required and not available', () => {
35-
const onlineStatus = { isOnline: false };
36-
const awsCredentials = { credentialsAvailable: () => true };
37-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
36+
it('should pass when internet is not required and not available', async () => {
37+
const onlineStatus = { checkNow: () => Promise.resolve(false) } as OnlineStatus;
38+
const awsCredentials = { credentialsAvailable: () => true } as AwsCredentials;
39+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
3840

39-
expect(() => guard.check({ requiresInternet: false, requiresAuth: true })).not.toThrow();
41+
await expect(guard.check({ requiresInternet: false, requiresAuth: true })).resolves.not.toThrow();
4042
});
4143

42-
it('should pass when auth is not required and not available', () => {
43-
const onlineStatus = { isOnline: true };
44-
const awsCredentials = { credentialsAvailable: () => false };
45-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
44+
it('should pass when auth is not required and not available', async () => {
45+
const onlineStatus = { checkNow: () => Promise.resolve(true) } as OnlineStatus;
46+
const awsCredentials = { credentialsAvailable: () => false } as AwsCredentials;
47+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
4648

47-
expect(() => guard.check({ requiresInternet: true, requiresAuth: false })).not.toThrow();
49+
await expect(guard.check({ requiresInternet: true, requiresAuth: false })).resolves.not.toThrow();
4850
});
4951

50-
it('should check internet before auth', () => {
51-
const onlineStatus = { isOnline: false };
52-
const awsCredentials = { credentialsAvailable: () => false };
53-
const guard = new OnlineFeatureGuard(onlineStatus as any, awsCredentials as any);
52+
it('should check internet before auth', async () => {
53+
const onlineStatus = { checkNow: () => Promise.resolve(false) } as OnlineStatus;
54+
const awsCredentials = { credentialsAvailable: () => false } as AwsCredentials;
55+
const guard = new OnlineFeatureGuard(onlineStatus, awsCredentials);
5456

55-
expect(() => guard.check({ requiresInternet: true, requiresAuth: true })).toThrow(
57+
await expect(guard.check({ requiresInternet: true, requiresAuth: true })).rejects.toThrow(
5658
expect.objectContaining({ code: OnlineFeatureErrorCode.NoInternet }),
5759
);
5860
});

0 commit comments

Comments
 (0)