merge tutor updates #82
|
@ -37,6 +37,16 @@
|
||||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.codecentric</groupId>
|
||||||
|
<artifactId>chaos-monkey-spring-boot</artifactId>
|
||||||
|
<version>2.5.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
@ -47,6 +57,17 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>2.21.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.tngtech.archunit</groupId>
|
||||||
|
<artifactId>archunit-junit4</artifactId>
|
||||||
|
<version>0.22.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -74,6 +95,10 @@
|
||||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||||
<version>1.2.0</version>
|
<version>1.2.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-test</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -11,11 +11,14 @@ import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
|
||||||
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Component
|
@Component
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@Service("AddNewTaskToTaskList")
|
||||||
public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase {
|
public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase {
|
||||||
|
|
||||||
private final NewTaskAddedEventPort newTaskAddedEventPort;
|
private final NewTaskAddedEventPort newTaskAddedEventPort;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ch.unisg.tapastasks.tasks.domain.Task;
|
||||||
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -14,6 +15,7 @@ import java.util.Optional;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Component
|
@Component
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@Service("RetrieveTaskFromTaskList")
|
||||||
public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase {
|
public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase {
|
||||||
|
|
||||||
private final LoadTaskPort loadTaskFromRepositoryPort;
|
private final LoadTaskPort loadTaskFromRepositoryPort;
|
||||||
|
|
|
@ -1,7 +1,38 @@
|
||||||
server.port=8081
|
server.port=8081
|
||||||
//spring.data.mongodb.uri=mongodb://127.0.0.1:27017
|
spring.data.mongodb.uri=mongodb://127.0.0.1:27017
|
||||||
spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
|
#spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
|
||||||
spring.data.mongodb.database=tapas-tasks
|
spring.data.mongodb.database=tapas-tasks
|
||||||
baseuri=https://tapas-tasks.86-119-34-23.nip.io/
|
baseuri=https://tapas-tasks.86-119-34-23.nip.io/
|
||||||
|
|
||||||
|
spring.profiles.active=chaos-monkey
|
||||||
|
chaos.monkey.enabled=true
|
||||||
|
management.endpoint.chaosmonkey.enabled=true
|
||||||
|
management.endpoint.chaosmonkeyjmx.enabled=true
|
||||||
|
# include specific endpoints
|
||||||
|
management.endpoints.web.exposure.include=health,info,chaosmonkey
|
||||||
|
chaos.monkey.watcher.controller=true
|
||||||
|
chaos.monkey.watcher.restController=true
|
||||||
|
chaos.monkey.watcher.service=true
|
||||||
|
chaos.monkey.watcher.repository=true
|
||||||
|
chaos.monkey.watcher.component=true
|
||||||
|
|
||||||
|
#Chaos Monkey configs taken from here: https://www.baeldung.com/spring-boot-chaos-monkey
|
||||||
|
|
||||||
|
#Latency Assault
|
||||||
|
#chaos.monkey.assaults.latencyActive=true
|
||||||
|
#chaos.monkey.assaults.latencyRangeStart=3000
|
||||||
|
#chaos.monkey.assaults.latencyRangeEnd=15000
|
||||||
|
|
||||||
|
#Exception Assault
|
||||||
|
#chaos.monkey.assaults.latencyActive=false
|
||||||
|
#chaos.monkey.assaults.exceptionsActive=true
|
||||||
|
#chaos.monkey.assaults.killApplicationActive=false
|
||||||
|
|
||||||
|
#AppKiller Assault
|
||||||
|
#chaos.monkey.assaults.latencyActive=false
|
||||||
|
#chaos.monkey.assaults.exceptionsActive=false
|
||||||
|
#chaos.monkey.assaults.killApplicationActive=true
|
||||||
|
|
||||||
|
#Chaos Monkey assaults via REST to endpoint /actuator/chaosmonkey/assaults/
|
||||||
|
#https://softwarehut.com/blog/tech/chaos-monkey
|
||||||
|
#https://codecentric.github.io/chaos-monkey-spring-boot/latest/
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package ch.unisg.tapastasks;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.Task;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.BDDAssertions.*;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
public class AddNewTaskToTaskListSystemTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AddTaskPort addTaskPort;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addNewTaskToTaskList() throws JSONException {
|
||||||
|
|
||||||
|
Task.TaskName taskName = new Task.TaskName("system-integration-test-task");
|
||||||
|
Task.TaskType taskType = new Task.TaskType("system-integration-test-type");
|
||||||
|
Task.OriginalTaskUri originalTaskUri = new Task.OriginalTaskUri("example.org");
|
||||||
|
|
||||||
|
ResponseEntity response = whenAddNewTaskToEmptyList(taskName, taskType, originalTaskUri);
|
||||||
|
|
||||||
|
JSONObject responseJson = new JSONObject(response.getBody().toString());
|
||||||
|
String respTaskId = responseJson.getString("taskId");
|
||||||
|
String respTaskName = responseJson.getString("taskName");
|
||||||
|
String respTaskType = responseJson.getString("taskType");
|
||||||
|
|
||||||
|
then(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||||
|
then(respTaskId).isNotEmpty();
|
||||||
|
then(respTaskName).isEqualTo(taskName.getValue());
|
||||||
|
then(respTaskType).isEqualTo(taskType.getValue());
|
||||||
|
then(TaskList.getTapasTaskList().getListOfTasks().getValue()).hasSize(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity whenAddNewTaskToEmptyList(
|
||||||
|
Task.TaskName taskName,
|
||||||
|
Task.TaskType taskType,
|
||||||
|
Task.OriginalTaskUri originalTaskUri) throws JSONException {
|
||||||
|
|
||||||
|
TaskList.getTapasTaskList().getListOfTasks().getValue().clear();
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Content-Type", TaskJsonRepresentation.MEDIA_TYPE);
|
||||||
|
|
||||||
|
String jsonPayLoad = new JSONObject()
|
||||||
|
.put("taskName", taskName.getValue() )
|
||||||
|
.put("taskType", taskType.getValue())
|
||||||
|
.put("originalTaskUri",originalTaskUri.getValue())
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
HttpEntity<String> request = new HttpEntity<>(jsonPayLoad,headers);
|
||||||
|
|
||||||
|
return restTemplate.exchange(
|
||||||
|
"/tasks/",
|
||||||
|
HttpMethod.POST,
|
||||||
|
request,
|
||||||
|
Object.class
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ch.unisg.tapastasks;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.archunit.HexagonalArchitecture;
|
||||||
|
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||||
|
|
||||||
|
class DependencyRuleTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPackageDependencies() {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("ch.unisg.tapastasks.tasks.domain..")
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage("ch.unisg.tapastasks.tasks.application..")
|
||||||
|
.check(new ClassFileImporter()
|
||||||
|
.importPackages("ch.unisg.tapastasks.tasks.."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,13 +1,18 @@
|
||||||
package ch.unisg.tapastasks;
|
package ch.unisg.tapastasks;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.service.AddNewTaskToTaskListService;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class TapasTasksApplicationTests {
|
class TapasTasksApplicationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void contextLoads() {
|
void contextLoads() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Adapters extends ArchitectureElement {
|
||||||
|
|
||||||
|
private final HexagonalArchitecture parentContext;
|
||||||
|
private List<String> incomingAdapterPackages = new ArrayList<>();
|
||||||
|
private List<String> outgoingAdapterPackages = new ArrayList<>();
|
||||||
|
|
||||||
|
Adapters(HexagonalArchitecture parentContext, String basePackage) {
|
||||||
|
super(basePackage);
|
||||||
|
this.parentContext = parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters outgoing(String packageName) {
|
||||||
|
this.incomingAdapterPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters incoming(String packageName) {
|
||||||
|
this.outgoingAdapterPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> allAdapterPackages() {
|
||||||
|
List<String> allAdapters = new ArrayList<>();
|
||||||
|
allAdapters.addAll(incomingAdapterPackages);
|
||||||
|
allAdapters.addAll(outgoingAdapterPackages);
|
||||||
|
return allAdapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture and() {
|
||||||
|
return parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getBasePackage() {
|
||||||
|
return basePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dontDependOnEachOther(JavaClasses classes) {
|
||||||
|
List<String> allAdapters = allAdapterPackages();
|
||||||
|
for (String adapter1 : allAdapters) {
|
||||||
|
for (String adapter2 : allAdapters) {
|
||||||
|
if (!adapter1.equals(adapter2)) {
|
||||||
|
denyDependency(adapter1, adapter2, classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotDependOn(String packageName, JavaClasses classes) {
|
||||||
|
denyDependency(this.basePackage, packageName, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotContainEmptyPackages() {
|
||||||
|
denyEmptyPackages(allAdapterPackages());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ApplicationLayer extends ArchitectureElement {
|
||||||
|
|
||||||
|
private final HexagonalArchitecture parentContext;
|
||||||
|
private List<String> incomingPortsPackages = new ArrayList<>();
|
||||||
|
private List<String> outgoingPortsPackages = new ArrayList<>();
|
||||||
|
private List<String> servicePackages = new ArrayList<>();
|
||||||
|
|
||||||
|
public ApplicationLayer(String basePackage, HexagonalArchitecture parentContext) {
|
||||||
|
super(basePackage);
|
||||||
|
this.parentContext = parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer incomingPorts(String packageName) {
|
||||||
|
this.incomingPortsPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer outgoingPorts(String packageName) {
|
||||||
|
this.outgoingPortsPackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer services(String packageName) {
|
||||||
|
this.servicePackages.add(fullQualifiedPackage(packageName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture and() {
|
||||||
|
return parentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doesNotDependOn(String packageName, JavaClasses classes) {
|
||||||
|
denyDependency(this.basePackage, packageName, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incomingAndOutgoingPortsDoNotDependOnEachOther(JavaClasses classes) {
|
||||||
|
denyAnyDependency(this.incomingPortsPackages, this.outgoingPortsPackages, classes);
|
||||||
|
denyAnyDependency(this.outgoingPortsPackages, this.incomingPortsPackages, classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> allPackages() {
|
||||||
|
List<String> allPackages = new ArrayList<>();
|
||||||
|
allPackages.addAll(incomingPortsPackages);
|
||||||
|
allPackages.addAll(outgoingPortsPackages);
|
||||||
|
allPackages.addAll(servicePackages);
|
||||||
|
return allPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doesNotContainEmptyPackages() {
|
||||||
|
denyEmptyPackages(allPackages());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.tngtech.archunit.base.DescribedPredicate.*;
|
||||||
|
import static com.tngtech.archunit.base.DescribedPredicate.greaterThanOrEqualTo;
|
||||||
|
import static com.tngtech.archunit.lang.conditions.ArchConditions.*;
|
||||||
|
import static com.tngtech.archunit.lang.conditions.ArchConditions.containNumberOfElements;
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||||
|
|
||||||
|
abstract class ArchitectureElement {
|
||||||
|
|
||||||
|
final String basePackage;
|
||||||
|
|
||||||
|
public ArchitectureElement(String basePackage) {
|
||||||
|
this.basePackage = basePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fullQualifiedPackage(String relativePackage) {
|
||||||
|
return this.basePackage + "." + relativePackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denyDependency(String fromPackageName, String toPackageName, JavaClasses classes) {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("ch.unisg.tapastasks.tasks.domain..")
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage("ch.unisg.tapastasks.tasks.application..")
|
||||||
|
.check(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denyAnyDependency(
|
||||||
|
List<String> fromPackages, List<String> toPackages, JavaClasses classes) {
|
||||||
|
for (String fromPackage : fromPackages) {
|
||||||
|
for (String toPackage : toPackages) {
|
||||||
|
noClasses()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage(matchAllClassesInPackage(fromPackage))
|
||||||
|
.should()
|
||||||
|
.dependOnClassesThat()
|
||||||
|
.resideInAnyPackage(matchAllClassesInPackage(toPackage))
|
||||||
|
.check(classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchAllClassesInPackage(String packageName) {
|
||||||
|
return packageName + "..";
|
||||||
|
}
|
||||||
|
|
||||||
|
void denyEmptyPackage(String packageName) {
|
||||||
|
classes()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage(matchAllClassesInPackage(packageName))
|
||||||
|
.should(containNumberOfElements(greaterThanOrEqualTo(1)))
|
||||||
|
.check(classesInPackage(packageName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JavaClasses classesInPackage(String packageName) {
|
||||||
|
return new ClassFileImporter().importPackages(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void denyEmptyPackages(List<String> packages) {
|
||||||
|
for (String packageName : packages) {
|
||||||
|
denyEmptyPackage(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package ch.unisg.tapastasks.archunit;
|
||||||
|
|
||||||
|
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HexagonalArchitecture extends ArchitectureElement {
|
||||||
|
|
||||||
|
private Adapters adapters;
|
||||||
|
private ApplicationLayer applicationLayer;
|
||||||
|
private String configurationPackage;
|
||||||
|
private List<String> domainPackages = new ArrayList<>();
|
||||||
|
|
||||||
|
public static HexagonalArchitecture boundedContext(String basePackage) {
|
||||||
|
return new HexagonalArchitecture(basePackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture(String basePackage) {
|
||||||
|
super(basePackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Adapters withAdaptersLayer(String adaptersPackage) {
|
||||||
|
this.adapters = new Adapters(this, fullQualifiedPackage(adaptersPackage));
|
||||||
|
return this.adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture withDomainLayer(String domainPackage) {
|
||||||
|
this.domainPackages.add(fullQualifiedPackage(domainPackage));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationLayer withApplicationLayer(String applicationPackage) {
|
||||||
|
this.applicationLayer = new ApplicationLayer(fullQualifiedPackage(applicationPackage), this);
|
||||||
|
return this.applicationLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HexagonalArchitecture withConfiguration(String packageName) {
|
||||||
|
this.configurationPackage = fullQualifiedPackage(packageName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void domainDoesNotDependOnOtherPackages(JavaClasses classes) {
|
||||||
|
denyAnyDependency(
|
||||||
|
this.domainPackages, Collections.singletonList(adapters.basePackage), classes);
|
||||||
|
denyAnyDependency(
|
||||||
|
this.domainPackages, Collections.singletonList(applicationLayer.basePackage), classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void check(JavaClasses classes) {
|
||||||
|
this.adapters.doesNotContainEmptyPackages();
|
||||||
|
this.adapters.dontDependOnEachOther(classes);
|
||||||
|
this.adapters.doesNotDependOn(this.configurationPackage, classes);
|
||||||
|
this.applicationLayer.doesNotContainEmptyPackages();
|
||||||
|
this.applicationLayer.doesNotDependOn(this.adapters.getBasePackage(), classes);
|
||||||
|
this.applicationLayer.doesNotDependOn(this.configurationPackage, classes);
|
||||||
|
this.applicationLayer.incomingAndOutgoingPortsDoNotDependOnEachOther(classes);
|
||||||
|
this.domainDoesNotDependOnOtherPackages(classes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ch.unisg.tapastasks.tasks.adapter.in.web;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
|
||||||
|
import ch.unisg.tapastasks.tasks.adapter.in.web.AddNewTaskToTaskListWebController;
|
||||||
|
import ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb.TaskRepository;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.Task;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.eq;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
|
||||||
|
@WebMvcTest(controllers = AddNewTaskToTaskListWebController.class)
|
||||||
|
public class AddNewTaskToTaskListWebControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private AddNewTaskToTaskListUseCase addNewTaskToTaskListUseCase;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
TaskRepository taskRepository;
|
||||||
|
|
||||||
|
@Disabled
|
||||||
|
@Test
|
||||||
|
void testAddNewTaskToTaskList() throws Exception {
|
||||||
|
|
||||||
|
String taskName = "test-request";
|
||||||
|
String taskType = "test-request-type";
|
||||||
|
String originalTaskUri = "example.org";
|
||||||
|
|
||||||
|
String jsonPayLoad = new JSONObject()
|
||||||
|
.put("taskName", taskName )
|
||||||
|
.put("taskType", taskType)
|
||||||
|
.put("originalTaskUri",originalTaskUri)
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
//This raises a NullPointerException since it tries to build the HTTP response with attributes from
|
||||||
|
//the domain object (created task), which is mocked --> we need System Tests here!
|
||||||
|
//See the buckpal example from the lecture for a working integration test for testing the web controller
|
||||||
|
mockMvc.perform(post("/tasks/")
|
||||||
|
.contentType(TaskJsonRepresentation.MEDIA_TYPE)
|
||||||
|
.content(jsonPayLoad))
|
||||||
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
|
then(addNewTaskToTaskListUseCase).should()
|
||||||
|
.addNewTaskToTaskList(eq(new AddNewTaskToTaskListCommand(
|
||||||
|
new Task.TaskName(taskName), new Task.TaskType(taskType),
|
||||||
|
Optional.of(new Task.OriginalTaskUri(originalTaskUri))
|
||||||
|
)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.Task;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||||
|
@AutoConfigureDataMongo
|
||||||
|
@Import({TaskPersistenceAdapter.class, TaskMapper.class})
|
||||||
|
public class TaskPersistenceAdapterTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskRepository taskRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskPersistenceAdapter adapterUnderTest;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addsNewTask() {
|
||||||
|
|
||||||
|
String testTaskId = UUID.randomUUID().toString();
|
||||||
|
String testTaskName = "adds-persistence-task-name";
|
||||||
|
String testTaskType = "adds-persistence-task-type";
|
||||||
|
String testTaskOuri = "adds-persistence-test-task-ouri";
|
||||||
|
String testTaskStatus = Task.Status.OPEN.toString();
|
||||||
|
String testTaskListName = "tapas-tasks-tutors";
|
||||||
|
|
||||||
|
|
||||||
|
Task testTask = new Task(
|
||||||
|
new Task.TaskId(testTaskId),
|
||||||
|
new Task.TaskName(testTaskName),
|
||||||
|
new Task.TaskType(testTaskType),
|
||||||
|
new Task.OriginalTaskUri(testTaskOuri),
|
||||||
|
new Task.TaskStatus(Task.Status.valueOf(testTaskStatus))
|
||||||
|
);
|
||||||
|
adapterUnderTest.addTask(testTask);
|
||||||
|
|
||||||
|
MongoTaskDocument retrievedDoc = taskRepository.findByTaskId(testTaskId,testTaskListName);
|
||||||
|
|
||||||
|
assertThat(retrievedDoc.taskId).isEqualTo(testTaskId);
|
||||||
|
assertThat(retrievedDoc.taskName).isEqualTo(testTaskName);
|
||||||
|
assertThat(retrievedDoc.taskListName).isEqualTo(testTaskListName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void retrievesTask() {
|
||||||
|
|
||||||
|
String testTaskId = UUID.randomUUID().toString();
|
||||||
|
String testTaskName = "reads-persistence-task-name";
|
||||||
|
String testTaskType = "reads-persistence-task-type";
|
||||||
|
String testTaskOuri = "reads-persistence-test-task-ouri";
|
||||||
|
String testTaskStatus = Task.Status.OPEN.toString();
|
||||||
|
String testTaskListName = "tapas-tasks-tutors";
|
||||||
|
|
||||||
|
MongoTaskDocument mongoTask = new MongoTaskDocument(testTaskId, testTaskName, testTaskType, testTaskOuri,
|
||||||
|
testTaskStatus, testTaskListName);
|
||||||
|
taskRepository.insert(mongoTask);
|
||||||
|
|
||||||
|
Task retrievedTask = adapterUnderTest.loadTask(new Task.TaskId(testTaskId),
|
||||||
|
new TaskList.TaskListName(testTaskListName));
|
||||||
|
|
||||||
|
assertThat(retrievedTask.getTaskName().getValue()).isEqualTo(testTaskName);
|
||||||
|
assertThat(retrievedTask.getTaskId().getValue()).isEqualTo(testTaskId);
|
||||||
|
assertThat(retrievedTask.getTaskStatus().getValue()).isEqualTo(Task.Status.valueOf(testTaskStatus));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package ch.unisg.tapastasks.tasks.application.service;
|
||||||
|
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
|
||||||
|
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.Task;
|
||||||
|
import ch.unisg.tapastasks.tasks.domain.TaskList;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.*;
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class AddNewTaskToTaskListServiceTest {
|
||||||
|
|
||||||
|
private final AddTaskPort addTaskPort = Mockito.mock(AddTaskPort.class);
|
||||||
|
private final TaskListLock taskListLock = Mockito.mock(TaskListLock.class);
|
||||||
|
private final NewTaskAddedEventPort newTaskAddedEventPort = Mockito.mock(NewTaskAddedEventPort.class);
|
||||||
|
private final AddNewTaskToTaskListService addNewTaskToTaskListService = new AddNewTaskToTaskListService(
|
||||||
|
newTaskAddedEventPort, addTaskPort, taskListLock);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addingSucceeds() {
|
||||||
|
|
||||||
|
Task newTask = givenATaskWithNameAndTypeAndURI(new Task.TaskName("test-task"),
|
||||||
|
new Task.TaskType("test-type"), Optional.of(new Task.OriginalTaskUri("example.org")));
|
||||||
|
|
||||||
|
TaskList taskList = givenAnEmptyTaskList(TaskList.getTapasTaskList());
|
||||||
|
|
||||||
|
AddNewTaskToTaskListCommand addNewTaskToTaskListCommand = new AddNewTaskToTaskListCommand(newTask.getTaskName(),
|
||||||
|
newTask.getTaskType(), Optional.ofNullable(newTask.getOriginalTaskUri()));
|
||||||
|
|
||||||
|
Task addedTask = addNewTaskToTaskListService.addNewTaskToTaskList(addNewTaskToTaskListCommand);
|
||||||
|
|
||||||
|
assertThat(addedTask).isNotNull();
|
||||||
|
assertThat(taskList.getListOfTasks().getValue()).hasSize(1);
|
||||||
|
|
||||||
|
then(taskListLock).should().lockTaskList(eq(TaskList.getTapasTaskList().getTaskListName()));
|
||||||
|
then(newTaskAddedEventPort).should(times(1))
|
||||||
|
.publishNewTaskAddedEvent(any(NewTaskAddedEvent.class));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskList givenAnEmptyTaskList(TaskList taskList) {
|
||||||
|
taskList.getListOfTasks().getValue().clear();
|
||||||
|
return taskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task givenATaskWithNameAndTypeAndURI(Task.TaskName taskName, Task.TaskType taskType,
|
||||||
|
Optional<Task.OriginalTaskUri> originalTaskUri) {
|
||||||
|
Task task = Mockito.mock(Task.class);
|
||||||
|
given(task.getTaskName()).willReturn(taskName);
|
||||||
|
given(task.getTaskType()).willReturn(taskType);
|
||||||
|
given(task.getOriginalTaskUri()).willReturn(originalTaskUri.get());
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package ch.unisg.tapastasks.tasks.domain;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class TaskListTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addNewTaskToTaskListSuccess() {
|
||||||
|
TaskList taskList = TaskList.getTapasTaskList();
|
||||||
|
taskList.getListOfTasks().getValue().clear();
|
||||||
|
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task"),
|
||||||
|
new Task.TaskType("My-Test-Type"));
|
||||||
|
|
||||||
|
assertThat(newTask.getTaskName().getValue()).isEqualTo("My-Test-Task");
|
||||||
|
assertThat(taskList.getListOfTasks().getValue()).hasSize(1);
|
||||||
|
assertThat(taskList.getListOfTasks().getValue().get(0)).isEqualTo(newTask);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void retrieveTaskSuccess() {
|
||||||
|
TaskList taskList = TaskList.getTapasTaskList();
|
||||||
|
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task2"),
|
||||||
|
new Task.TaskType("My-Test-Type2"));
|
||||||
|
|
||||||
|
Task retrievedTask = taskList.retrieveTaskById(newTask.getTaskId()).get();
|
||||||
|
|
||||||
|
assertThat(retrievedTask).isEqualTo(newTask);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void retrieveTaskFailure() {
|
||||||
|
TaskList taskList = TaskList.getTapasTaskList();
|
||||||
|
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task3"),
|
||||||
|
new Task.TaskType("My-Test-Type3"));
|
||||||
|
|
||||||
|
Task.TaskId fakeId = new Task.TaskId("fake-id");
|
||||||
|
|
||||||
|
Optional<Task> retrievedTask = taskList.retrieveTaskById(fakeId);
|
||||||
|
|
||||||
|
assertThat(retrievedTask.isPresent()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
spring.data.mongodb.uri=mongodb://127.0.0.1:27017
|
||||||
|
spring.data.mongodb.database=tapas-tasks
|
Loading…
Reference in New Issue
Block a user