Skip to content

Commit 86e958f

Browse files
deepfuriyaDeep Furiya
andauthored
New handlers and protocols to communicate with client for Related Resources workflow (#139)
* added new handlers to communicate with client for related resources workflow --------- Co-authored-by: Deep Furiya <[email protected]>
1 parent 3c6399c commit 86e958f

17 files changed

+1101
-26
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { RequestHandler } from 'vscode-languageserver';
2+
import { TopLevelSection } from '../context/ContextType';
3+
import { getEntityMap } from '../context/SectionContextBuilder';
4+
import { Resource } from '../context/semantic/Entity';
5+
import {
6+
GetRelatedResourceTypesParams,
7+
InsertRelatedResourcesParams,
8+
RelatedResourcesCodeAction,
9+
TemplateUri,
10+
} from '../protocol/RelatedResourcesProtocol';
11+
import { ServerComponents } from '../server/ServerComponents';
12+
import { handleLspError } from '../utils/Errors';
13+
import { parseWithPrettyError } from '../utils/ZodErrorWrapper';
14+
import {
15+
parseGetRelatedResourceTypesParams,
16+
parseInsertRelatedResourcesParams,
17+
parseTemplateUriParams,
18+
} from './RelatedResourcesParser';
19+
20+
export function getAuthoredResourceTypesHandler(
21+
components: ServerComponents,
22+
): RequestHandler<TemplateUri, string[], void> {
23+
return (rawParams) => {
24+
try {
25+
const templateUri = parseWithPrettyError(parseTemplateUriParams, rawParams);
26+
const syntaxTree = components.syntaxTreeManager.getSyntaxTree(templateUri);
27+
if (syntaxTree) {
28+
const resourcesMap = getEntityMap(syntaxTree, TopLevelSection.Resources);
29+
if (resourcesMap) {
30+
const resourceTypes = [...resourcesMap.values()]
31+
.map((context) => {
32+
const resource = context.entity as Resource;
33+
return resource?.Type;
34+
})
35+
.filter((type): type is string => type !== undefined && type !== null);
36+
37+
return [...new Set(resourceTypes)];
38+
}
39+
}
40+
41+
return [];
42+
} catch (error) {
43+
handleLspError(error, 'Failed to get authored resource types');
44+
}
45+
};
46+
}
47+
48+
export function getRelatedResourceTypesHandler(
49+
components: ServerComponents,
50+
): RequestHandler<GetRelatedResourceTypesParams, string[], void> {
51+
return (rawParams) => {
52+
try {
53+
const { parentResourceType } = parseWithPrettyError(parseGetRelatedResourceTypesParams, rawParams);
54+
const relatedTypes = components.relationshipSchemaService.getAllRelatedResourceTypes(parentResourceType);
55+
return [...relatedTypes];
56+
} catch (error) {
57+
handleLspError(error, 'Failed to get related resource types');
58+
}
59+
};
60+
}
61+
62+
export function insertRelatedResourcesHandler(
63+
components: ServerComponents,
64+
): RequestHandler<InsertRelatedResourcesParams, RelatedResourcesCodeAction, void> {
65+
return (rawParams) => {
66+
try {
67+
const { templateUri, relatedResourceTypes, parentResourceType } = parseWithPrettyError(
68+
parseInsertRelatedResourcesParams,
69+
rawParams,
70+
);
71+
return components.relatedResourcesSnippetProvider.insertRelatedResources(
72+
templateUri,
73+
relatedResourceTypes,
74+
parentResourceType,
75+
);
76+
} catch (error) {
77+
handleLspError(error, 'Failed to insert related resources');
78+
}
79+
};
80+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { z } from 'zod';
2+
import {
3+
GetRelatedResourceTypesParams,
4+
InsertRelatedResourcesParams,
5+
TemplateUri,
6+
} from '../protocol/RelatedResourcesProtocol';
7+
8+
const TemplateUriSchema = z.string().min(1);
9+
10+
const GetRelatedResourceTypesParamsSchema = z.object({
11+
parentResourceType: z.string().min(1),
12+
});
13+
14+
const InsertRelatedResourcesParamsSchema = z.object({
15+
templateUri: z.string().min(1),
16+
relatedResourceTypes: z.array(z.string().min(1)).min(1),
17+
parentResourceType: z.string().min(1),
18+
});
19+
20+
export function parseTemplateUriParams(input: unknown): TemplateUri {
21+
return TemplateUriSchema.parse(input);
22+
}
23+
24+
export function parseGetRelatedResourceTypesParams(input: unknown): GetRelatedResourceTypesParams {
25+
return GetRelatedResourceTypesParamsSchema.parse(input);
26+
}
27+
28+
export function parseInsertRelatedResourcesParams(input: unknown): InsertRelatedResourcesParams {
29+
return InsertRelatedResourcesParamsSchema.parse(input);
30+
}

src/handlers/StackHandler.ts

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {
4848
DescribeChangeSetResult,
4949
} from '../stacks/StackRequestType';
5050
import { LoggerFactory } from '../telemetry/LoggerFactory';
51-
import { extractErrorMessage } from '../utils/Errors';
51+
import { handleLspError } from '../utils/Errors';
5252
import { parseWithPrettyError } from '../utils/ZodErrorWrapper';
5353

5454
const log = LoggerFactory.getLogger('StackHandler');
@@ -74,7 +74,7 @@ export function getParametersHandler(
7474
parameters: [],
7575
};
7676
} catch (error) {
77-
handleStackActionError(error, 'Failed to get parameters');
77+
handleLspError(error, 'Failed to get parameters');
7878
}
7979
};
8080
}
@@ -87,7 +87,7 @@ export function createValidationHandler(
8787
const params = parseWithPrettyError(parseCreateValidationParams, rawParams);
8888
return await components.validationWorkflowService.start(params);
8989
} catch (error) {
90-
handleStackActionError(error, 'Failed to start validation workflow');
90+
handleLspError(error, 'Failed to start validation workflow');
9191
}
9292
};
9393
}
@@ -100,7 +100,7 @@ export function createDeploymentHandler(
100100
const params = parseWithPrettyError(parseCreateDeploymentParams, rawParams);
101101
return await components.deploymentWorkflowService.start(params);
102102
} catch (error) {
103-
handleStackActionError(error, 'Failed to start deployment workflow');
103+
handleLspError(error, 'Failed to start deployment workflow');
104104
}
105105
};
106106
}
@@ -113,7 +113,7 @@ export function getValidationStatusHandler(
113113
const params = parseWithPrettyError(parseIdentifiable, rawParams);
114114
return components.validationWorkflowService.getStatus(params);
115115
} catch (error) {
116-
handleStackActionError(error, 'Failed to get validation status');
116+
handleLspError(error, 'Failed to get validation status');
117117
}
118118
};
119119
}
@@ -126,7 +126,7 @@ export function getDeploymentStatusHandler(
126126
const params = parseWithPrettyError(parseIdentifiable, rawParams);
127127
return components.deploymentWorkflowService.getStatus(params);
128128
} catch (error) {
129-
handleStackActionError(error, 'Failed to get deployment status');
129+
handleLspError(error, 'Failed to get deployment status');
130130
}
131131
};
132132
}
@@ -139,7 +139,7 @@ export function describeValidationStatusHandler(
139139
const params = parseWithPrettyError(parseIdentifiable, rawParams);
140140
return components.validationWorkflowService.describeStatus(params);
141141
} catch (error) {
142-
handleStackActionError(error, 'Failed to describe validation status');
142+
handleLspError(error, 'Failed to describe validation status');
143143
}
144144
};
145145
}
@@ -152,7 +152,7 @@ export function describeDeploymentStatusHandler(
152152
const params = parseWithPrettyError(parseIdentifiable, rawParams);
153153
return components.deploymentWorkflowService.describeStatus(params);
154154
} catch (error) {
155-
handleStackActionError(error, 'Failed to describe deployment status');
155+
handleLspError(error, 'Failed to describe deployment status');
156156
}
157157
};
158158
}
@@ -165,7 +165,7 @@ export function deleteChangeSetHandler(
165165
const params = parseWithPrettyError(parseDeleteChangeSetParams, rawParams);
166166
return await components.changeSetDeletionWorkflowService.start(params);
167167
} catch (error) {
168-
handleStackActionError(error, 'Failed to start change set deletion workflow');
168+
handleLspError(error, 'Failed to start change set deletion workflow');
169169
}
170170
};
171171
}
@@ -178,7 +178,7 @@ export function getChangeSetDeletionStatusHandler(
178178
const params = parseWithPrettyError(parseIdentifiable, rawParams);
179179
return components.changeSetDeletionWorkflowService.getStatus(params);
180180
} catch (error) {
181-
handleStackActionError(error, 'Failed to get change set deletion status');
181+
handleLspError(error, 'Failed to get change set deletion status');
182182
}
183183
};
184184
}
@@ -191,7 +191,7 @@ export function describeChangeSetDeletionStatusHandler(
191191
const params = parseWithPrettyError(parseIdentifiable, rawParams);
192192
return components.changeSetDeletionWorkflowService.describeStatus(params);
193193
} catch (error) {
194-
handleStackActionError(error, 'Failed to describe change set deletion status');
194+
handleLspError(error, 'Failed to describe change set deletion status');
195195
}
196196
};
197197
}
@@ -211,7 +211,7 @@ export function getCapabilitiesHandler(
211211

212212
return { capabilities };
213213
} catch (error) {
214-
handleStackActionError(error, 'Failed to analyze template capabilities');
214+
handleLspError(error, 'Failed to analyze template capabilities');
215215
}
216216
};
217217
}
@@ -252,7 +252,7 @@ export function getTemplateResourcesHandler(
252252

253253
return { resources };
254254
} catch (error) {
255-
handleStackActionError(error, 'Failed to get template resources');
255+
handleLspError(error, 'Failed to get template resources');
256256
}
257257
};
258258
}
@@ -377,7 +377,7 @@ export function getStackEventsHandler(
377377
}
378378
return await components.stackEventManager.fetchEvents(params.stackName, params.nextToken);
379379
} catch (error) {
380-
handleStackActionError(error, 'Failed to get stack events');
380+
handleLspError(error, 'Failed to get stack events');
381381
}
382382
};
383383
}
@@ -390,7 +390,7 @@ export function clearStackEventsHandler(
390390
parseWithPrettyError(parseClearStackEventsParams, rawParams);
391391
components.stackEventManager.clear();
392392
} catch (error) {
393-
handleStackActionError(error, 'Failed to clear stack events');
393+
handleLspError(error, 'Failed to clear stack events');
394394
}
395395
};
396396
}
@@ -405,17 +405,7 @@ export function getStackOutputsHandler(
405405
const outputs = response.Stacks?.[0]?.Outputs ?? [];
406406
return { outputs };
407407
} catch (error) {
408-
handleStackActionError(error, 'Failed to get stack outputs');
408+
handleLspError(error, 'Failed to get stack outputs');
409409
}
410410
};
411411
}
412-
413-
function handleStackActionError(error: unknown, contextMessage: string): never {
414-
if (error instanceof ResponseError) {
415-
throw error;
416-
}
417-
if (error instanceof TypeError) {
418-
throw new ResponseError(ErrorCodes.InvalidParams, error.message);
419-
}
420-
throw new ResponseError(ErrorCodes.InternalError, `${contextMessage}: ${extractErrorMessage(error)}`);
421-
}

src/protocol/LspComponents.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LspCommunication } from './LspCommunication';
33
import { LspDiagnostics } from './LspDiagnostics';
44
import { LspDocuments } from './LspDocuments';
55
import { LspHandlers } from './LspHandlers';
6+
import { LspRelatedResourcesHandlers } from './LspRelatedResourcesHandlers';
67
import { LspResourceHandlers } from './LspResourceHandlers';
78
import { LspStackHandlers } from './LspStackHandlers';
89
import { LspWorkspace } from './LspWorkspace';
@@ -17,5 +18,6 @@ export class LspComponents {
1718
public readonly authHandlers: LspAuthHandlers,
1819
public readonly stackHandlers: LspStackHandlers,
1920
public readonly resourceHandlers: LspResourceHandlers,
21+
public readonly relatedResourcesHandlers: LspRelatedResourcesHandlers,
2022
) {}
2123
}

src/protocol/LspConnection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { LspComponents } from './LspComponents';
1414
import { LspDiagnostics } from './LspDiagnostics';
1515
import { LspDocuments } from './LspDocuments';
1616
import { LspHandlers } from './LspHandlers';
17+
import { LspRelatedResourcesHandlers } from './LspRelatedResourcesHandlers';
1718
import { LspResourceHandlers } from './LspResourceHandlers';
1819
import { LspStackHandlers } from './LspStackHandlers';
1920
import { LspWorkspace } from './LspWorkspace';
@@ -34,6 +35,7 @@ export class LspConnection {
3435
private readonly authHandlers: LspAuthHandlers;
3536
private readonly stackHandlers: LspStackHandlers;
3637
private readonly resourceHandlers: LspResourceHandlers;
38+
private readonly relatedResourcesHandlers: LspRelatedResourcesHandlers;
3739

3840
private initializeParams?: InitializeParams;
3941

@@ -56,6 +58,7 @@ export class LspConnection {
5658
this.authHandlers = new LspAuthHandlers(this.connection);
5759
this.stackHandlers = new LspStackHandlers(this.connection);
5860
this.resourceHandlers = new LspResourceHandlers(this.connection);
61+
this.relatedResourcesHandlers = new LspRelatedResourcesHandlers(this.connection);
5962

6063
this.communication.console.info(`${ExtensionName} launched from ${__dirname}`);
6164

@@ -88,6 +91,7 @@ export class LspConnection {
8891
this.authHandlers,
8992
this.stackHandlers,
9093
this.resourceHandlers,
94+
this.relatedResourcesHandlers,
9195
);
9296
}
9397

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Connection, RequestHandler } from 'vscode-languageserver';
2+
import {
3+
GetAuthoredResourceTypesRequest,
4+
GetRelatedResourceTypesParams,
5+
GetRelatedResourceTypesRequest,
6+
InsertRelatedResourcesParams,
7+
InsertRelatedResourcesRequest,
8+
RelatedResourcesCodeAction,
9+
TemplateUri,
10+
} from './RelatedResourcesProtocol';
11+
12+
export class LspRelatedResourcesHandlers {
13+
constructor(private readonly connection: Connection) {}
14+
15+
onGetAuthoredResourceTypes(handler: RequestHandler<TemplateUri, string[], void>) {
16+
this.connection.onRequest(GetAuthoredResourceTypesRequest.method, handler);
17+
}
18+
19+
onGetRelatedResourceTypes(handler: RequestHandler<GetRelatedResourceTypesParams, string[], void>) {
20+
this.connection.onRequest(GetRelatedResourceTypesRequest.method, handler);
21+
}
22+
23+
onInsertRelatedResources(handler: RequestHandler<InsertRelatedResourcesParams, RelatedResourcesCodeAction, void>) {
24+
this.connection.onRequest(InsertRelatedResourcesRequest.method, handler);
25+
}
26+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { RequestType, CodeAction, Position } from 'vscode-languageserver-protocol';
2+
3+
export type TemplateUri = string;
4+
5+
export type GetRelatedResourceTypesParams = {
6+
parentResourceType: string;
7+
};
8+
9+
export type InsertRelatedResourcesParams = {
10+
templateUri: string;
11+
relatedResourceTypes: string[];
12+
parentResourceType: string;
13+
};
14+
15+
export interface RelatedResourcesCodeAction extends CodeAction {
16+
data?: {
17+
scrollToPosition?: Position;
18+
firstLogicalId?: string;
19+
};
20+
}
21+
22+
export const GetAuthoredResourceTypesRequest = new RequestType<TemplateUri, string[], void>(
23+
'aws/cfn/template/resources/authored',
24+
);
25+
26+
export const GetRelatedResourceTypesRequest = new RequestType<GetRelatedResourceTypesParams, string[], void>(
27+
'aws/cfn/template/resources/related',
28+
);
29+
30+
export const InsertRelatedResourcesRequest = new RequestType<
31+
InsertRelatedResourcesParams,
32+
RelatedResourcesCodeAction,
33+
void
34+
>('aws/cfn/template/resources/insert');

0 commit comments

Comments
 (0)