# tapas-tasks

Micro-service for Managing Tasks in a Task List implemented following Hexagonal Architecture.

Based on examples from book "Get Your Hands Dirty on Clean Architecture" by Tom Hombergs

Technologies: Java, Spring Boot, Maven

**Note:** this repository contains an [EditorConfig](https://editorconfig.org/) file (`.editorconfig`)
with default editor settings. EditorConfig is supported out-of-the-box by the IntelliJ IDE. To help maintain
consistent code styles, we recommend to reuse this editor configuration file in all your services.

## HTTP API Overview
The code we provide includes a minimalistic uniform HTTP API for (i) creating a new task, (ii) retrieving
a representation of the current state of a task, and (iii) patching the representation of a task, which
is mapped to a domain/integration event.

The representations exchanged with the API use two media types:
* a JSON-based format for task with the media type `application/task+json`; this media type is defined
  in the context of our project, but could be [registered with IANA](https://www.iana.org/assignments/media-types)
  to promote interoperability (see
  [TaskJsonRepresentation](src/main/java/ch/unisg/tapastasks/tasks/adapter/in/formats/TaskJsonRepresentation.java)
  for more details)
* the [JSON Patch](http://jsonpatch.com/) format with the registered media type `application/json-patch+json`, which is also a
  JSON-based format (see sample HTTP requests below).

For further developing and working with your HTTP API, we recommend to use [Postman](https://www.postman.com/).

### Creating a new task

A new task is created via an `HTTP POST` request to the `/tasks/` endpoint. The body of the request
must include a representation of the task to be created using the content type `application/task+json`
defined in the context of this project. A valid representation must include at least two required fields
(see [TaskJsonRepresentation](src/main/java/ch/unisg/tapastasks/tasks/adapter/in/formats/TaskJsonRepresentation.java)
for more details):
* `taskName`: a string that represents the name of the task to be created
* `taskType`: a string that represents the type of the task to be created

A sample HTTP request with `curl`:
```shell
curl -i --location --request POST 'http://localhost:8081/tasks/' \
--header 'Content-Type: application/task+json' \
--data-raw '{
  "taskName" : "task1",
  "taskType" : "computation",
  "originalTaskUri" : "http://example.org",
  "inputData" : "1+1"
}'

HTTP/1.1 201
Location: http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354
Content-Type: application/task+json
Content-Length: 170
Date: Sun, 17 Oct 2021 21:03:34 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"OPEN",
  "originalTaskUri":"http://example.org",
  "inputData":"1+1"
}
```

If the task is created successfuly, a `201 Created` status code is returned together with a
representation of the created task. The response also includes a `Location` header filed that points
to the URI of the newly created task.

### Retrieving a task

The representation of a task is retrieved via an `HTTP GET` request to the URI of task.

A sample HTTP request with `curl`:
```shell
curl -i --location --request GET 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 170
Date: Sun, 17 Oct 2021 21:07:04 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"OPEN",
  "originalTaskUri":"http://example.org",
  "inputData":"1+1"
}
```

### Patching a task

REST emphasizes the generality of interfaces to promote uniform interaction. For instance, we can use
the `HTTP PATCH` method to implement fine-grained updates to the representational state of a task, which
may translate to various domain/integration events. However, to conform to the uniform interface
contraint in REST, any such updates have to rely on standard knowledge — and thus to hide away the
implementation details of our service.

In addition to the `application/task+json` media type we defined for our uniform HTTP API, a standard
representation format we can use to specify fine-grained updates to the representation of tasks
is [JSON Patch](http://jsonpatch.com/). In what follow, we provide a few examples of `HTTP PATCH` requests.
For further details on the JSON Patch format, see also [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)).

#### Changing the status of a task from OPEN to ASSIGNED

Sample HTTP request that assigns the previously created task to group `tapas-group1`:

```shell
curl -i --location --request PATCH 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[ {"op" : "replace", "path": "/taskStatus", "value" : "ASSIGNED" },
  {"op" : "add", "path": "/serviceProvider", "value" : "tapas-group1" } ]'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 207
Date: Sun, 17 Oct 2021 21:20:58 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"ASSIGNED",
  "originalTaskUri":"http://example.org",
  "serviceProvider":"tapas-group1",
  "inputData":"1+1"
}
```

In this example, the requested patch includes two JSON Patch operations:
* an operation to `replace` the `taskStatus` already in the task's representation with the value `ASSIGNED`
* an operation to `add` to the task's representation a `serviceProvider` with the value `tapas-group1`

Internally, this request is mapped to a
[TaskAssignedEvent](src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedEvent.java).
The HTTP response returns a `200 OK` status code together with the updated representation of the task.

#### Changing the status of a task from to EXECUTED

Sample HTTP request that changes the status of the task to `EXECUTED` and adds an output result:

```shell
curl -i --location --request PATCH 'http://localhost:8081/tasks/cef2fa9d-367b-4e7f-bf06-3b1fea35f354' \
--header 'Content-Type: application/json-patch+json' \
--data-raw '[ {"op" : "replace", "path": "/taskStatus", "value" : "EXECUTED" },
  {"op" : "add", "path": "/outputData", "value" : "2" } ]'

HTTP/1.1 200
Content-Type: application/task+json
Content-Length: 224
Date: Sun, 17 Oct 2021 21:32:25 GMT

{
  "taskId":"cef2fa9d-367b-4e7f-bf06-3b1fea35f354",
  "taskName":"task1",
  "taskType":"computation",
  "taskStatus":"EXECUTED",
  "originalTaskUri":"http://example.org",
  "serviceProvider":"tapas-group1",
  "inputData":"1+1",
  "outputData":"2"
}
```

Internally, this request is mapped to a
[TaskExecutedEvent](src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskExecutedEvent.java).
The HTTP response returns a `200 OK` status code together with the updated representation of the task.
