Merge pull request #34 from myrle-krantz/develop

Implemented setting of comments on task instances.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
index 777e819..7a0550f 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
@@ -33,6 +33,7 @@
   String PUT_TASK_DEFINITION = "put-task-definition";
   String DELETE_TASK_DEFINITION = "delete-task-definition";
   String POST_CHARGE_DEFINITION = "post-charge-definition";
+  String PUT_TASK_INSTANCE = "put-task-instance";
   String PUT_CHARGE_DEFINITION = "put-charge-definition";
   String DELETE_PRODUCT_CHARGE_DEFINITION = "delete-product-charge-definition";
   String SELECTOR_INITIALIZE = SELECTOR_NAME + " = '" + INITIALIZE + "'";
@@ -45,6 +46,7 @@
   String SELECTOR_POST_TASK_DEFINITION = SELECTOR_NAME + " = '" + POST_TASK_DEFINITION + "'";
   String SELECTOR_PUT_TASK_DEFINITION = SELECTOR_NAME + " = '" + PUT_TASK_DEFINITION + "'";
   String SELECTOR_DELETE_TASK_DEFINITION = SELECTOR_NAME + " = '" + DELETE_TASK_DEFINITION + "'";
+  String SELECTOR_PUT_TASK_INSTANCE = SELECTOR_NAME + " = '" + PUT_TASK_INSTANCE + "'";
   String SELECTOR_POST_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + POST_CHARGE_DEFINITION + "'";
   String SELECTOR_PUT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + PUT_CHARGE_DEFINITION + "'";
   String SELECTOR_DELETE_PRODUCT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + DELETE_PRODUCT_CHARGE_DEFINITION + "'";
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/events/TaskInstanceEvent.java b/api/src/main/java/io/mifos/portfolio/api/v1/events/TaskInstanceEvent.java
new file mode 100644
index 0000000..bb4095e
--- /dev/null
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/events/TaskInstanceEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.api.v1.events;
+
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class TaskInstanceEvent {
+  private String productIdentifier;
+  private String caseIdentifier;
+  private String taskIdentifier;
+
+  public TaskInstanceEvent(String productIdentifier, String caseIdentifier, String taskIdentifier) {
+    this.productIdentifier = productIdentifier;
+    this.caseIdentifier = caseIdentifier;
+    this.taskIdentifier = taskIdentifier;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    TaskInstanceEvent that = (TaskInstanceEvent) o;
+    return Objects.equals(productIdentifier, that.productIdentifier) &&
+        Objects.equals(caseIdentifier, that.caseIdentifier) &&
+        Objects.equals(taskIdentifier, that.taskIdentifier);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(productIdentifier, caseIdentifier, taskIdentifier);
+  }
+
+  @Override
+  public String toString() {
+    return "TaskInstanceEvent{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", caseIdentifier='" + caseIdentifier + '\'' +
+        ", taskIdentifier='" + taskIdentifier + '\'' +
+        '}';
+  }
+}
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java b/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
index 956839c..ce838b5 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestTaskInstances.java
@@ -19,6 +19,8 @@
 import io.mifos.portfolio.api.v1.domain.Product;
 import io.mifos.portfolio.api.v1.domain.TaskDefinition;
 import io.mifos.portfolio.api.v1.domain.TaskInstance;
+import io.mifos.portfolio.api.v1.events.EventConstants;
+import io.mifos.portfolio.api.v1.events.TaskInstanceEvent;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -42,4 +44,24 @@
     Assert.assertEquals(1, taskInstances.size());
     Assert.assertEquals(taskDefinition.getIdentifier(), taskInstances.get(0).getTaskIdentifier());
   }
+
+  @Test
+  public void shouldCommentTaskInstance() throws InterruptedException {
+    final Product product = createProduct();
+    final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+    enableProduct(product);
+
+    final Case customerCase = createCase(product.getIdentifier());
+
+    final TaskInstance taskInstance = portfolioManager.getTaskForCase(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier());
+    taskInstance.setComment("And the words of the prophets are written on the subway walls");
+
+    portfolioManager.changeTaskForCase(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier(), taskInstance);
+    Assert.assertTrue(eventRecorder.wait(EventConstants.PUT_TASK_INSTANCE, new TaskInstanceEvent(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier())));
+
+    final TaskInstance taskInstanceChanged = portfolioManager.getTaskForCase(product.getIdentifier(), customerCase.getIdentifier(), taskDefinition.getIdentifier());
+    taskInstance.setComment("And the words of the prophets are written on the subway walls");
+    Assert.assertEquals(taskInstance, taskInstanceChanged);
+  }
 }
diff --git a/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java b/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java
new file mode 100644
index 0000000..6f01215
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/portfolio/listener/TaskInstanceEventListener.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.listener;
+
+import io.mifos.core.lang.config.TenantHeaderFilter;
+import io.mifos.core.test.listener.EventRecorder;
+import io.mifos.portfolio.api.v1.events.EventConstants;
+import io.mifos.portfolio.api.v1.events.TaskInstanceEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@Component
+public class TaskInstanceEventListener {
+
+  private final EventRecorder eventRecorder;
+
+  @Autowired
+  public TaskInstanceEventListener(final EventRecorder eventRecorder) {
+    super();
+    this.eventRecorder = eventRecorder;
+  }
+
+  @JmsListener(
+      subscription = EventConstants.DESTINATION,
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_PUT_TASK_INSTANCE
+  )
+  public void onChangeTaskInstance(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                                     final String payload) {
+    this.eventRecorder.event(tenant, EventConstants.PUT_TASK_INSTANCE, payload, TaskInstanceEvent.class);
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java
new file mode 100644
index 0000000..c7c26fc
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskInstanceCommandHandler.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.service.internal.command.handler;
+
+import io.mifos.core.command.annotation.Aggregate;
+import io.mifos.core.command.annotation.CommandHandler;
+import io.mifos.core.command.annotation.CommandLogLevel;
+import io.mifos.core.command.annotation.EventEmitter;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.portfolio.api.v1.domain.TaskInstance;
+import io.mifos.portfolio.api.v1.events.EventConstants;
+import io.mifos.portfolio.api.v1.events.TaskInstanceEvent;
+import io.mifos.portfolio.service.internal.command.ChangeTaskInstanceCommand;
+import io.mifos.portfolio.service.internal.mapper.TaskInstanceMapper;
+import io.mifos.portfolio.service.internal.repository.TaskInstanceEntity;
+import io.mifos.portfolio.service.internal.repository.TaskInstanceRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@Aggregate
+public class TaskInstanceCommandHandler {
+  private final TaskInstanceRepository taskInstanceRepository;
+
+  @Autowired
+  public TaskInstanceCommandHandler(TaskInstanceRepository taskInstanceRepository) {
+    this.taskInstanceRepository = taskInstanceRepository;
+  }
+
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.PUT_TASK_INSTANCE)
+  public TaskInstanceEvent process(final ChangeTaskInstanceCommand changeTaskInstanceCommand) {
+    final String productIdentifier = changeTaskInstanceCommand.getProductIdentifier();
+    final String caseIdentifier = changeTaskInstanceCommand.getCaseIdentifier();
+    final TaskInstance taskInstance = changeTaskInstanceCommand.getInstance();
+
+    final TaskInstanceEntity existingTaskInstance
+        = taskInstanceRepository.findByProductIdAndCaseIdAndTaskId(productIdentifier, caseIdentifier, taskInstance.getTaskIdentifier())
+        .orElseThrow(() -> ServiceException.notFound("Task instance ''{0}.{1}.{2}'' not found.",
+            productIdentifier, caseIdentifier, taskInstance.getTaskIdentifier()));
+
+    final TaskInstanceEntity taskInstanceEntity =
+        TaskInstanceMapper.mapOverOldEntity(taskInstance, existingTaskInstance);
+    taskInstanceRepository.save(taskInstanceEntity);
+
+    return new TaskInstanceEvent(
+        changeTaskInstanceCommand.getProductIdentifier(),
+        changeTaskInstanceCommand.getCaseIdentifier(),
+        changeTaskInstanceCommand.getInstance().getTaskIdentifier());
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/TaskInstanceMapper.java b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/TaskInstanceMapper.java
index 89a899c..97a77fd 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/mapper/TaskInstanceMapper.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/mapper/TaskInstanceMapper.java
@@ -33,4 +33,16 @@
 
     return ret;
   }
+
+  static TaskInstanceEntity mapOverOldEntity(final TaskInstance from, final TaskInstanceEntity oldEntity) {
+    final TaskInstanceEntity ret = new TaskInstanceEntity();
+    ret.setComment(from.getComment());
+
+    ret.setId(oldEntity.getId());
+    ret.setCustomerCase(oldEntity.getCustomerCase());
+    ret.setTaskDefinition(oldEntity.getTaskDefinition());
+    ret.setExecutedBy(oldEntity.getExecutedBy());
+    ret.setExecutedOn(oldEntity.getExecutedOn());
+    return ret;
+  }
 }