77import org .apache .commons .logging .LogFactory ;
88import org .folio .rest .workflow .dto .WorkflowDto ;
99import org .folio .rest .workflow .dto .WorkflowOperationalDto ;
10+ import org .folio .rest .workflow .exception .WorkflowDeploymentNotFound ;
1011import org .folio .rest .workflow .exception .WorkflowEngineServiceException ;
1112import org .folio .rest .workflow .exception .WorkflowNotFoundException ;
1213import org .folio .rest .workflow .model .Workflow ;
1314import org .folio .rest .workflow .model .repo .WorkflowRepo ;
14- import org .mockito .ArgumentMatchers ;
1515import org .springframework .beans .factory .annotation .Value ;
1616import org .springframework .boot .restclient .RestTemplateBuilder ;
1717import org .springframework .http .HttpEntity ;
2020import org .springframework .http .HttpStatus ;
2121import org .springframework .http .ResponseEntity ;
2222import org .springframework .stereotype .Service ;
23+ import org .springframework .web .client .RestClientException ;
2324import org .springframework .web .client .RestTemplate ;
2425import tools .jackson .databind .JsonNode ;
2526import tools .jackson .databind .json .JsonMapper ;
@@ -95,13 +96,34 @@ public Workflow deactivate(String workflowId, String tenant, String token)
9596 public void delete (String workflowId , String tenant , String token )
9697 throws WorkflowEngineServiceException {
9798
98- final ArrayNode node = fetchProcessInstanceHistory (workflowId , tenant , token );
99-
100- if (!node .isEmpty ()) {
101- try {
102- deactivate (workflowId , tenant , token );
103- } catch (WorkflowEngineServiceException e ) {
104- throw new WorkflowEngineServiceException (String .format ("Failed to delete Workflow '%s' due to deactivation failure: %s!" , workflowId , e .getMessage ()), e );
99+ final WorkflowOperationalDto workflow = workflowRepo .getViewById (workflowId , WorkflowOperationalDto .class );
100+ final String id = workflow .getDeploymentId ();
101+ final String version = workflow .getVersionTag ();
102+
103+ // Deployment ID will not exist if it has never been activated.
104+ if (id != null ) {
105+ final ResponseEntity <ArrayNode > response = fetchDeploymentDefinitions (id , version , tenant , token );
106+
107+ if (response .getStatusCode () == HttpStatus .OK || response .getStatusCode () == HttpStatus .NOT_FOUND ) {
108+ final ArrayNode definitions = !response .hasBody ()
109+ ? null
110+ : response .getBody ();
111+
112+ if (definitions != null && response .getStatusCode () != HttpStatus .NOT_FOUND && !definitions .isEmpty ()) {
113+ try {
114+ deactivate (workflowId , tenant , token );
115+ } catch (WorkflowEngineServiceException e ) {
116+ final String message = String .format (
117+ "Failed to delete Workflow ID '%s' for Deployment ID '%s' and Version Tag '%s' due to deactivation failure: %s!" ,
118+ workflowId ,
119+ id ,
120+ version ,
121+ e .getMessage ()
122+ );
123+
124+ throw new WorkflowEngineServiceException (message , e );
125+ }
126+ }
105127 }
106128 }
107129
@@ -122,13 +144,23 @@ public void exists(String workflowId) throws WorkflowNotFoundException {
122144 }
123145
124146 public JsonNode start (String workflowId , String tenant , String token , JsonNode context )
125- throws WorkflowEngineServiceException {
147+ throws WorkflowDeploymentNotFound , WorkflowEngineServiceException , WorkflowNotFoundException {
126148
127149 WorkflowOperationalDto workflow = workflowRepo .getViewById (workflowId , WorkflowOperationalDto .class );
150+
151+ if (workflow == null ) {
152+ throw new WorkflowNotFoundException (String .format ("Workflow ID '%s'" , workflowId ));
153+ }
154+
128155 String id = workflow .getDeploymentId ();
129156 String version = workflow .getVersionTag ();
130157
131- JsonNode definition = fetchDeploymentDefinition (id , version , tenant , token );
158+ JsonNode definition = fetchFirstDeploymentDefinition (workflowId , id , version , tenant , token );
159+
160+ if (!definition .has ("id" ) ) {
161+ throw new WorkflowEngineServiceException (String .format ("Workflow ID '%s' with Deployment ID '%s' has no definition ID!" , workflowId , id ));
162+ }
163+
132164 String definitionId = definition .get ("id" ).asString ();
133165
134166 HttpEntity <JsonNode > contextHttpEntity = new HttpEntity <>(context , headers (tenant , token ));
@@ -137,20 +169,26 @@ public JsonNode start(String workflowId, String tenant, String token, JsonNode c
137169 Map <String , Object > params = Map .of ("arg1" , definitionId );
138170
139171 try {
140- ResponseEntity <JsonNode > response = exchange (url , HttpMethod .POST , contextHttpEntity , JsonNode .class , params );
172+ final ResponseEntity <JsonNode > response = exchange (url , HttpMethod .POST , contextHttpEntity , JsonNode .class , params );
173+
174+ if (response .getStatusCode () == HttpStatus .NOT_FOUND ) {
175+ throw new WorkflowNotFoundException (String .format ("Workflow ID '%s'" , workflowId ));
176+ }
141177
142178 return response .getBody ();
143- } catch (Exception e ) {
179+ } catch (RestClientException e ) {
144180 throw new WorkflowEngineServiceException (String .format ("Failed to start workflow: %s!" , e .getMessage ()), e );
145181 }
146182 }
147183
148- public JsonNode history (String workflowId , String tenant , String token ) throws WorkflowEngineServiceException {
184+ public JsonNode history (String workflowId , String tenant , String token )
185+ throws WorkflowDeploymentNotFound , WorkflowEngineServiceException {
186+
149187 WorkflowOperationalDto workflow = workflowRepo .getViewById (workflowId , WorkflowOperationalDto .class );
150- String deploymentId = workflow .getDeploymentId ();
188+ String id = workflow .getDeploymentId ();
151189 String version = workflow .getVersionTag ();
152190
153- JsonNode processDefinition = fetchDeploymentDefinition ( deploymentId , version , tenant , token );
191+ JsonNode processDefinition = fetchFirstDeploymentDefinition ( workflowId , id , version , tenant , token );
154192 String processDefinitionId = processDefinition .get ("id" ).asString ();
155193
156194 ArrayNode instances = fetchProcessInstanceHistory (processDefinitionId , tenant , token );
@@ -168,24 +206,66 @@ public JsonNode history(String workflowId, String tenant, String token) throws W
168206 return instances ;
169207 }
170208
171- private JsonNode fetchDeploymentDefinition (String deploymentId , String version , String tenant , String token )
172- throws WorkflowEngineServiceException {
173-
174- HttpEntity <Void > httpEntity = new HttpEntity <>(headers (tenant , token ));
209+ /**
210+ * Fetch the first matching deployment for the given workflow.
211+ *
212+ * @param workflowId The Workflow ID, for use in exception logs.
213+ * @param deploymentId The Deployment ID to find.
214+ * @param version The version tag to use.
215+ * @param tenant The FOLIO tenant.
216+ * @param token The session token.
217+ *
218+ * @return The first matching response.
219+ *
220+ * @throws WorkflowDeploymentNotFound On not found.
221+ * @throws WorkflowEngineServiceException On error.
222+ */
223+ private JsonNode fetchFirstDeploymentDefinition (String workflowId , String deploymentId , String version , String tenant , String token )
224+ throws WorkflowDeploymentNotFound , WorkflowEngineServiceException {
175225
176- String url = String .format (PROCESS_DEFINITION_GET_URL_TEMPLATE , okapiUrl , restPath );
177- Map <String , Object > params = Map .of ("arg1" , deploymentId , "arg2" , version );
226+ final ResponseEntity <ArrayNode > response = fetchDeploymentDefinitions (deploymentId , version , tenant , token );
178227
179- try {
180- ResponseEntity <ArrayNode > response = exchange (url , HttpMethod .GET , httpEntity , ArrayNode .class , params );
228+ if (response .getStatusCode () == HttpStatus .OK || response .getStatusCode () == HttpStatus .NOT_FOUND ) {
229+ final ArrayNode definitions = !response .hasBody ()
230+ ? null
231+ : response .getBody ();
181232
182- ArrayNode definitions = response .getBody ();
183- if (response .getStatusCode () == HttpStatus .OK && definitions != null && !definitions .isEmpty ()) {
184- return definitions .get (0 );
233+ if (definitions == null || response .getStatusCode () == HttpStatus .NOT_FOUND || definitions .isEmpty () || definitions .get (0 ) == null ) {
234+ throw new WorkflowDeploymentNotFound (String .format ("Workflow with Deployment ID '%s' for Workflow ID '%s' is not found." , deploymentId , workflowId ));
185235 }
186236
187- throw new WorkflowEngineServiceException ("Unable to get workflow process definition from workflow engine!" );
188- } catch (Exception e ) {
237+ return definitions .get (0 );
238+ }
239+
240+ throw new WorkflowEngineServiceException ("Unable to get workflow process definition from workflow engine!" );
241+ }
242+
243+ /**
244+ * Fetch all deployments, but return the response entity to allow caller to handle.
245+ *
246+ * @param deploymentId The activated Workflow deployment ID.
247+ * @param version The Workflow version number.
248+ * @param tenant The tenant.
249+ * @param token The token.
250+ *
251+ * @return The response entity.
252+ *
253+ * @throws WorkflowEngineServiceException On error.
254+ */
255+ private ResponseEntity <ArrayNode > fetchDeploymentDefinitions (String deploymentId , String version , String tenant , String token )
256+ throws WorkflowEngineServiceException {
257+
258+ if (deploymentId == null ) {
259+ throw new WorkflowEngineServiceException ("Failed to deployment definition: Deployment ID is missing!" );
260+ }
261+
262+ final HttpEntity <Void > httpEntity = new HttpEntity <>(headers (tenant , token ));
263+ final String url = String .format (PROCESS_DEFINITION_GET_URL_TEMPLATE , okapiUrl , restPath );
264+ final Map <String , Object > params = Map .of ("arg1" , deploymentId , "arg2" , version );
265+
266+ try {
267+ return exchange (url , HttpMethod .GET , httpEntity , ArrayNode .class , params );
268+ } catch (RestClientException e ) {
189269 throw new WorkflowEngineServiceException (String .format ("Failed to deployment definition: %s!" , e .getMessage ()), e );
190270 }
191271 }
@@ -207,7 +287,7 @@ private ArrayNode fetchProcessInstanceHistory(String processDefinitionId, String
207287 }
208288
209289 throw new WorkflowEngineServiceException ("Unable to get workflow process instance history from workflow engine!" );
210- } catch (Exception e ) {
290+ } catch (RestClientException e ) {
211291 throw new WorkflowEngineServiceException (String .format ("Failed to fetch process instance history: %s!" , e .getMessage ()), e );
212292 }
213293 }
@@ -230,7 +310,7 @@ private ArrayNode fetchIncidentsHistory(String processInstanceId, String tenant,
230310 }
231311
232312 return incidents ;
233- } catch (Exception e ) {
313+ } catch (RestClientException e ) {
234314 throw new WorkflowEngineServiceException (String .format ("Failed to fetch incident history: %s!" , e .getMessage ()), e );
235315 }
236316 }
@@ -273,7 +353,7 @@ private Workflow sendWorkflowRequest(WorkflowDto workflow, String requestPath, S
273353 return workflowRepo .save (responseWorkflow );
274354 }
275355 }
276- } catch (Exception e ) {
356+ } catch (RestClientException e ) {
277357 throw new WorkflowEngineServiceException (String .format ("Failed to send workflow request: %s!" , e .getMessage ()), e );
278358 }
279359
@@ -296,9 +376,9 @@ private HttpHeaders headers(String tenant, String token) {
296376 return requestHeaders ;
297377 }
298378
299- private <T > ResponseEntity <T > exchange (String url , HttpMethod method , HttpEntity <?> request , Class <T > responseType , Map <String , ?> params ) {
300- LOG .debug (String .format ("Exchange for %s %s %s" , responseType .getSimpleName (), method , url ));
301- return this .restTemplate .exchange (url , method , request , responseType , ( Object []) new String [ 0 ], params );
379+ private <T > ResponseEntity <T > exchange (String url , HttpMethod method , HttpEntity <?> request , Class <T > responseType , Map <String , ? extends Object > params ) {
380+ LOG .debug (String .format ("Exchange for %s %s %s %s " , responseType .getSimpleName (), method , url , params ));
381+ return this .restTemplate .exchange (url , method , request , responseType , params );
302382 }
303383
304384}
0 commit comments