Skip to content

Commit 390d7ca

Browse files
committed
Fix notification tests not working for Jira
While assembling the configuration of the publisher under test, values defined on the notification rule config were partially dropped. This caused e.g. `jiraTicketType` to not be forwarded correctly. Fixes #4237 Signed-off-by: nscuro <[email protected]>
1 parent e09099c commit 390d7ca

File tree

2 files changed

+144
-42
lines changed

2 files changed

+144
-42
lines changed

src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
3535
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
3636
import io.swagger.v3.oas.annotations.tags.Tag;
37-
3837
import org.dependencytrack.auth.Permissions;
3938
import org.dependencytrack.model.ConfigPropertyConstants;
4039
import org.dependencytrack.model.NotificationPublisher;
@@ -340,38 +339,52 @@ public Response testSmtpPublisherConfig(@FormParam("destination") String destina
340339
@ApiResponse(responseCode = "401", description = "Unauthorized")
341340
})
342341
@PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION)
343-
public Response testSlackPublisherConfig(
342+
public Response testNotificationRule(
344343
@Parameter(description = "The UUID of the rule to test", schema = @Schema(type = "string", format = "uuid"), required = true)
345344
@PathParam("uuid") @ValidUuid String ruleUuid) throws Exception {
346-
try(QueryManager qm = new QueryManager()){
347-
NotificationRule rule = qm.getObjectByUuid(NotificationRule.class, ruleUuid);
348-
NotificationPublisher notificationPublisher = rule.getPublisher();
349-
final Class<?> publisherClass = Class.forName(notificationPublisher.getPublisherClass());
350-
Publisher publisher = (Publisher) publisherClass.getDeclaredConstructor().newInstance();
351-
String publisherConfig = rule.getPublisherConfig();
352-
JsonReader jsonReader = Json.createReader(new StringReader(publisherConfig));
353-
JsonObject configObject = jsonReader.readObject();
354-
jsonReader.close();
355-
final JsonObject config = Json.createObjectBuilder()
356-
.add(Publisher.CONFIG_DESTINATION, configObject.getString("destination"))
345+
try (final var qm = new QueryManager()) {
346+
final NotificationRule rule = qm.getObjectByUuid(NotificationRule.class, ruleUuid);
347+
if (rule == null) {
348+
return Response.status(Response.Status.NOT_FOUND).build();
349+
}
350+
351+
final JsonObject publisherConfig;
352+
final String publisherConfigJson = rule.getPublisherConfig() != null ? rule.getPublisherConfig() : "{}";
353+
try (final JsonReader jsonReader = Json.createReader(new StringReader(publisherConfigJson))) {
354+
publisherConfig = jsonReader.readObject();
355+
}
356+
357+
final JsonObject config = Json.createObjectBuilder(publisherConfig)
357358
.add(Publisher.CONFIG_TEMPLATE_KEY, rule.getPublisher().getTemplate())
358359
.add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, rule.getPublisher().getTemplateMimeType())
359360
.build();
360-
361-
for(NotificationGroup group : rule.getNotifyOn()){
361+
362+
final Class<?> publisherClass = Class.forName(rule.getPublisher().getPublisherClass());
363+
final Publisher publisher = (Publisher) publisherClass.getDeclaredConstructor().newInstance();
364+
365+
for (NotificationGroup group : rule.getNotifyOn()) {
362366
final Notification notification = new Notification()
363-
.scope(rule.getScope())
364-
.group(group.toString())
365-
.title(group)
366-
.content("Rule configuration test")
367-
.level(rule.getNotificationLevel())
368-
.subject(NotificationUtil.generateSubject(group.toString()));
367+
.scope(rule.getScope())
368+
.group(group.toString())
369+
.title(group)
370+
.content("Rule configuration test")
371+
.level(rule.getNotificationLevel())
372+
.subject(NotificationUtil.generateSubject(group.toString()));
373+
369374
publisher.inform(PublishContext.from(notification), notification, config);
370375
}
376+
371377
return Response.ok().build();
372-
} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
378+
} catch (
379+
InvocationTargetException
380+
| InstantiationException
381+
| IllegalAccessException
382+
| NoSuchMethodException e) {
373383
LOGGER.error(e.getMessage(), e);
374-
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending the notification.").build();
384+
return Response
385+
.status(Response.Status.INTERNAL_SERVER_ERROR)
386+
.entity("Exception occurred while sending the notification.")
387+
.build();
375388
}
376389
}
377390
}

src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java

Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
*/
1919
package 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;
2528
import org.dependencytrack.JerseyTestRule;
2629
import org.dependencytrack.ResourceTest;
2730
import org.dependencytrack.model.ConfigPropertyConstants;
@@ -40,16 +43,24 @@
4043
import org.junit.ClassRule;
4144
import 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;
4746
import jakarta.json.JsonArray;
4847
import jakarta.json.JsonObject;
4948
import jakarta.ws.rs.client.Entity;
5049
import jakarta.ws.rs.core.Form;
5150
import jakarta.ws.rs.core.MediaType;
5251
import 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

5465
public 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

Comments
 (0)