Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port: Ensure no unique constraint violation for ProjectMetadata #926

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/org/dependencytrack/common/MdcKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public final class MdcKeys {
public static final String MDC_BOM_UPLOAD_TOKEN = "bomUploadToken";
public static final String MDC_BOM_VERSION = "bomVersion";
public static final String MDC_COMPONENT_UUID = "componentUuid";
public static final String MDC_EVENT_TOKEN = "eventToken";
public static final String MDC_EXTENSION = "extension";
public static final String MDC_EXTENSION_NAME = "extensionName";
public static final String MDC_EXTENSION_POINT = "extensionPoint";
Expand Down
250 changes: 130 additions & 120 deletions src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -512,147 +512,157 @@ public Project updateProject(Project transientProject, boolean commitIndex) {
}

@Override
public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeProperties,
boolean includeComponents, boolean includeServices, boolean includeAuditHistory,
boolean includeACL, boolean includePolicyViolations) {
final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name());
if (source == null) {
throw new IllegalStateException("Project with UUID %s was supposed to be cloned, but it does not exist anymore".formatted(from));
}
if (doesProjectExist(source.getName(), newVersion)) {
// Project cloning is an asynchronous process. When receiving the clone request, we already perform
// this check. It is possible though that a project with the new version is created synchronously
// between the clone event being dispatched, and it being processed.
throw new IllegalStateException("Project %s was supposed to be cloned to version %s, but that version already exists"
.formatted(source, newVersion));
}
Project project = new Project();
project.setAuthors(source.getAuthors());
project.setManufacturer(source.getManufacturer());
project.setSupplier(source.getSupplier());
project.setPublisher(source.getPublisher());
project.setGroup(source.getGroup());
project.setName(source.getName());
project.setDescription(source.getDescription());
project.setVersion(newVersion);
project.setClassifier(source.getClassifier());
project.setActive(source.isActive());
project.setCpe(source.getCpe());
project.setPurl(source.getPurl());
project.setSwidTagId(source.getSwidTagId());
if (includeComponents && includeServices) {
project.setDirectDependencies(source.getDirectDependencies());
}
project.setParent(source.getParent());
project = persist(project);

if (source.getMetadata() != null) {
final var metadata = new ProjectMetadata();
metadata.setProject(project);
metadata.setAuthors(source.getMetadata().getAuthors());
metadata.setSupplier(source.getMetadata().getSupplier());
persist(metadata);
}
public Project clone(
final UUID from,
final String newVersion,
final boolean includeTags,
final boolean includeProperties,
final boolean includeComponents,
final boolean includeServices,
final boolean includeAuditHistory,
final boolean includeACL,
final boolean includePolicyViolations
) {
return callInTransaction(() -> {
final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name());
if (source == null) {
throw new IllegalStateException("Project was supposed to be cloned, but it does not exist anymore");
}
if (doesProjectExist(source.getName(), newVersion)) {
// Project cloning is an asynchronous process. When receiving the clone request, we already perform
// this check. It is possible though that a project with the new version is created synchronously
// between the clone event being dispatched, and it being processed.
throw new IllegalStateException("""
Project was supposed to be cloned to version %s, \
but that version already exists""".formatted(newVersion));
}
Project project = new Project();
project.setAuthors(source.getAuthors());
project.setManufacturer(source.getManufacturer());
project.setSupplier(source.getSupplier());
project.setPublisher(source.getPublisher());
project.setGroup(source.getGroup());
project.setName(source.getName());
project.setDescription(source.getDescription());
project.setVersion(newVersion);
project.setClassifier(source.getClassifier());
project.setActive(source.isActive());
project.setCpe(source.getCpe());
project.setPurl(source.getPurl());
project.setSwidTagId(source.getSwidTagId());
if (includeComponents && includeServices) {
project.setDirectDependencies(source.getDirectDependencies());
}
project.setParent(source.getParent());
project = persist(project);

if (source.getMetadata() != null) {
final var metadata = new ProjectMetadata();
metadata.setProject(project);
metadata.setAuthors(source.getMetadata().getAuthors());
metadata.setSupplier(source.getMetadata().getSupplier());
persist(metadata);
}

if (includeTags) {
for (final Tag tag : source.getTags()) {
tag.getProjects().add(project);
persist(tag);
if (includeTags) {
for (final Tag tag : source.getTags()) {
tag.getProjects().add(project);
persist(tag);
}
}
}

if (includeProperties && source.getProperties() != null) {
for (final ProjectProperty sourceProperty : source.getProperties()) {
final ProjectProperty property = new ProjectProperty();
property.setProject(project);
property.setPropertyType(sourceProperty.getPropertyType());
property.setGroupName(sourceProperty.getGroupName());
property.setPropertyName(sourceProperty.getPropertyName());
property.setPropertyValue(sourceProperty.getPropertyValue());
property.setDescription(sourceProperty.getDescription());
persist(property);
if (includeProperties && source.getProperties() != null) {
for (final ProjectProperty sourceProperty : source.getProperties()) {
final ProjectProperty property = new ProjectProperty();
property.setProject(project);
property.setPropertyType(sourceProperty.getPropertyType());
property.setGroupName(sourceProperty.getGroupName());
property.setPropertyName(sourceProperty.getPropertyName());
property.setPropertyValue(sourceProperty.getPropertyValue());
property.setDescription(sourceProperty.getDescription());
persist(property);
}
}
}

final Map<Long, Component> clonedComponents = new HashMap<>();
if (includeComponents) {
final List<Component> sourceComponents = getAllComponents(source);
if (sourceComponents != null) {
for (final Component sourceComponent : sourceComponents) {
final Component clonedComponent = cloneComponent(sourceComponent, project, false);
// Add vulnerabilties and finding attribution from the source component to the cloned component
for (Vulnerability vuln : sourceComponent.getVulnerabilities()) {
final FindingAttribution sourceAttribution = this.getFindingAttribution(vuln, sourceComponent);
this.addVulnerability(vuln, clonedComponent, sourceAttribution.getAnalyzerIdentity(), sourceAttribution.getAlternateIdentifier(),
sourceAttribution.getReferenceUrl(), sourceAttribution.getAttributedOn());
final Map<Long, Component> clonedComponents = new HashMap<>();
if (includeComponents) {
final List<Component> sourceComponents = getAllComponents(source);
if (sourceComponents != null) {
for (final Component sourceComponent : sourceComponents) {
final Component clonedComponent = cloneComponent(sourceComponent, project, false);
// Add vulnerabilties and finding attribution from the source component to the cloned component
for (Vulnerability vuln : sourceComponent.getVulnerabilities()) {
final FindingAttribution sourceAttribution = this.getFindingAttribution(vuln, sourceComponent);
this.addVulnerability(vuln, clonedComponent, sourceAttribution.getAnalyzerIdentity(), sourceAttribution.getAlternateIdentifier(),
sourceAttribution.getReferenceUrl(), sourceAttribution.getAttributedOn());
}
clonedComponents.put(sourceComponent.getId(), clonedComponent);
}
clonedComponents.put(sourceComponent.getId(), clonedComponent);
}
}
}

if (includeServices) {
final List<ServiceComponent> sourceServices = getAllServiceComponents(source);
if (sourceServices != null) {
for (final ServiceComponent sourceService : sourceServices) {
cloneServiceComponent(sourceService, project, false);
if (includeServices) {
final List<ServiceComponent> sourceServices = getAllServiceComponents(source);
if (sourceServices != null) {
for (final ServiceComponent sourceService : sourceServices) {
cloneServiceComponent(sourceService, project, false);
}
}
}
}

if (includeAuditHistory && includeComponents) {
final List<Analysis> analyses = super.getAnalyses(source);
if (analyses != null) {
for (final Analysis sourceAnalysis : analyses) {
Analysis analysis = new Analysis();
analysis.setAnalysisState(sourceAnalysis.getAnalysisState());
final Component clonedComponent = clonedComponents.get(sourceAnalysis.getComponent().getId());
if (clonedComponent == null) {
break;
}
analysis.setComponent(clonedComponent);
analysis.setVulnerability(sourceAnalysis.getVulnerability());
analysis.setSuppressed(sourceAnalysis.isSuppressed());
analysis.setAnalysisResponse(sourceAnalysis.getAnalysisResponse());
analysis.setAnalysisJustification(sourceAnalysis.getAnalysisJustification());
analysis.setAnalysisState(sourceAnalysis.getAnalysisState());
analysis.setAnalysisDetails(sourceAnalysis.getAnalysisDetails());
analysis.setVulnerabilityPolicyId(sourceAnalysis.getVulnerabilityPolicyId());
analysis = persist(analysis);
if (sourceAnalysis.getAnalysisComments() != null) {
for (final AnalysisComment sourceComment : sourceAnalysis.getAnalysisComments()) {
final AnalysisComment analysisComment = new AnalysisComment();
analysisComment.setAnalysis(analysis);
analysisComment.setTimestamp(sourceComment.getTimestamp());
analysisComment.setComment(sourceComment.getComment());
analysisComment.setCommenter(sourceComment.getCommenter());
persist(analysisComment);
if (includeAuditHistory && includeComponents) {
final List<Analysis> analyses = super.getAnalyses(source);
if (analyses != null) {
for (final Analysis sourceAnalysis : analyses) {
Analysis analysis = new Analysis();
analysis.setAnalysisState(sourceAnalysis.getAnalysisState());
final Component clonedComponent = clonedComponents.get(sourceAnalysis.getComponent().getId());
if (clonedComponent == null) {
break;
}
analysis.setComponent(clonedComponent);
analysis.setVulnerability(sourceAnalysis.getVulnerability());
analysis.setSuppressed(sourceAnalysis.isSuppressed());
analysis.setAnalysisResponse(sourceAnalysis.getAnalysisResponse());
analysis.setAnalysisJustification(sourceAnalysis.getAnalysisJustification());
analysis.setAnalysisState(sourceAnalysis.getAnalysisState());
analysis.setAnalysisDetails(sourceAnalysis.getAnalysisDetails());
analysis.setVulnerabilityPolicyId(sourceAnalysis.getVulnerabilityPolicyId());
analysis = persist(analysis);
if (sourceAnalysis.getAnalysisComments() != null) {
for (final AnalysisComment sourceComment : sourceAnalysis.getAnalysisComments()) {
final AnalysisComment analysisComment = new AnalysisComment();
analysisComment.setAnalysis(analysis);
analysisComment.setTimestamp(sourceComment.getTimestamp());
analysisComment.setComment(sourceComment.getComment());
analysisComment.setCommenter(sourceComment.getCommenter());
persist(analysisComment);
}
}
}
}
}
}

if (includeACL) {
List<Team> accessTeams = source.getAccessTeams();
if (!CollectionUtils.isEmpty(accessTeams)) {
project.setAccessTeams(new ArrayList<>(accessTeams));
if (includeACL) {
List<Team> accessTeams = source.getAccessTeams();
if (!CollectionUtils.isEmpty(accessTeams)) {
project.setAccessTeams(new ArrayList<>(accessTeams));
}
}
}

if(includeComponents && includePolicyViolations){
final List<PolicyViolation> sourcePolicyViolations = getAllPolicyViolations(source);
if(sourcePolicyViolations != null){
for(final PolicyViolation policyViolation: sourcePolicyViolations){
final Component destinationComponent = clonedComponents.get(policyViolation.getComponent().getId());
final PolicyViolation clonedPolicyViolation = clonePolicyViolation(policyViolation, destinationComponent);
persist(clonedPolicyViolation);
}
if (includeComponents && includePolicyViolations) {
final List<PolicyViolation> sourcePolicyViolations = getAllPolicyViolations(source);
if (sourcePolicyViolations != null) {
for (final PolicyViolation policyViolation : sourcePolicyViolations) {
final Component destinationComponent = clonedComponents.get(policyViolation.getComponent().getId());
final PolicyViolation clonedPolicyViolation = clonePolicyViolation(policyViolation, destinationComponent);
persist(clonedPolicyViolation);
}
}
}
}

project = getObjectById(Project.class, project.getId());
return project;
return project;
});
}

/**
Expand Down
Loading
Loading