Skip to content

Commit 1df3bdf

Browse files
committed
make DescribeEvents passthrough API and remove caching
1 parent 95cf122 commit 1df3bdf

File tree

9 files changed

+55
-389
lines changed

9 files changed

+55
-389
lines changed

src/handlers/StackHandler.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { OperationEvent } from '@aws-sdk/client-cloudformation';
12
import { ErrorCodes, RequestHandler, ResponseError } from 'vscode-languageserver';
23
import { ArtifactExporter } from '../artifactexporter/ArtifactExporter';
34
import { TopLevelSection } from '../context/ContextType';
@@ -455,16 +456,32 @@ export function describeEventsHandler(
455456
try {
456457
const params = parseWithPrettyError(parseDescribeEventsParams, rawParams);
457458

458-
if (!params.stackName) {
459-
throw new Error('stackName is required for describeEventsHandler');
460-
}
459+
const response = await components.cfnService.describeEvents({
460+
StackName: params.stackName,
461+
ChangeSetName: params.changeSetName,
462+
OperationId: params.operationId,
463+
FailedEventsOnly: params.failedEventsOnly,
464+
NextToken: params.nextToken,
465+
});
461466

462-
if (params.refresh) {
463-
const result = await components.stackOperationEventManager.refresh(params.stackName);
464-
return { operations: result.operations, nextToken: undefined, gapDetected: result.gapDetected };
467+
const operations = new Map<string, OperationEvent[]>();
468+
for (const event of response.OperationEvents ?? []) {
469+
const opId = event.OperationId ?? 'unknown';
470+
const existing = operations.get(opId);
471+
if (existing) {
472+
existing.push(event);
473+
} else {
474+
operations.set(opId, [event]);
475+
}
465476
}
466477

467-
return await components.stackOperationEventManager.fetchEvents(params.stackName, params.nextToken);
478+
return {
479+
operations: [...operations.entries()].map(([operationId, events]) => ({
480+
operationId,
481+
events: events.sort((a, b) => (b.Timestamp?.getTime() ?? 0) - (a.Timestamp?.getTime() ?? 0)),
482+
})),
483+
nextToken: response.NextToken,
484+
};
468485
} catch (error) {
469486
handleLspError(error, 'Failed to describe events');
470487
}

src/server/CfnLspProviders.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { StackActionWorkflow } from '../stacks/actions/StackActionWorkflowType';
2525
import { ValidationWorkflow } from '../stacks/actions/ValidationWorkflow';
2626
import { StackEventManager } from '../stacks/StackEventManager';
2727
import { StackManager } from '../stacks/StackManager';
28-
import { StackOperationEventManager } from '../stacks/StackOperationEventManager';
2928
import { Closeable, closeSafely } from '../utils/Closeable';
3029
import { Configurable, Configurables } from '../utils/Configurable';
3130
import { CfnExternal } from './CfnExternal';
@@ -39,7 +38,6 @@ export class CfnLspProviders implements Configurables, Closeable {
3938
readonly changeSetDeletionWorkflowService: StackActionWorkflow<DeleteChangeSetParams, DescribeDeletionStatusResult>;
4039
readonly stackManager: StackManager;
4140
readonly stackEventManager: StackEventManager;
42-
readonly stackOperationEventManager: StackOperationEventManager;
4341
readonly resourceStateManager: ResourceStateManager;
4442
readonly resourceStateImporter: ResourceStateImporter;
4543
readonly relationshipSchemaService: RelationshipSchemaService;
@@ -62,8 +60,6 @@ export class CfnLspProviders implements Configurables, Closeable {
6260
overrides.stackManagementInfoProvider ?? new StackManagementInfoProvider(external.cfnService);
6361
this.stackManager = overrides.stackManager ?? new StackManager(external.cfnService);
6462
this.stackEventManager = overrides.stackEventManager ?? new StackEventManager(external.cfnService);
65-
this.stackOperationEventManager =
66-
overrides.stackOperationEventManager ?? new StackOperationEventManager(external.cfnService);
6763
this.validationWorkflowService =
6864
overrides.validationWorkflowService ?? ValidationWorkflow.create(core, external, core.validationManager);
6965
this.deploymentWorkflowService =

src/stacks/StackOperationEventManager.ts

Lines changed: 0 additions & 94 deletions
This file was deleted.

src/stacks/StackRequestType.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ export type DescribeEventsParams = {
125125
operationId?: string;
126126
failedEventsOnly?: boolean;
127127
nextToken?: string;
128-
refresh?: boolean;
129128
};
130129

131130
export type StackOperationGroup = {
@@ -136,7 +135,6 @@ export type StackOperationGroup = {
136135
export type DescribeEventsResult = {
137136
operations: StackOperationGroup[];
138137
nextToken?: string;
139-
gapDetected?: boolean;
140138
};
141139

142140
export const DescribeEventsRequest = new RequestType<DescribeEventsParams, DescribeEventsResult, void>(

src/stacks/actions/StackActionParser.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ const DescribeEventsParamsSchema = z
108108
operationId: z.string().optional(),
109109
failedEventsOnly: z.boolean().optional(),
110110
nextToken: z.string().optional(),
111-
refresh: z.boolean().optional(),
112111
})
113112
.refine((data) => data.stackName ?? data.changeSetName ?? data.operationId, {
114113
message: 'At least one of stackName, changeSetName, or operationId must be provided',

tst/unit/handlers/StackHandler.test.ts

Lines changed: 31 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import {
5252
DescribeStackResult,
5353
DescribeChangeSetParams,
5454
DescribeChangeSetResult,
55-
DescribeEventsParams,
5655
DescribeEventsResult,
5756
} from '../../../src/stacks/StackRequestType';
5857
import {
@@ -928,83 +927,53 @@ describe('StackActionHandler', () => {
928927
});
929928

930929
describe('describeEventsHandler', () => {
931-
it('should fetch events for stack', async () => {
932-
mockComponents.stackOperationEventManager.fetchEvents.resolves({
933-
operations: [{ operationId: 'op1', events: [] }],
934-
nextToken: undefined,
930+
it('should fetch and group events by operation ID', async () => {
931+
mockComponents.cfnService.describeEvents.resolves({
932+
OperationEvents: [
933+
{ EventId: '1', OperationId: 'op1', Timestamp: new Date('2024-01-01') },
934+
{ EventId: '2', OperationId: 'op1', Timestamp: new Date('2024-01-02') },
935+
{ EventId: '3', OperationId: 'op2', Timestamp: new Date('2024-01-03') },
936+
],
937+
$metadata: {},
935938
});
936939

937940
const handler = describeEventsHandler(mockComponents);
938941
const result = (await handler({ stackName: 'test-stack' }, CancellationToken.None)) as DescribeEventsResult;
939942

940-
expect(result.operations).toHaveLength(1);
941-
expect(result.nextToken).toBeUndefined();
942-
expect(mockComponents.stackOperationEventManager.fetchEvents.calledWith('test-stack', undefined)).toBe(
943-
true,
944-
);
943+
expect(result.operations).toHaveLength(2);
944+
expect(result.operations[0].operationId).toBe('op1');
945+
expect(result.operations[0].events).toHaveLength(2);
945946
});
946947

947-
it('should refresh events when refresh flag is true', async () => {
948-
mockComponents.stackOperationEventManager.refresh.resolves({
949-
operations: [{ operationId: 'op1', events: [] }],
950-
gapDetected: false,
951-
});
948+
it('should pass all parameters to API', async () => {
949+
mockComponents.cfnService.describeEvents.resolves({ OperationEvents: [], $metadata: {} });
952950

953951
const handler = describeEventsHandler(mockComponents);
954-
const result = (await handler(
955-
{ stackName: 'test-stack', refresh: true },
956-
CancellationToken.None,
957-
)) as DescribeEventsResult;
958-
959-
expect(result.operations).toHaveLength(1);
960-
expect(result.gapDetected).toBe(false);
961-
expect(mockComponents.stackOperationEventManager.refresh.calledWith('test-stack')).toBe(true);
962-
});
963-
964-
it('should pass nextToken for pagination', async () => {
965-
mockComponents.stackOperationEventManager.fetchEvents.resolves({
966-
operations: [],
967-
nextToken: 'token123',
968-
});
969-
970-
const handler = describeEventsHandler(mockComponents);
971-
const result = (await handler(
972-
{ stackName: 'test-stack', nextToken: 'token123' },
952+
await handler(
953+
{
954+
stackName: 'test-stack',
955+
changeSetName: 'cs',
956+
operationId: 'op',
957+
failedEventsOnly: true,
958+
nextToken: 'token',
959+
},
973960
CancellationToken.None,
974-
)) as DescribeEventsResult;
975-
976-
expect(result.nextToken).toBe('token123');
977-
expect(mockComponents.stackOperationEventManager.fetchEvents.calledWith('test-stack', 'token123')).toBe(
978-
true,
979961
);
980-
});
981-
982-
it('should return gapDetected from refresh', async () => {
983-
mockComponents.stackOperationEventManager.refresh.resolves({
984-
operations: [],
985-
gapDetected: true,
986-
});
987-
988-
const handler = describeEventsHandler(mockComponents);
989-
const result = (await handler(
990-
{ stackName: 'test-stack', refresh: true },
991-
CancellationToken.None,
992-
)) as DescribeEventsResult;
993-
994-
expect(result.gapDetected).toBe(true);
995-
});
996-
997-
it('should throw error when stackName is missing with refresh', async () => {
998-
const handler = describeEventsHandler(mockComponents);
999962

1000-
await expect(
1001-
handler({ operationId: 'op-123', refresh: true } as DescribeEventsParams, CancellationToken.None),
1002-
).rejects.toThrow();
963+
expect(
964+
mockComponents.cfnService.describeEvents.calledWith({
965+
StackName: 'test-stack',
966+
ChangeSetName: 'cs',
967+
OperationId: 'op',
968+
FailedEventsOnly: true,
969+
NextToken: 'token',
970+
}),
971+
).toBe(true);
1003972
});
1004973

1005974
it('should handle service errors', async () => {
1006975
const serviceError = new Error('Service error');
1007-
mockComponents.stackOperationEventManager.fetchEvents.rejects(serviceError);
976+
mockComponents.cfnService.describeEvents.rejects(serviceError);
1008977

1009978
const handler = describeEventsHandler(mockComponents);
1010979

tst/unit/stackActions/StackActionParser.test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,13 @@ describe('StackActionParser', () => {
284284
operationId: 'op-123',
285285
failedEventsOnly: true,
286286
nextToken: 'token123',
287-
refresh: true,
288287
});
289288

290289
expect(result.stackName).toBe('test-stack');
291290
expect(result.changeSetName).toBe('test-changeset');
292291
expect(result.operationId).toBe('op-123');
293292
expect(result.failedEventsOnly).toBe(true);
294293
expect(result.nextToken).toBe('token123');
295-
expect(result.refresh).toBe(true);
296294
});
297295

298296
it('should throw error when no identifier provided', () => {
@@ -310,21 +308,18 @@ describe('StackActionParser', () => {
310308

311309
it('should throw error for invalid types', () => {
312310
expect(() => parseDescribeEventsParams({ stackName: 'test', failedEventsOnly: 'true' as any })).toThrow();
313-
expect(() => parseDescribeEventsParams({ stackName: 'test', refresh: 'yes' as any })).toThrow();
314311
});
315312

316313
it('should accept undefined optional parameters', () => {
317314
const result = parseDescribeEventsParams({
318315
stackName: 'test-stack',
319316
failedEventsOnly: undefined,
320317
nextToken: undefined,
321-
refresh: undefined,
322318
});
323319

324320
expect(result.stackName).toBe('test-stack');
325321
expect(result.failedEventsOnly).toBeUndefined();
326322
expect(result.nextToken).toBeUndefined();
327-
expect(result.refresh).toBeUndefined();
328323
});
329324
});
330325
});

0 commit comments

Comments
 (0)