Skip to content

Commit

Permalink
[TACKLE-192] - Bulk copy Assessment/Review (#142)
Browse files Browse the repository at this point in the history
* Add first template

* Add native tests

* Add utils

* Add tests

* Add audit data
  • Loading branch information
carlosthe19916 authored Oct 29, 2021
1 parent 2af484e commit e03f379
Show file tree
Hide file tree
Showing 12 changed files with 687 additions and 4 deletions.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
<!-- todo remove Hibernate Validator? -->
<dependency>
<groupId>io.quarkus</groupId>
Expand Down Expand Up @@ -203,6 +207,12 @@
<version>2.30.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.tackle.applicationinventory.dto;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

public class BulkReviewDto {

private Long id;

@NotNull
private Long sourceReview;

@NotEmpty
private List<Long> targetApplications;

private boolean completed;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Long getSourceReview() {
return sourceReview;
}

public void setSourceReview(Long sourceReview) {
this.sourceReview = sourceReview;
}

public List<Long> getTargetApplications() {
return targetApplications;
}

public void setTargetApplications(List<Long> targetApplications) {
this.targetApplications = targetApplications;
}

public boolean isCompleted() {
return completed;
}

public void setCompleted(boolean completed) {
this.completed = completed;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.tackle.applicationinventory.dto.utils;

import io.tackle.applicationinventory.dto.BulkReviewDto;
import io.tackle.applicationinventory.entities.BulkCopyReview;

import java.util.stream.Collectors;

public class EntityToDTO {

public static BulkReviewDto toDTO(BulkCopyReview entity) {
BulkReviewDto dto = new BulkReviewDto();

dto.setId(entity.id);
dto.setCompleted(entity.completed);
dto.setSourceReview(entity.sourceReview.id);
dto.setTargetApplications(entity.targetApplications.stream()
.map(f -> f.application.id)
.collect(Collectors.toList())
);

return dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.tackle.applicationinventory.entities;

import io.tackle.commons.entities.AbstractEntity;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "bulk_copy_review")
@SQLDelete(sql = "UPDATE bulk_copy_review SET deleted = true WHERE id = ?", check = ResultCheckStyle.COUNT)
@Where(clause = "deleted = false")
public class BulkCopyReview extends AbstractEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey, name = "review_id")
public Review sourceReview;

@OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true, mappedBy = "bulkCopyReview")
public Set<BulkCopyReviewDetails> targetApplications = new HashSet<>();

public boolean completed = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.tackle.applicationinventory.entities;

import io.tackle.commons.entities.AbstractEntity;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import javax.persistence.*;

@Entity
@Table(name = "bulk_copy_review_details")
@SQLDelete(sql = "UPDATE bulk_copy_review_details SET deleted = true WHERE id = ?", check = ResultCheckStyle.COUNT)
@Where(clause = "deleted = false")
public class BulkCopyReviewDetails extends AbstractEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey, name = "bulk_copy_review_id")
public BulkCopyReview bulkCopyReview;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(foreignKey = @ForeignKey, name = "application_id")
public Application application;

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ public class Review extends AbstractEntity {
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JsonIgnoreProperties(value = {"review"}, allowSetters = true)
public Application application;

// No foreign keys associated to this field since foreign keys might
// complicate DELETE operations.
public Long copiedFromReviewId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.tackle.applicationinventory.resources;

import io.tackle.applicationinventory.dto.BulkReviewDto;
import io.tackle.applicationinventory.dto.utils.EntityToDTO;
import io.tackle.applicationinventory.entities.Application;
import io.tackle.applicationinventory.entities.BulkCopyReview;
import io.tackle.applicationinventory.entities.BulkCopyReviewDetails;
import io.tackle.applicationinventory.entities.Review;
import io.tackle.applicationinventory.services.BulkCopyReviewService;
import io.vertx.core.eventbus.EventBus;

import javax.inject.Inject;
import javax.transaction.NotSupportedException;
import javax.transaction.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Path("/review/bulk")
@Produces("application/json")
@Consumes("application/json")
public class ReviewBulkCopyResource {

@Inject
UserTransaction transaction;

@Inject
EventBus eventBus;

private void handleSimpleRollback(UserTransaction transaction) {
try {
transaction.rollback();
} catch (SystemException e) {
throw new IllegalStateException(e);
}
}

@POST
public BulkReviewDto createBulkCopyReview(
@NotNull @Valid BulkReviewDto bulkReviewInput
) throws SystemException, NotSupportedException, HeuristicRollbackException, HeuristicMixedException, RollbackException {
transaction.begin();

// Find source review
Long sourceReviewId = bulkReviewInput.getSourceReview();
Review sourceReview = Review.<Review>findByIdOptional(sourceReviewId)
.orElseThrow(() -> {
handleSimpleRollback(transaction);
return new BadRequestException("Source review not valid");
});

// Find target applications
List<Application> applicationsTarget = bulkReviewInput.getTargetApplications().stream()
.map(appId -> Application.<Application>findById(appId))
.collect(Collectors.toList());
if (applicationsTarget.stream().anyMatch(Objects::isNull)) {
handleSimpleRollback(transaction);
throw new BadRequestException("One or more target applications is not valid");
}

// Prepare JPA object to be persisted
BulkCopyReview bulkCopyReviewOutput = new BulkCopyReview();

bulkCopyReviewOutput.sourceReview = sourceReview;
bulkCopyReviewOutput.targetApplications = applicationsTarget.stream()
.map(application -> {
BulkCopyReviewDetails detail = new BulkCopyReviewDetails();
detail.bulkCopyReview = bulkCopyReviewOutput;
detail.application = application;

return detail;
})
.collect(Collectors.toSet());
bulkCopyReviewOutput.completed = false;

bulkCopyReviewOutput.persist();
transaction.commit();

// Fire bus event
eventBus.send(BulkCopyReviewService.BUS_EVENT, bulkCopyReviewOutput.id);

// Generate response
return EntityToDTO.toDTO(bulkCopyReviewOutput);
}

@GET
@Path("/{id}")
public BulkReviewDto getBulkCopyReview(@PathParam("id") Long id) {
BulkCopyReview entity = BulkCopyReview
.<BulkCopyReview>findByIdOptional(id)
.orElseThrow(NotFoundException::new);

return EntityToDTO.toDTO(entity);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.tackle.applicationinventory.services;

import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
import io.tackle.applicationinventory.entities.Application;
import io.tackle.applicationinventory.entities.BulkCopyReview;
import io.tackle.applicationinventory.entities.Review;

import javax.enterprise.context.ApplicationScoped;
import javax.transaction.Transactional;

@ApplicationScoped
public class BulkCopyReviewService {

public static final String BUS_EVENT = "process-bulk-review-copy";

@Transactional
@ConsumeEvent(BUS_EVENT)
@Blocking
public void processBulkCopyReview(Long bulkId) {
BulkCopyReview bulk = BulkCopyReview.findById(bulkId);

// Copy review to all apps
bulk.targetApplications.forEach(detail -> {
Application application = Application.findById(detail.application.id);

Review oldReview = Review.find("application.id", detail.application.id).firstResult();
if (oldReview != null) {
oldReview = copyReviewFromSourceToTarget(bulk.sourceReview, oldReview);
oldReview.persist();
} else {
Review newReview = copyReviewFromSourceToTarget(bulk.sourceReview, new Review());
newReview.application = application;
newReview.persist();
}
});

bulk.completed = true;
bulk.persist();
}

private Review copyReviewFromSourceToTarget(Review source, Review target) {
target.proposedAction = source.proposedAction;
target.effortEstimate = source.effortEstimate;
target.businessCriticality = source.businessCriticality;
target.workPriority = source.workPriority;
target.comments = source.comments;

target.copiedFromReviewId = source.id;
return target;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
create table bulk_copy_review
(
id int8 not null,

createTime timestamp,
createUser varchar(255),
deleted boolean,
updateTime timestamp,
updateUser varchar(255),

completed boolean,
review_id int8 not null,
primary key (id)
);

create table bulk_copy_review_details
(
id int8 not null,

createTime timestamp,
createUser varchar(255),
deleted boolean,
updateTime timestamp,
updateUser varchar(255),

bulk_copy_review_id int8 not null,
application_id int8 not null,
primary key (id)
);

alter table if exists bulk_copy_review_details
add constraint fk_bulk_copy_review_details_application
foreign key (application_id)
references application;

-- Add audit data

alter table if exists review
add column copiedFromReviewId int8;
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
@QuarkusTest
@TestProfile(FlywayMigrationProfile.class)
public class FlywayMigrationTest {

@Inject
Flyway flyway;

@Test
public void testMigration() {
// check the number of migrations applied equals the number of files in resources/db/migration folder
assertEquals(18, flyway.info().applied().length);
assertEquals(19, flyway.info().applied().length);
// check the current migration version is the one from the last file in resources/db/migration folder
assertEquals("20210914.1", flyway.info().current().getVersion().toString());
assertEquals("20211008.1", flyway.info().current().getVersion().toString());
// just a basic test to double check the application started
// to prove the flyway scripts ran successfully during startup
given()
Expand Down
Loading

0 comments on commit e03f379

Please sign in to comment.