diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index 2c5b192..ee925ca 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -78,6 +78,7 @@ jobs:
cd /home/${{ secrets.SSH_USER }}/
touch acme.json
sudo chmod 0600 acme.json
+ sudo docker-compose down --remove-orphans
sudo echo "PUB_IP=$(wget -qO- http://ipecho.net/plain | xargs echo)" | sed -e 's/\./-/g' > .env
sudo docker-compose up -d
diff --git a/docker-compose.yml b/docker-compose.yml
index 0081933..84b12fe 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -70,3 +70,36 @@ services:
- "traefik.http.routers.app.tls=true"
- "traefik.http.routers.app.entryPoints=web,websecure"
- "traefik.http.routers.app.tls.certresolver=le"
+
+ mongodb:
+ image: mongo
+ container_name: mongodb
+ restart: unless-stopped
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: root
+ MONGO_INITDB_ROOT_PASSWORD: 8nP7s0a # Can not be changed again later on
+ volumes:
+ - database:/data/db
+
+ dbadmin:
+ image: mongo-express
+ container_name: dbadmin
+ restart: unless-stopped
+ environment:
+ ME_CONFIG_BASICAUTH_USERNAME: student # Access to web interface: username
+ ME_CONFIG_BASICAUTH_PASSWORD: studious # Access to web interface: password
+ ME_CONFIG_MONGODB_ADMINUSERNAME: root
+ ME_CONFIG_MONGODB_ADMINPASSWORD: 8nP7s0a # must correspond to the db
+ ME_CONFIG_MONGODB_PORT: 27017 # Default 27017
+ ME_CONFIG_MONGODB_SERVER: mongodb
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.dbadmin.rule=Host(`dbadmin.${PUB_IP}.nip.io`)"
+ - "traefik.http.routers.dbadmin.service=dbadmin"
+ - "traefik.http.services.dbadmin.loadbalancer.server.port=8081"
+ - "traefik.http.routers.dbadmin.tls=true"
+ - "traefik.http.routers.dbadmin.entryPoints=web,websecure"
+ - "traefik.http.routers.dbadmin.tls.certresolver=le"
+
+volumes:
+ database:
diff --git a/tapas-tasks/pom.xml b/tapas-tasks/pom.xml
index 3eac732..7dcf6ae 100644
--- a/tapas-tasks/pom.xml
+++ b/tapas-tasks/pom.xml
@@ -27,6 +27,15 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.data
+ spring-data-mongodb
+ 3.2.6
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
org.projectlombok
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
index 2675391..78a6145 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
@@ -1,14 +1,20 @@
package ch.unisg.tapastasks;
+import ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb.TaskRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@SpringBootApplication
+@EnableMongoRepositories(basePackageClasses = TaskRepository.class)
public class TapasTasksApplication {
public static void main(String[] args) {
SpringApplication tapasTasksApp = new SpringApplication(TapasTasksApplication.class);
tapasTasksApp.run(args);
+
+
+
}
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/MongoTaskDocument.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/MongoTaskDocument.java
new file mode 100644
index 0000000..442e01e
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/MongoTaskDocument.java
@@ -0,0 +1,32 @@
+package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
+
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Data
+@Document(collection = "tasks")
+public class MongoTaskDocument {
+
+ @Id
+ public String taskId;
+
+ public String taskName;
+ public String taskType;
+ public String originalTaskUri;
+ public String taskStatus;
+ public String taskListName;
+
+
+ public MongoTaskDocument(String taskId, String taskName, String taskType,
+ String originalTaskUri,
+ String taskStatus, String taskListName) {
+
+ this.taskId = taskId;
+ this.taskName = taskName;
+ this.taskType = taskType;
+ this.originalTaskUri = originalTaskUri;
+ this.taskStatus = taskStatus;
+ this.taskListName = taskListName;
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskMapper.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskMapper.java
new file mode 100644
index 0000000..0af73b6
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskMapper.java
@@ -0,0 +1,30 @@
+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.springframework.stereotype.Component;
+
+@Component
+class TaskMapper {
+
+ Task mapToDomainEntity(MongoTaskDocument task) {
+ return Task.withIdNameTypeOriginaluriStatus(
+ new Task.TaskId(task.taskId),
+ new Task.TaskName(task.taskName),
+ new Task.TaskType(task.taskType),
+ new Task.OriginalTaskUri(task.originalTaskUri),
+ new Task.TaskStatus(Task.Status.valueOf(task.taskStatus))
+ );
+ }
+
+ MongoTaskDocument mapToMongoDocument(Task task) {
+ return new MongoTaskDocument(
+ task.getTaskId().getValue(),
+ task.getTaskName().getValue(),
+ task.getTaskType().getValue(),
+ task.getOriginalTaskUri().getValue(),
+ task.getTaskStatus().getValue().toString(),
+ TaskList.getTapasTaskList().getTaskListName().getValue()
+ );
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapter.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapter.java
new file mode 100644
index 0000000..beabf63
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskPersistenceAdapter.java
@@ -0,0 +1,34 @@
+package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
+
+import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
+import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class TaskPersistenceAdapter implements
+ AddTaskPort,
+ LoadTaskPort {
+
+ @Autowired
+ private final TaskRepository taskRepository;
+
+ private final TaskMapper taskMapper;
+
+ @Override
+ public void addTask(Task task) {
+ MongoTaskDocument mongoTaskDocument = taskMapper.mapToMongoDocument(task);
+ taskRepository.save(mongoTaskDocument);
+ }
+
+ @Override
+ public Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName) {
+ MongoTaskDocument mongoTaskDocument = taskRepository.findByTaskId(taskId.getValue(),taskListName.getValue());
+ Task task = taskMapper.mapToDomainEntity(mongoTaskDocument);
+ return task;
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskRepository.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskRepository.java
new file mode 100644
index 0000000..867671c
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/persistence/mongodb/TaskRepository.java
@@ -0,0 +1,14 @@
+package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface TaskRepository extends MongoRepository {
+
+ public MongoTaskDocument findByTaskId(String taskId, String taskListName);
+
+ public List findByTaskListName(String taskListName);
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/AddTaskPort.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/AddTaskPort.java
new file mode 100644
index 0000000..b87795d
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/AddTaskPort.java
@@ -0,0 +1,9 @@
+package ch.unisg.tapastasks.tasks.application.port.out;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+
+public interface AddTaskPort {
+
+ void addTask(Task task);
+
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/LoadTaskPort.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/LoadTaskPort.java
new file mode 100644
index 0000000..acca3c0
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/LoadTaskPort.java
@@ -0,0 +1,10 @@
+package ch.unisg.tapastasks.tasks.application.port.out;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+
+public interface LoadTaskPort {
+
+ Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName);
+
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/TaskListLock.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/TaskListLock.java
new file mode 100644
index 0000000..802abba
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/out/TaskListLock.java
@@ -0,0 +1,11 @@
+package ch.unisg.tapastasks.tasks.application.port.out;
+
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+
+public interface TaskListLock {
+
+ void lockTaskList(TaskList.TaskListName taskListName);
+
+ void releaseTaskList(TaskList.TaskListName taskListName);
+
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
index 2380fcf..d78de83 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
@@ -2,7 +2,9 @@ package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase;
+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.Task;
import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
@@ -17,11 +19,14 @@ import javax.transaction.Transactional;
public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase {
private final NewTaskAddedEventPort newTaskAddedEventPort;
+ private final AddTaskPort addTaskToRepositoryPort;
+ private final TaskListLock taskListLock;
@Override
public Task addNewTaskToTaskList(AddNewTaskToTaskListCommand command) {
TaskList taskList = TaskList.getTapasTaskList();
+ taskListLock.lockTaskList(taskList.getTaskListName());
Task newTask = (command.getOriginalTaskUri().isPresent()) ?
// Create a delegated task that points back to the original task
taskList.addNewTaskWithNameAndTypeAndOriginalTaskUri(command.getTaskName(),
@@ -29,6 +34,9 @@ public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase
// Create an original task
: taskList.addNewTaskWithNameAndType(command.getTaskName(), command.getTaskType());
+ addTaskToRepositoryPort.addTask(newTask);
+ taskListLock.releaseTaskList(taskList.getTaskListName());
+
//Here we are using the application service to emit the domain event to the outside of the bounded context.
//This event should be considered as a light-weight "integration event" to communicate with other services.
//Domain events are usually rather "fat". In our implementation we simplify at this point. In general, it is
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/NoOpTaskListLock.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/NoOpTaskListLock.java
new file mode 100644
index 0000000..783dca1
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/NoOpTaskListLock.java
@@ -0,0 +1,18 @@
+package ch.unisg.tapastasks.tasks.application.service;
+
+import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NoOpTaskListLock implements TaskListLock {
+ @Override
+ public void lockTaskList(TaskList.TaskListName taskListName) {
+ //do nothing
+ }
+
+ @Override
+ public void releaseTaskList(TaskList.TaskListName taskListName) {
+ //do nothing
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
index fd6aea5..5e5ec29 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/RetrieveTaskFromTaskListService.java
@@ -2,6 +2,7 @@ package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListQuery;
import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListUseCase;
+import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
@@ -14,9 +15,17 @@ import java.util.Optional;
@Component
@Transactional
public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase {
+
+ private final LoadTaskPort loadTaskFromRepositoryPort;
+
@Override
public Optional retrieveTaskFromTaskList(RetrieveTaskFromTaskListQuery query) {
TaskList taskList = TaskList.getTapasTaskList();
- return taskList.retrieveTaskById(query.getTaskId());
+
+ Optional task = taskList.retrieveTaskById(query.getTaskId());
+
+ Optional taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(query.getTaskId(), taskList.getTaskListName()));
+
+ return taskFromRepo;
}
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
index b664a64..7a02976 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
@@ -49,17 +49,39 @@ public class Task {
this.outputData = null;
}
- protected static Task createTaskWithNameAndType(TaskName name, TaskType type) {
+ //Constructor from repo
+ public Task(TaskId taskId, TaskName taskName, TaskType taskType, OriginalTaskUri taskUri,
+ TaskStatus taskStatus) {
+ this.taskId = taskId;
+ this.taskName = taskName;
+ this.taskType = taskType;
+ this.originalTaskUri = taskUri;
+ this.taskStatus = taskStatus;
+ this.inputData = null;
+ this.outputData = null;
+ }
+
+
+ public static Task createTaskWithNameAndType(TaskName name, TaskType type) {
//This is a simple debug message to see that the request has reached the right method in the core
System.out.println("New Task: " + name.getValue() + " " + type.getValue());
return new Task(name, type, null);
}
- protected static Task createTaskWithNameAndTypeAndOriginalTaskUri(TaskName name, TaskType type,
+ public static Task createTaskWithNameAndTypeAndOriginalTaskUri(TaskName name, TaskType type,
OriginalTaskUri originalTaskUri) {
return new Task(name, type, originalTaskUri);
}
+ //This is for recreating a task from a repository.
+ public static Task withIdNameTypeOriginaluriStatus(TaskId taskId, TaskName taskName,
+ TaskType taskType,
+ OriginalTaskUri originalTaskUri,
+ TaskStatus taskStatus) {
+ return new Task(taskId, taskName, taskType, originalTaskUri, taskStatus);
+ }
+
+
@Value
public static class TaskId {
String value;
diff --git a/tapas-tasks/src/main/resources/application.properties b/tapas-tasks/src/main/resources/application.properties
index fe25873..badc0f8 100644
--- a/tapas-tasks/src/main/resources/application.properties
+++ b/tapas-tasks/src/main/resources/application.properties
@@ -1,2 +1,7 @@
server.port=8081
+//spring.data.mongodb.uri=mongodb://127.0.0.1:27017
+spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
+spring.data.mongodb.database=tapas-tasks
baseuri=https://tapas-tasks.86-119-34-23.nip.io/
+
+