Skip to content

Commit

Permalink
support for parent-child-relationships of projects
Browse files Browse the repository at this point in the history
with Dependency-Track v4.7 and newer

closes #139
  • Loading branch information
sephiroth-j committed Jan 31, 2023
1 parent 3abc68a commit 65059a1
Show file tree
Hide file tree
Showing 13 changed files with 92 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Unreleased
### ⚠ Breaking
### ⭐ New Features
- Added support for parent-child-relationships of projects with Dependency-Track v4.7 and newer (fixes [#139](https://github.com/jenkinsci/dependency-track-plugin/issues/139))

### 🐞 Bugs Fixed
- Searching on the result page was partially broken due to [a bug in bootstrap-vue 2.22+](https://github.com/bootstrap-vue/bootstrap-vue/issues/6967)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ public void updateProjectProperties(@NonNull final String projectUuid, @NonNull
rawProject.elementOpt("group", properties.getGroup());
// overwrite description only if it is set (means not null)
rawProject.elementOpt("description", properties.getDescription());
// set new parent project if it is set (means not null)
if (properties.getParentId() != null) {
JSONObject newParent = new JSONObject().elementOpt("uuid", properties.getParentId());
rawProject.element("parent", newParent);
}
// 3. update project
updateProject(projectUuid, rawProject);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
*/
@Extension
@Symbol("dependencyTrackPublisher") // This indicates to Jenkins that this is an implementation of an extension point.
public final class DescriptorImpl extends BuildStepDescriptor<Publisher> implements Serializable {
public class DescriptorImpl extends BuildStepDescriptor<Publisher> implements Serializable {

private static final long serialVersionUID = -2018722914973282748L;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Project parse(final JSONObject json) {
.active(activeStr != null ? Boolean.parseBoolean(activeStr) : null)
.swidTagId(getKeyOrNull(json, "swidTagId"))
.group(getKeyOrNull(json, "group"))
.parent(json.has("parent") ? ProjectParser.parse(json.getJSONObject("parent")) : null)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,27 @@
package org.jenkinsci.plugins.DependencyTrack;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Extension;
import hudson.RelativePath;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.util.ListBoxModel;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jenkins.model.Jenkins;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.verb.POST;

import static org.jenkinsci.plugins.DependencyTrack.PluginUtil.areAllElementsOfType;

Expand All @@ -46,22 +54,32 @@ public final class ProjectProperties extends AbstractDescribableImpl<ProjectProp
/**
* Tags to set for the project
*/
@Nullable
private List<String> tags;

/**
* SWID Tag ID for the project
*/
@Nullable
private String swidTagId;

/**
* Group to set for the project
*/
@Nullable
private String group;

/**
* Description to set for the project
*/
@Nullable
private String description;

/**
* UUID of the parent project
*/
@Nullable
private String parentId;

@NonNull
public List<String> getTags() {
Expand Down Expand Up @@ -111,6 +129,11 @@ public void setDescription(final String description) {
this.description = StringUtils.trimToNull(description);
}

@DataBoundSetter
public void setParentId(final String parentId) {
this.parentId = StringUtils.trimToNull(parentId);
}

@NonNull
public String getTagsAsText() {
return StringUtils.join(getTags(), System.lineSeparator());
Expand All @@ -129,5 +152,19 @@ private List<String> normalizeTags(final Collection<String> values) {

@Extension
public static class DescriptorImpl extends Descriptor<ProjectProperties> {

/**
* Retrieve the projects to populate the dropdown.
*
* @param dependencyTrackUrl the base URL to Dependency-Track
* @param dependencyTrackApiKey the API key to use for authentication
* @param item used to lookup credentials in job config
* @return ListBoxModel
*/
@POST
public ListBoxModel doFillParentIdItems(@RelativePath("..") @QueryParameter final String dependencyTrackUrl, @RelativePath("..") @QueryParameter final String dependencyTrackApiKey, @AncestorInPath @Nullable final Item item) {
org.jenkinsci.plugins.DependencyTrack.DescriptorImpl pluginDescriptor = Jenkins.get().getDescriptorByType(org.jenkinsci.plugins.DependencyTrack.DescriptorImpl.class);
return pluginDescriptor.doFillProjectIdItems(dependencyTrackUrl, dependencyTrackApiKey, item);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ public class Project implements Serializable {
private Boolean active;
private String swidTagId;
private String group;
private Project parent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ limitations under the License.
<f:entry field="description" title="${%description}">
<f:textbox id="description" />
</f:entry>
<f:entry field="parentId" title="${%parentId}">
<f:select id="parentId"/>
</f:entry>

</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ tags=Tags
swidTagId=SWID Tag ID
group=Namespace / Group / Vendor
description=Description
parentId=Parent project
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ tags=Tags
swidTagId=SWID Tag ID
group=Namensraum / Gruppe / Hersteller
description=Beschreibung
parentId=\u00dcbergeordnetes Projekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
The ID (UUID) of the parent project.
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
Die ID (UUID) des übergeordneten Projektes.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ public void updateProjectPropertiesTest() throws ApiClientException, Interrupted
.get(ApiClient.PROJECT_URL + "/uuid-3", (request, response) -> {
assertThat(request.requestHeaders().contains(ApiClient.API_KEY_HEADER, API_KEY, false)).isTrue();
assertThat(request.requestHeaders().contains(HttpHeaderNames.ACCEPT, HttpHeaderValues.APPLICATION_JSON, true)).isTrue();
return response.sendString(Mono.just("{\"name\":\"test-project\",\"uuid\":\"uuid-3\",\"version\":\"1.2.3\",\"tags\":[{\"name\":\"tag1\"},{\"name\":\"tag2\"}]}"));
return response.sendString(Mono.just("{\"name\":\"test-project\",\"uuid\":\"uuid-3\",\"version\":\"1.2.3\",\"tags\":[{\"name\":\"tag1\"},{\"name\":\"tag2\"}],\"parent\":{\"uuid\":\"old-parent\"}}"));
})
.post(ApiClient.PROJECT_URL, (request, response) -> {
assertThat(request.requestHeaders().contains(ApiClient.API_KEY_HEADER, API_KEY, false)).isTrue();
Expand All @@ -380,6 +380,7 @@ public void updateProjectPropertiesTest() throws ApiClientException, Interrupted
props.setSwidTagId("my swid tag id");
props.setGroup("my group");
props.setDescription("my description");
props.setParentId("parent-uuid");

assertThatCode(() -> uut.updateProjectProperties("uuid-3", props)).doesNotThrowAnyException();
completionSignal.await(5, TimeUnit.SECONDS);
Expand All @@ -388,6 +389,7 @@ public void updateProjectPropertiesTest() throws ApiClientException, Interrupted
assertThat(project.getSwidTagId()).isEqualTo(props.getSwidTagId());
assertThat(project.getGroup()).isEqualTo(props.getGroup());
assertThat(project.getDescription()).isEqualTo(props.getDescription());
assertThat(project.getParent()).hasFieldOrPropertyWithValue("uuid", props.getParentId());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@
*/
package org.jenkinsci.plugins.DependencyTrack;

import hudson.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jenkins.model.Jenkins;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
*
Expand Down Expand Up @@ -70,8 +77,32 @@ void verifyEmptyStringsShallBeNull() {
uut.setDescription("");
uut.setGroup("\t");
uut.setSwidTagId(System.lineSeparator());
uut.setParentId(" ");
assertThat(uut.getDescription()).isNull();
assertThat(uut.getGroup()).isNull();
assertThat(uut.getSwidTagId()).isNull();
assertThat(uut.getParentId()).isNull();
}

@Nested
class DescriptorImplTest {

@Test
void doFillParentIdItemsTest() throws Exception {
Field instanceField = ReflectionUtils.findField(Jenkins.class, "theInstance", Jenkins.class);
ReflectionUtils.makeAccessible(instanceField);
Jenkins origJenkins = (Jenkins) instanceField.get(null);
Jenkins mockJenkins = mock(Jenkins.class);
ReflectionUtils.setField(instanceField, null, mockJenkins);
org.jenkinsci.plugins.DependencyTrack.DescriptorImpl descriptorMock = mock(org.jenkinsci.plugins.DependencyTrack.DescriptorImpl.class);
when(mockJenkins.getDescriptorByType(org.jenkinsci.plugins.DependencyTrack.DescriptorImpl.class)).thenReturn(descriptorMock);
ProjectProperties.DescriptorImpl uut = new ProjectProperties.DescriptorImpl();

uut.doFillParentIdItems("url", "key", null);

ReflectionUtils.setField(instanceField, null, origJenkins);
verify(mockJenkins).getDescriptorByType(org.jenkinsci.plugins.DependencyTrack.DescriptorImpl.class);
verify(descriptorMock).doFillProjectIdItems("url", "key", null);
}
}
}

0 comments on commit 65059a1

Please sign in to comment.