Skip to content

Commit b516af6

Browse files
committed
Wrap handlers with online guard at the registration layer
1 parent 5d13c40 commit b516af6

File tree

9 files changed

+229
-135
lines changed

9 files changed

+229
-135
lines changed

src/handlers/ResourceHandler.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { ServerComponents } from '../server/ServerComponents';
2222
import { GetStackTemplateParams, GetStackTemplateResult } from '../stacks/StackRequestType';
2323
import { LoggerFactory } from '../telemetry/LoggerFactory';
2424
import { TelemetryService } from '../telemetry/TelemetryService';
25-
import { withOnlineFeatures } from '../utils/OnlineFeatureWrapper';
25+
import { extractErrorMessage } from '../utils/Errors';
2626
import { parseWithPrettyError } from '../utils/ZodErrorWrapper';
2727

2828
const log = LoggerFactory.getLogger('ResourceHandler');
@@ -52,7 +52,7 @@ export function listResourcesHandler(
5252
components: ServerComponents,
5353
): RequestHandler<ListResourcesParams, ListResourcesResult, void> {
5454
return async (params: ListResourcesParams): Promise<ListResourcesResult> => {
55-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
55+
try {
5656
const resourceRequests = params.resources;
5757
if (!resourceRequests || resourceRequests.length === 0) {
5858
return { resources: [] };
@@ -75,40 +75,44 @@ export function listResourcesHandler(
7575
}
7676

7777
return { resources };
78-
});
78+
} catch (error) {
79+
log.error(error, 'Error listing resources');
80+
return { resources: [] };
81+
}
7982
};
8083
}
8184

8285
export function importResourceStateHandler(
8386
components: ServerComponents,
8487
): ServerRequestHandler<ResourceStateParams, ResourceStateResult, never, void> {
8588
return async (params: ResourceStateParams): Promise<ResourceStateResult> => {
86-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
87-
return await components.resourceStateImporter.importResourceState(params);
88-
});
89+
return await components.resourceStateImporter.importResourceState(params);
8990
};
9091
}
9192

9293
export function refreshResourceListHandler(
9394
components: ServerComponents,
9495
): ServerRequestHandler<RefreshResourcesParams, RefreshResourcesResult, never, void> {
9596
return async (params: RefreshResourcesParams): Promise<RefreshResourcesResult> => {
96-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
97+
try {
9798
const timeout = new Promise<never>((resolve, reject) =>
9899
setTimeout(() => reject(new Error('Resource list refresh timed out')), 30_000),
99100
);
100101

101102
const resourceTypes = params.resources.map((r) => r.resourceType);
102103
return await Promise.race([components.resourceStateManager.refreshResourceList(resourceTypes), timeout]);
103-
});
104+
} catch (error) {
105+
log.error(error, 'Failed to refresh resource list');
106+
throw new Error(`Failed to refresh resource list: ${extractErrorMessage(error)}`);
107+
}
104108
};
105109
}
106110

107111
export function searchResourceHandler(
108112
components: ServerComponents,
109113
): ServerRequestHandler<SearchResourceParams, SearchResourceResult, never, void> {
110114
return async (params: SearchResourceParams): Promise<SearchResourceResult> => {
111-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
115+
try {
112116
const result = await components.resourceStateManager.searchResourceByIdentifier(
113117
params.resourceType,
114118
params.identifier,
@@ -123,17 +127,18 @@ export function searchResourceHandler(
123127
}
124128
: undefined,
125129
};
126-
});
130+
} catch (error) {
131+
log.error(error, 'Failed to search resource');
132+
return { found: false };
133+
}
127134
};
128135
}
129136

130137
export function getStackMgmtInfo(
131138
components: ServerComponents,
132139
): ServerRequestHandler<ResourceIdentifier, ResourceStackManagementResult, never, void> {
133140
return async (id) => {
134-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
135-
return await components.stackManagementInfoProvider.getResourceManagementState(id);
136-
});
141+
return await components.stackManagementInfoProvider.getResourceManagementState(id);
137142
};
138143
}
139144

@@ -144,7 +149,7 @@ export function getManagedResourceStackTemplateHandler(
144149

145150
return async (params, _token) => {
146151
return await telemetry.measureAsync('getManagedResourceStackTemplate', async () => {
147-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
152+
try {
148153
const template = await components.cfnService.getTemplate({ StackName: params.stackName });
149154
if (!template) {
150155
return;
@@ -189,7 +194,16 @@ export function getManagedResourceStackTemplateHandler(
189194
templateBody: template,
190195
lineNumber,
191196
};
192-
});
197+
} catch (error) {
198+
log.error({
199+
Handler: 'GetManagedResourceStackTemplateHandler',
200+
StackName: params.stackName,
201+
ErrorMessage: error instanceof Error ? error.message : String(error),
202+
ErrorStack: error instanceof Error ? error.stack : undefined,
203+
Error: error,
204+
});
205+
throw error;
206+
}
193207
});
194208
};
195209
}

src/handlers/S3Handler.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
import { RequestHandler } from 'vscode-languageserver';
1+
import { ErrorCodes, RequestHandler, ResponseError } from 'vscode-languageserver';
22
import { parseUploadFileParams } from '../s3/S3RequestParser';
33
import { UploadFileParams } from '../s3/S3RequestType';
44
import { ServerComponents } from '../server/ServerComponents';
5-
import { withOnlineFeatures } from '../utils/OnlineFeatureWrapper';
5+
import { extractErrorMessage } from '../utils/Errors';
66
import { parseWithPrettyError } from '../utils/ZodErrorWrapper';
77

88
export function uploadFileToS3Handler(components: ServerComponents): RequestHandler<UploadFileParams, void, void> {
99
return async (rawParams) => {
10-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
10+
try {
1111
const params = parseWithPrettyError(parseUploadFileParams, rawParams);
1212
await components.s3Service.putObject(params.localFilePath, params.s3Url);
13-
});
13+
} catch (error) {
14+
handleS3Error(error, 'Failed to upload file to S3');
15+
}
1416
};
1517
}
18+
19+
function handleS3Error(error: unknown, contextMessage: string): never {
20+
if (error instanceof ResponseError) {
21+
throw error;
22+
}
23+
if (error instanceof TypeError) {
24+
throw new ResponseError(ErrorCodes.InvalidParams, error.message);
25+
}
26+
throw new ResponseError(ErrorCodes.InternalError, `${contextMessage}: ${extractErrorMessage(error)}`);
27+
}

src/handlers/StackHandler.ts

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ import {
4949
DescribeChangeSetParams,
5050
DescribeChangeSetResult,
5151
} from '../stacks/StackRequestType';
52+
import { LoggerFactory } from '../telemetry/LoggerFactory';
5253
import { TelemetryService } from '../telemetry/TelemetryService';
5354
import { handleLspError } from '../utils/Errors';
54-
import { withOnlineFeatures } from '../utils/OnlineFeatureWrapper';
5555
import { parseWithPrettyError } from '../utils/ZodErrorWrapper';
5656

57+
const log = LoggerFactory.getLogger('StackHandler');
58+
5759
export function getParametersHandler(
5860
components: ServerComponents,
5961
): RequestHandler<TemplateUri, GetParametersResult, void> {
@@ -112,10 +114,12 @@ export function createValidationHandler(
112114
): RequestHandler<CreateValidationParams, CreateStackActionResult, void> {
113115
return async (rawParams) => {
114116
return await TelemetryService.instance.get('StackHandler').measureAsync('createValidation', async () => {
115-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
117+
try {
116118
const params = parseWithPrettyError(parseCreateValidationParams, rawParams);
117119
return await components.validationWorkflowService.start(params);
118-
});
120+
} catch (error) {
121+
handleLspError(error, 'Failed to start validation workflow');
122+
}
119123
});
120124
};
121125
}
@@ -125,10 +129,12 @@ export function createDeploymentHandler(
125129
): RequestHandler<CreateDeploymentParams, CreateStackActionResult, void> {
126130
return async (rawParams) => {
127131
return await TelemetryService.instance.get('StackHandler').measureAsync('createDeployment', async () => {
128-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
132+
try {
129133
const params = parseWithPrettyError(parseCreateDeploymentParams, rawParams);
130134
return await components.deploymentWorkflowService.start(params);
131-
});
135+
} catch (error) {
136+
handleLspError(error, 'Failed to start deployment workflow');
137+
}
132138
});
133139
};
134140
}
@@ -190,10 +196,12 @@ export function deleteChangeSetHandler(
190196
): RequestHandler<DeleteChangeSetParams, CreateStackActionResult, void> {
191197
return async (rawParams) => {
192198
return await TelemetryService.instance.get('StackHandler').measureAsync('deleteChangeSet', async () => {
193-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
199+
try {
194200
const params = parseWithPrettyError(parseDeleteChangeSetParams, rawParams);
195201
return await components.changeSetDeletionWorkflowService.start(params);
196-
});
202+
} catch (error) {
203+
handleLspError(error, 'Failed to start change set deletion workflow');
204+
}
197205
});
198206
};
199207
}
@@ -229,7 +237,7 @@ export function getCapabilitiesHandler(
229237
): RequestHandler<TemplateUri, GetCapabilitiesResult, void> {
230238
return async (rawParams) => {
231239
return await TelemetryService.instance.get('StackHandler').measureAsync('getCapabilities', async () => {
232-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
240+
try {
233241
const params = parseWithPrettyError(parseTemplateUriParams, rawParams);
234242
const document = components.documentManager.get(params);
235243
if (!document) {
@@ -240,8 +248,11 @@ export function getCapabilitiesHandler(
240248
}
241249

242250
const capabilities = await analyzeCapabilities(document, components.cfnService);
251+
243252
return { capabilities };
244-
});
253+
} catch (error) {
254+
handleLspError(error, 'Failed to analyze template capabilities');
255+
}
245256
});
246257
};
247258
}
@@ -315,7 +326,7 @@ export function listStacksHandler(
315326
): RequestHandler<ListStacksParams, ListStacksResult, void> {
316327
return async (params: ListStacksParams): Promise<ListStacksResult> => {
317328
return await TelemetryService.instance.get('StackHandler').measureAsync('listStacks', async () => {
318-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
329+
try {
319330
if (params.statusToInclude?.length && params.statusToExclude?.length) {
320331
throw new Error('Cannot specify both statusToInclude and statusToExclude');
321332
}
@@ -324,7 +335,10 @@ export function listStacksHandler(
324335
params.statusToExclude,
325336
params.loadMore,
326337
);
327-
});
338+
} catch (error) {
339+
log.error(error, 'Error listing stacks');
340+
return { stacks: [], nextToken: undefined };
341+
}
328342
});
329343
};
330344
}
@@ -334,7 +348,7 @@ export function listChangeSetsHandler(
334348
): RequestHandler<ListChangeSetParams, ListChangeSetResult, void> {
335349
return async (params: ListChangeSetParams): Promise<ListChangeSetResult> => {
336350
return await TelemetryService.instance.get('StackHandler').measureAsync('listChangeSets', async () => {
337-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
351+
try {
338352
const result = await components.cfnService.listChangeSets(params.stackName, params.nextToken);
339353
return {
340354
changeSets: result.changeSets.map((cs) => ({
@@ -345,7 +359,9 @@ export function listChangeSetsHandler(
345359
})),
346360
nextToken: result.nextToken,
347361
};
348-
});
362+
} catch {
363+
return { changeSets: [] };
364+
}
349365
});
350366
};
351367
}
@@ -354,7 +370,7 @@ export function listStackResourcesHandler(
354370
components: ServerComponents,
355371
): RequestHandler<ListStackResourcesParams, ListStackResourcesResult, void> {
356372
return async (rawParams): Promise<ListStackResourcesResult> => {
357-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
373+
try {
358374
const params = parseWithPrettyError(parseListStackResourcesParams, rawParams);
359375
const response = await components.cfnService.listStackResources({
360376
StackName: params.stackName,
@@ -364,47 +380,50 @@ export function listStackResourcesHandler(
364380
resources: response.StackResourceSummaries ?? [],
365381
nextToken: response.NextToken,
366382
};
367-
});
383+
} catch (error) {
384+
log.error(error, 'Error listing stack resources');
385+
return { resources: [] };
386+
}
368387
};
369388
}
370389

371390
export function describeChangeSetHandler(
372391
components: ServerComponents,
373392
): RequestHandler<DescribeChangeSetParams, DescribeChangeSetResult, void> {
374393
return async (rawParams: DescribeChangeSetParams): Promise<DescribeChangeSetResult> => {
375-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
376-
const params = parseWithPrettyError(parseDescribeChangeSetParams, rawParams);
394+
const params = parseWithPrettyError(parseDescribeChangeSetParams, rawParams);
377395

378-
const result = await components.cfnService.describeChangeSet({
379-
ChangeSetName: params.changeSetName,
380-
IncludePropertyValues: true,
381-
StackName: params.stackName,
382-
});
383-
384-
return {
385-
changeSetName: params.changeSetName,
386-
stackName: params.stackName,
387-
status: result.Status ?? '',
388-
creationTime: result.CreationTime?.toISOString(),
389-
description: result.Description,
390-
changes: mapChangesToStackChanges(result.Changes),
391-
};
396+
const result = await components.cfnService.describeChangeSet({
397+
ChangeSetName: params.changeSetName,
398+
IncludePropertyValues: true,
399+
StackName: params.stackName,
392400
});
401+
402+
return {
403+
changeSetName: params.changeSetName,
404+
stackName: params.stackName,
405+
status: result.Status ?? '',
406+
creationTime: result.CreationTime?.toISOString(),
407+
description: result.Description,
408+
changes: mapChangesToStackChanges(result.Changes),
409+
};
393410
};
394411
}
395412

396413
export function getStackEventsHandler(
397414
components: ServerComponents,
398415
): RequestHandler<GetStackEventsParams, GetStackEventsResult, void> {
399416
return async (rawParams): Promise<GetStackEventsResult> => {
400-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
417+
try {
401418
const params = parseWithPrettyError(parseGetStackEventsParams, rawParams);
402419
if (params.refresh) {
403420
const result = await components.stackEventManager.refresh(params.stackName);
404421
return { events: result.events, nextToken: undefined, gapDetected: result.gapDetected };
405422
}
406423
return await components.stackEventManager.fetchEvents(params.stackName, params.nextToken);
407-
});
424+
} catch (error) {
425+
handleLspError(error, 'Failed to get stack events');
426+
}
408427
};
409428
}
410429

@@ -425,11 +444,13 @@ export function describeStackHandler(
425444
components: ServerComponents,
426445
): RequestHandler<DescribeStackParams, DescribeStackResult, void> {
427446
return async (rawParams): Promise<DescribeStackResult> => {
428-
return await withOnlineFeatures(components.onlineFeatureGuard, async () => {
447+
try {
429448
const params = parseWithPrettyError(parseDescribeStackParams, rawParams);
430449
const response = await components.cfnService.describeStacks({ StackName: params.stackName });
431450
const stack = response.Stacks?.[0];
432451
return { stack };
433-
});
452+
} catch (error) {
453+
handleLspError(error, 'Failed to describe stack');
454+
}
434455
};
435456
}

0 commit comments

Comments
 (0)