1919package org .dependencytrack .resources .v2 ;
2020
2121import alpine .server .auth .PermissionRequired ;
22+ import com .fasterxml .jackson .core .type .TypeReference ;
23+ import com .fasterxml .jackson .databind .ObjectMapper ;
24+ import com .google .protobuf .util .JsonFormat ;
2225import jakarta .inject .Inject ;
2326import jakarta .ws .rs .NotFoundException ;
2427import jakarta .ws .rs .Path ;
2528import jakarta .ws .rs .core .Response ;
2629import org .dependencytrack .api .v2 .WorkflowsApi ;
30+ import org .dependencytrack .api .v2 .model .ListWorkflowRunEventsResponse ;
31+ import org .dependencytrack .api .v2 .model .ListWorkflowRunEventsResponseItem ;
2732import org .dependencytrack .api .v2 .model .ListWorkflowRunsResponse ;
2833import org .dependencytrack .api .v2 .model .SortDirection ;
2934import org .dependencytrack .api .v2 .model .WorkflowRunStatus ;
3035import org .dependencytrack .auth .Permissions ;
3136import org .dependencytrack .common .pagination .Page ;
3237import org .dependencytrack .dex .engine .api .DexEngine ;
38+ import org .dependencytrack .dex .engine .api .WorkflowRunHistoryEntry ;
3339import org .dependencytrack .dex .engine .api .WorkflowRunMetadata ;
40+ import org .dependencytrack .dex .engine .api .request .ListWorkflowRunHistoryRequest ;
3441import org .dependencytrack .dex .engine .api .request .ListWorkflowRunsRequest ;
3542import org .dependencytrack .resources .AbstractApiResource ;
3643import org .jspecify .annotations .NullMarked ;
3744import org .jspecify .annotations .Nullable ;
3845
46+ import java .io .IOException ;
47+ import java .io .UncheckedIOException ;
3948import java .time .Instant ;
49+ import java .util .Map ;
50+ import java .util .UUID ;
4051
4152@ Path ("/" )
4253@ NullMarked
4354public class WorkflowsResource extends AbstractApiResource implements WorkflowsApi {
4455
4556 private final DexEngine dexEngine ;
57+ private final ObjectMapper objectMapper ;
4658
4759 @ Inject
48- WorkflowsResource (DexEngine dexEngine ) {
60+ WorkflowsResource (DexEngine dexEngine , ObjectMapper objectMapper ) {
4961 this .dexEngine = dexEngine ;
62+ this .objectMapper = objectMapper ;
5063 }
5164
5265 @ Override
@@ -105,11 +118,7 @@ public Response listWorkflowRuns(
105118 case "completed_at" -> ListWorkflowRunsRequest .SortBy .COMPLETED_AT ;
106119 case null , default -> null ;
107120 })
108- .withSortDirection (switch (sortDirection ) {
109- case ASC -> org .dependencytrack .common .pagination .SortDirection .ASC ;
110- case DESC -> org .dependencytrack .common .pagination .SortDirection .DESC ;
111- case null -> null ;
112- })
121+ .withSortDirection (convert (sortDirection ))
113122 .withPageToken (pageToken )
114123 .withLimit (limit ));
115124
@@ -123,6 +132,35 @@ public Response listWorkflowRuns(
123132 return Response .ok (response ).build ();
124133 }
125134
135+ @ Override
136+ @ PermissionRequired ({
137+ Permissions .Constants .SYSTEM_CONFIGURATION ,
138+ Permissions .Constants .SYSTEM_CONFIGURATION_READ
139+ })
140+ public Response listWorkflowRunEvents (
141+ UUID id ,
142+ @ Nullable Integer fromSequenceNumber ,
143+ Integer limit ,
144+ @ Nullable String pageToken ,
145+ @ Nullable SortDirection sortDirection ) {
146+ final Page <WorkflowRunHistoryEntry > historyEntryPage =
147+ dexEngine .listRunHistory (
148+ new ListWorkflowRunHistoryRequest (id )
149+ .withFromSequenceNumber (fromSequenceNumber )
150+ .withSortDirection (convert (sortDirection ))
151+ .withPageToken (pageToken )
152+ .withLimit (limit ));
153+
154+ final var response = ListWorkflowRunEventsResponse .builder ()
155+ .events (historyEntryPage .items ().stream ()
156+ .map (entry -> convert (entry , objectMapper ))
157+ .toList ())
158+ .pagination (createPaginationMetadata (getUriInfo (), historyEntryPage ))
159+ .build ();
160+
161+ return Response .ok (response ).build ();
162+ }
163+
126164 private static org .dependencytrack .dex .engine .api .@ Nullable WorkflowRunStatus convert (@ Nullable WorkflowRunStatus status ) {
127165 return switch (status ) {
128166 case CANCELLED -> org .dependencytrack .dex .engine .api .WorkflowRunStatus .CANCELLED ;
@@ -170,4 +208,31 @@ private static org.dependencytrack.api.v2.model.WorkflowRunMetadata convert(Work
170208 .build ();
171209 }
172210
211+ private static ListWorkflowRunEventsResponseItem convert (
212+ WorkflowRunHistoryEntry entry ,
213+ ObjectMapper objectMapper ) {
214+ final Map <String , Object > eventJsonMap ;
215+ try {
216+ final String eventJson = JsonFormat .printer ().print (entry .event ());
217+ eventJsonMap = objectMapper .readValue (eventJson , new TypeReference <>() {
218+ });
219+ } catch (IOException e ) {
220+ throw new UncheckedIOException (e );
221+ }
222+
223+ return ListWorkflowRunEventsResponseItem .builder ()
224+ .sequenceNumber (entry .sequenceNumber ())
225+ .event (eventJsonMap )
226+ .build ();
227+ }
228+
229+ private static org .dependencytrack .common .pagination .@ Nullable SortDirection convert (
230+ @ Nullable SortDirection sortDirection ) {
231+ return switch (sortDirection ) {
232+ case ASC -> org .dependencytrack .common .pagination .SortDirection .ASC ;
233+ case DESC -> org .dependencytrack .common .pagination .SortDirection .DESC ;
234+ case null -> null ;
235+ };
236+ }
237+
173238}
0 commit comments