Skip to content

Commit e3ebb0c

Browse files
authored
replace abort mutation with call to delete in workflow pod (#304)
FLPATH-1676 https://issues.redhat.com/browse/FLPATH-1676 Signed-off-by: Yaron Dayagi <[email protected]>
1 parent 1d9c824 commit e3ebb0c

File tree

8 files changed

+77
-61
lines changed

8 files changed

+77
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-orchestrator-backend': patch
3+
---
4+
5+
replace abort mutation with call to delete

workspaces/orchestrator/plugins/orchestrator-backend/src/service/DataIndexService.ts

-25
Original file line numberDiff line numberDiff line change
@@ -128,31 +128,6 @@ export class DataIndexService {
128128
return pairs;
129129
}
130130

131-
public async abortWorkflowInstance(instanceId: string): Promise<void> {
132-
this.logger.info(`Aborting workflow instance ${instanceId}`);
133-
const ProcessInstanceAbortMutationDocument = gql`
134-
mutation ProcessInstanceAbortMutation($id: String) {
135-
ProcessInstanceAbort(id: $id)
136-
}
137-
`;
138-
139-
const result = await this.client.mutation(
140-
ProcessInstanceAbortMutationDocument,
141-
{ id: instanceId },
142-
);
143-
144-
this.logger.debug(
145-
`Abort workflow instance result: ${JSON.stringify(result)}`,
146-
);
147-
148-
if (result.error) {
149-
throw new Error(
150-
`Error aborting workflow instance ${instanceId}: ${result.error}`,
151-
);
152-
}
153-
this.logger.debug(`Successfully aborted workflow instance ${instanceId}`);
154-
}
155-
156131
public async fetchWorkflowInfo(
157132
definitionId: string,
158133
): Promise<WorkflowInfo | undefined> {

workspaces/orchestrator/plugins/orchestrator-backend/src/service/OrchestratorService.test.ts

+17-24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import {
1718
ProcessInstance,
1819
WorkflowDefinition,
@@ -88,64 +89,56 @@ describe('OrchestratorService', () => {
8889
});
8990

9091
it('should execute the operation when the workflow is available', async () => {
91-
dataIndexServiceMock.fetchDefinitionIdByInstanceId = jest
92-
.fn()
93-
.mockResolvedValue(definitionId);
9492
workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(true);
95-
dataIndexServiceMock.abortWorkflowInstance = jest.fn(
96-
(_instanceId: string) => Promise.resolve(),
93+
sonataFlowServiceMock.abortInstance = jest.fn(
94+
(_args: {
95+
definitionId: string;
96+
instanceId: string;
97+
serviceUrl: string;
98+
}) => Promise.resolve(),
9799
);
98100

99101
await orchestratorService.abortWorkflowInstance({
102+
definitionId,
100103
instanceId,
104+
serviceUrl,
101105
cacheHandler: 'skip',
102106
});
103107

104-
expect(
105-
dataIndexServiceMock.fetchDefinitionIdByInstanceId,
106-
).toHaveBeenCalled();
107-
expect(dataIndexServiceMock.abortWorkflowInstance).toHaveBeenCalled();
108+
expect(sonataFlowServiceMock.abortInstance).toHaveBeenCalled();
108109
});
109110

110111
it('should skip and not execute the operation when the workflow is not available', async () => {
111-
dataIndexServiceMock.fetchDefinitionIdByInstanceId = jest
112-
.fn()
113-
.mockResolvedValue(definitionId);
114112
workflowCacheServiceMock.isAvailable = jest.fn().mockReturnValue(false);
115113

116114
await orchestratorService.abortWorkflowInstance({
115+
definitionId,
117116
instanceId,
117+
serviceUrl,
118118
cacheHandler: 'skip',
119119
});
120120

121-
expect(
122-
dataIndexServiceMock.fetchDefinitionIdByInstanceId,
123-
).toHaveBeenCalled();
124-
expect(dataIndexServiceMock.abortWorkflowInstance).not.toHaveBeenCalled();
121+
expect(sonataFlowServiceMock.abortInstance).not.toHaveBeenCalled();
125122
});
126123

127124
it('should throw an error and not execute the operation when the workflow is not available', async () => {
128-
dataIndexServiceMock.fetchDefinitionIdByInstanceId = jest
129-
.fn()
130-
.mockResolvedValue(definitionId);
131125
workflowCacheServiceMock.isAvailable = jest
132126
.fn()
133127
.mockImplementation(() => {
134128
throw new Error();
135129
});
136130

137131
const promise = orchestratorService.abortWorkflowInstance({
132+
definitionId,
138133
instanceId,
134+
serviceUrl,
139135
cacheHandler: 'throw',
140136
});
141137

142138
await expect(promise).rejects.toThrow();
143139

144-
expect(
145-
dataIndexServiceMock.fetchDefinitionIdByInstanceId,
146-
).toHaveBeenCalled();
147140
expect(workflowCacheServiceMock.isAvailable).toHaveBeenCalled();
148-
expect(dataIndexServiceMock.abortWorkflowInstance).not.toHaveBeenCalled();
141+
expect(sonataFlowServiceMock.abortInstance).not.toHaveBeenCalled();
149142
});
150143
});
151144

workspaces/orchestrator/plugins/orchestrator-backend/src/service/OrchestratorService.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import {
1718
Filter,
1819
ProcessInstance,
@@ -41,18 +42,18 @@ export class OrchestratorService {
4142
}
4243

4344
public async abortWorkflowInstance(args: {
45+
definitionId: string;
4446
instanceId: string;
47+
serviceUrl: string;
4548
cacheHandler?: CacheHandler;
4649
}): Promise<void> {
47-
const { instanceId, cacheHandler } = args;
48-
const definitionId =
49-
await this.dataIndexService.fetchDefinitionIdByInstanceId(instanceId);
50+
const { definitionId, cacheHandler } = args;
5051
const isWorkflowAvailable = this.workflowCacheService.isAvailable(
5152
definitionId,
5253
cacheHandler,
5354
);
5455
return isWorkflowAvailable
55-
? await this.dataIndexService.abortWorkflowInstance(instanceId)
56+
? await this.sonataFlowService.abortInstance(args)
5657
: undefined;
5758
}
5859

workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.ts

+25
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,31 @@ export class SonataFlowService {
166166
return true;
167167
}
168168

169+
public async abortInstance(args: {
170+
definitionId: string;
171+
instanceId: string;
172+
serviceUrl: string;
173+
}): Promise<void> {
174+
const urlToFetch = `${args.serviceUrl}/management/processes/${args.definitionId}/instances/${args.instanceId}`;
175+
176+
const response = await fetch(urlToFetch, {
177+
method: 'DELETE',
178+
});
179+
180+
if (!response.ok) {
181+
const json = await response.json();
182+
this.logger.error(`Abort failed with: ${JSON.stringify(json)}`);
183+
throw new Error(
184+
`${await this.createPrefixFetchErrorMessage(
185+
urlToFetch,
186+
response,
187+
json,
188+
'DELETE',
189+
)}`,
190+
);
191+
}
192+
}
193+
169194
public async fetchWorkflowOverview(
170195
definitionId: string,
171196
): Promise<WorkflowOverview | undefined> {

workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ describe('abortWorkflow', () => {
548548
const expectedResult = `Workflow instance ${workflowId} successfully aborted`;
549549

550550
// Act
551-
const actualResult: string = await v2.abortWorkflow(workflowId);
551+
const actualResult: string = await v2.abortWorkflow('dummy', workflowId);
552552

553553
// Assert
554554
expect(actualResult).toBeDefined();
@@ -562,7 +562,7 @@ describe('abortWorkflow', () => {
562562
).mockRejectedValue(new Error('Simulated abort workflow error'));
563563

564564
// Act
565-
const promise = v2.abortWorkflow('instanceId');
565+
const promise = v2.abortWorkflow('definitionId', 'instanceId');
566566

567567
// Assert
568568
await expect(promise).rejects.toThrow('Simulated abort workflow error');

workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import { ParsedRequest } from 'openapi-backend';
1718

1819
import {
@@ -230,9 +231,24 @@ export class V2 {
230231
}
231232
}
232233

233-
public async abortWorkflow(instanceId: string): Promise<string> {
234+
public async abortWorkflow(
235+
workflowId: string,
236+
instanceId: string,
237+
): Promise<string> {
238+
const definition = await this.orchestratorService.fetchWorkflowInfo({
239+
definitionId: workflowId,
240+
cacheHandler: 'throw',
241+
});
242+
if (!definition) {
243+
throw new Error(`Couldn't fetch workflow definition for ${workflowId}`);
244+
}
245+
if (!definition.serviceUrl) {
246+
throw new Error(`ServiceURL is not defined for workflow ${workflowId}`);
247+
}
234248
await this.orchestratorService.abortWorkflowInstance({
235-
instanceId,
249+
definitionId: workflowId,
250+
instanceId: instanceId,
251+
serviceUrl: definition.serviceUrl,
236252
cacheHandler: 'throw',
237253
});
238254
return `Workflow instance ${instanceId} successfully aborted`;

workspaces/orchestrator/plugins/orchestrator-backend/src/service/router.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Backstage Authors
2+
* Copyright Red Hat, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';
1718
import {
1819
HttpAuthService,
@@ -897,7 +898,7 @@ function setupInternalRoutes(
897898
manageDenyAuthorization(endpointName, endpoint, _req);
898899
}
899900

900-
const result = await routerApi.v2.abortWorkflow(instanceId);
901+
const result = await routerApi.v2.abortWorkflow(workflowId, instanceId);
901902
res.status(200).json(result);
902903
} catch (error) {
903904
auditLogRequestError(error, endpointName, endpoint, _req);

0 commit comments

Comments
 (0)