1818 */
1919package org .dependencytrack .resources .v1 ;
2020
21- import java .util .HashSet ;
22- import java .util .Set ;
23- import java .util .UUID ;
24-
21+ import alpine .common .util .UuidUtil ;
22+ import alpine .notification .NotificationLevel ;
23+ import alpine .security .crypto .DataEncryption ;
24+ import alpine .server .filters .ApiFilter ;
25+ import alpine .server .filters .AuthenticationFilter ;
26+ import com .github .tomakehurst .wiremock .WireMockServer ;
27+ import com .github .tomakehurst .wiremock .client .WireMock ;
2528import org .dependencytrack .JerseyTestRule ;
2629import org .dependencytrack .ResourceTest ;
2730import org .dependencytrack .model .ConfigPropertyConstants ;
4043import org .junit .ClassRule ;
4144import org .junit .Test ;
4245
43- import alpine .common .util .UuidUtil ;
44- import alpine .notification .NotificationLevel ;
45- import alpine .server .filters .ApiFilter ;
46- import alpine .server .filters .AuthenticationFilter ;
4746import jakarta .json .JsonArray ;
4847import jakarta .json .JsonObject ;
4948import jakarta .ws .rs .client .Entity ;
5049import jakarta .ws .rs .core .Form ;
5150import jakarta .ws .rs .core .MediaType ;
5251import jakarta .ws .rs .core .Response ;
52+ import java .time .Duration ;
53+ import java .util .HashSet ;
54+ import java .util .Set ;
55+ import java .util .UUID ;
56+
57+ import static com .github .tomakehurst .wiremock .client .WireMock .aResponse ;
58+ import static com .github .tomakehurst .wiremock .client .WireMock .equalToJson ;
59+ import static com .github .tomakehurst .wiremock .client .WireMock .postRequestedFor ;
60+ import static com .github .tomakehurst .wiremock .client .WireMock .urlPathEqualTo ;
61+ import static com .github .tomakehurst .wiremock .core .WireMockConfiguration .options ;
62+ import static org .assertj .core .api .Assertions .assertThat ;
63+ import static org .awaitility .Awaitility .await ;
5364
5465public class NotificationPublisherResourceTest extends ResourceTest {
5566
@@ -139,7 +150,7 @@ public void createNotificationPublisherWithExistingNameTest() {
139150 .put (Entity .entity (publisher , MediaType .APPLICATION_JSON ));
140151 Assert .assertEquals (409 , response .getStatus (), 0 );
141152 String body = getPlainTextBody (response );
142- Assert .assertEquals ("The notification with the name " + DefaultNotificationPublishers .SLACK .getPublisherName ()+ " already exist" , body );
153+ Assert .assertEquals ("The notification with the name " + DefaultNotificationPublishers .SLACK .getPublisherName () + " already exist" , body );
143154 }
144155
145156 @ Test
@@ -156,7 +167,7 @@ public void createNotificationPublisherWithClassNotImplementingPublisherInterfac
156167 .put (Entity .entity (publisher , MediaType .APPLICATION_JSON ));
157168 Assert .assertEquals (400 , response .getStatus (), 0 );
158169 String body = getPlainTextBody (response );
159- Assert .assertEquals ("The class " + NotificationPublisherResource .class .getName ()+ " does not implement " + Publisher .class .getName (), body );
170+ Assert .assertEquals ("The class " + NotificationPublisherResource .class .getName () + " does not implement " + Publisher .class .getName (), body );
160171 }
161172
162173 @ Test
@@ -245,7 +256,7 @@ public void updateNotificationPublisherWithNameOfAnotherNotificationPublisherTes
245256 Assert .assertEquals (409 , response .getStatus (), 0 );
246257 Assert .assertNull (response .getHeaderString (TOTAL_COUNT_HEADER ));
247258 String body = getPlainTextBody (response );
248- Assert .assertEquals ("An existing publisher with the name '" + DefaultNotificationPublishers .MS_TEAMS .getPublisherName ()+ "' already exist" , body );
259+ Assert .assertEquals ("An existing publisher with the name '" + DefaultNotificationPublishers .MS_TEAMS .getPublisherName () + "' already exist" , body );
249260 }
250261
251262 @ Test
@@ -279,7 +290,7 @@ public void updateNotificationPublisherWithClassNotImplementingPublisherInterfac
279290 Assert .assertEquals (400 , response .getStatus (), 0 );
280291 Assert .assertNull (response .getHeaderString (TOTAL_COUNT_HEADER ));
281292 String body = getPlainTextBody (response );
282- Assert .assertEquals ("The class " + NotificationPublisherResource .class .getName ()+ " does not implement " + Publisher .class .getName (), body );
293+ Assert .assertEquals ("The class " + NotificationPublisherResource .class .getName () + " does not implement " + Publisher .class .getName (), body );
283294 }
284295
285296 @ Test
@@ -350,28 +361,106 @@ public void testNotificationRuleTest() {
350361 "Example Publisher" , "Publisher description" ,
351362 SlackPublisher .class , "template" , "text/html" ,
352363 false );
353-
364+
354365 NotificationRule rule = qm .createNotificationRule ("Example Rule 1" , NotificationScope .PORTFOLIO , NotificationLevel .INFORMATIONAL , publisher );
355366
356367 Set <NotificationGroup > groups = new HashSet <>(Set .of (NotificationGroup .BOM_CONSUMED , NotificationGroup .BOM_PROCESSED , NotificationGroup .BOM_PROCESSING_FAILED ,
357- NotificationGroup .BOM_VALIDATION_FAILED , NotificationGroup .NEW_VULNERABILITY , NotificationGroup .NEW_VULNERABLE_DEPENDENCY ,
358- NotificationGroup .POLICY_VIOLATION , NotificationGroup .PROJECT_CREATED , NotificationGroup .PROJECT_AUDIT_CHANGE ,
359- NotificationGroup .VEX_CONSUMED , NotificationGroup .VEX_PROCESSED ));
368+ NotificationGroup .BOM_VALIDATION_FAILED , NotificationGroup .NEW_VULNERABILITY , NotificationGroup .NEW_VULNERABLE_DEPENDENCY ,
369+ NotificationGroup .POLICY_VIOLATION , NotificationGroup .PROJECT_CREATED , NotificationGroup .PROJECT_AUDIT_CHANGE ,
370+ NotificationGroup .VEX_CONSUMED , NotificationGroup .VEX_PROCESSED ));
360371 rule .setNotifyOn (groups );
361372
362373 rule .setPublisherConfig ("{\" destination\" :\" https://example.com/webhook\" }" );
363-
374+
364375 Response sendMailResponse = jersey .target (V1_NOTIFICATION_PUBLISHER + "/test/" + rule .getUuid ()).request ()
365376 .header (X_API_KEY , apiKey )
366377 .post (Entity .entity ("" , MediaType .APPLICATION_FORM_URLENCODED_TYPE ));
367-
378+
368379 Assert .assertEquals (200 , sendMailResponse .getStatus ());
369380 }
370381
382+ @ Test
383+ public void testNotificationRuleJiraTest () throws Exception {
384+ new DefaultObjectGenerator ().loadDefaultNotificationPublishers ();
385+
386+ final NotificationPublisher jiraPublisher = qm .getNotificationPublisher (
387+ DefaultNotificationPublishers .JIRA .getPublisherName ());
388+ assertThat (jiraPublisher ).isNotNull ();
389+
390+ final var notificationRule = new NotificationRule ();
391+ notificationRule .setPublisher (jiraPublisher );
392+ notificationRule .setPublisherConfig ("""
393+ {
394+ "destination": "FOO",
395+ "jiraTicketType": "Task"
396+ }
397+ """ );
398+ notificationRule .setName ("Jira Test" );
399+ notificationRule .setNotifyOn (Set .of (NotificationGroup .NEW_VULNERABILITY ));
400+ notificationRule .setNotificationLevel (NotificationLevel .INFORMATIONAL );
401+ notificationRule .setScope (NotificationScope .PORTFOLIO );
402+ qm .persist (notificationRule );
403+
404+ final var wireMock = new WireMockServer (options ().dynamicPort ());
405+ wireMock .start ();
406+
407+ try {
408+ qm .createConfigProperty (
409+ ConfigPropertyConstants .JIRA_URL .getGroupName (),
410+ ConfigPropertyConstants .JIRA_URL .getPropertyName (),
411+ wireMock .baseUrl (),
412+ ConfigPropertyConstants .JIRA_URL .getPropertyType (),
413+ ConfigPropertyConstants .JIRA_URL .getDescription ());
414+ qm .createConfigProperty (
415+ ConfigPropertyConstants .JIRA_PASSWORD .getGroupName (),
416+ ConfigPropertyConstants .JIRA_PASSWORD .getPropertyName (),
417+ DataEncryption .encryptAsString ("authToken" ),
418+ ConfigPropertyConstants .JIRA_PASSWORD .getPropertyType (),
419+ ConfigPropertyConstants .JIRA_PASSWORD .getDescription ());
420+
421+ wireMock .stubFor (WireMock .post (WireMock .anyUrl ())
422+ .willReturn (aResponse ()
423+ .withStatus (200 )));
424+
425+ final Response response = jersey .target (V1_NOTIFICATION_PUBLISHER + "/test/" + notificationRule .getUuid ()).request ()
426+ .header (X_API_KEY , apiKey )
427+ .post (null );
428+ assertThat (response .getStatus ()).isEqualTo (200 );
429+
430+ await ("Notification Delivery" )
431+ .atMost (Duration .ofSeconds (5 ))
432+ .untilAsserted (() -> wireMock .verify (postRequestedFor (urlPathEqualTo ("/rest/api/2/issue" ))
433+ .withRequestBody (equalToJson ("""
434+ {
435+ "fields" : {
436+ "project" : {
437+ "key" : "FOO"
438+ },
439+ "issuetype" : {
440+ "name" : "Task"
441+ },
442+ "summary" : "[Dependency-Track] [NEW_VULNERABILITY] [MEDIUM] New medium vulnerability identified: INT-001",
443+ "description" : "A new vulnerability has been identified on your project(s).\\ n\\ \\ \\ \\ \\ n\\ \\ \\ \\ \\ n*Vulnerability description*\\ n{code:none|bgColor=white|borderStyle=none}{code}\\ n\\ n*VulnID*\\ nINT-001\\ n\\ n*Severity*\\ nMedium\\ n\\ n*Component*\\ n[componentName : componentVersion|/components/94f87321-a5d1-4c2f-b2fe-95165debebc6]\\ n\\ n*Affected project(s)*\\ n- [projectName (projectVersion)|/projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95]\\ n"
444+ }
445+ }
446+ """ ))));
447+ } finally {
448+ wireMock .stop ();
449+ }
450+ }
451+
452+ @ Test
453+ public void testNotificationRuleNotFoundTest () {
454+ final Response response = jersey .target (V1_NOTIFICATION_PUBLISHER + "/test/" + UUID .randomUUID ()).request ()
455+ .header (X_API_KEY , apiKey )
456+ .post (null );
457+ assertThat (response .getStatus ()).isEqualTo (404 );
458+ }
459+
371460 @ Test
372461 public void restoreDefaultTemplatesTest () {
373462 NotificationPublisher slackPublisher = qm .getDefaultNotificationPublisher (DefaultNotificationPublishers .SLACK .getPublisherClass ());
374- slackPublisher .setName (slackPublisher .getName ()+ " Updated" );
463+ slackPublisher .setName (slackPublisher .getName () + " Updated" );
375464 qm .persist (slackPublisher );
376465 qm .detach (NotificationPublisher .class , slackPublisher .getId ());
377466 qm .createConfigProperty (
0 commit comments