The short, direct answer
Since the request speaks of executing the list of tasks (tasks are the resource that we're speaking of here), then if the task group has been moved forward to execution (that is, regardless of execution result), then it would be sensible that the response status will be 200 OK
. Otherwise, if there was a problem that would prevent execution of the task group, such as failing validation of the task objects, or some required service isn't available for example, then the response status should denote that error. Past that, when execution of the tasks commences, seeing as the tasks to perform are listed in the request body, then I would expect that the execution results will be listed in the response body.
The long, philosophical answer
I suspect that you are experiencing this dilemma because you are diverting from what HTTP was designed for. I suspect that you are attempting to use it as means of RMI (Remote Method Invocation) rather than as means to manage resources.
The RMI perspective is that you would design your URI scheme as you would functions in an application, and upon request, these would execute an action, and then return its result. Although these types of implementations are relatively common still, these often produce situations HTTP is in the way, rather than making things easy.
(Just to note, RMI through HTTP has its merits in some instances, though you'd still be wise to implement these methods in a non-blocking manner. You could maybe offer startTask
and getTaskStatus
for instance, both of which would return instantly.)
The design of HTTP is asking you to use it to manage resources instead. It wants to express things like "add a task" (via POST), "get a task" (via GET), "delete a task" (via DELETE) and so on. Designing our URI scheme in that way, we rarely find ourselves in conflict with what HTTP has to offer.
To provide an example of what I mean by a URI scheme that conforms to resource management (vs RMI), here's a layout that might work for your case:
/task?complete=[true/false]&start=[start_timestamp]&end=[end_timestamp] ...
GET
searches for tasks according to querystring
POST
adds a single task
/task/[id]
GET
responds with a single task's state object
/task/[id]/cancellation_request
POST
adds a cancellation request for the task.
/task/[id]/[property_name]
GET
returns the value of the property of a task of the specified id
/task_group?complete=[true/false]&start=[start_timestamp]&end=[end_timestamp] ...
GET
searches for task groups according to querystring
POST
adds a group of tasks
/task_group/[id]
GET
responds with a task group object, which includes a list of task objects of all of the tasks in the group.
... and so on
As you may have guessed, task execution in this scheme would be an asynchronous thing -- POST to /task
would not wait until the task has completed, or even until it actually started running -- It would simply queue it for execution and then respond that it succeeded to add that task, or that it failed, if the queue is full, for instance.
Note how the URIs have no verbs in them -- They represent resources or collections of resources. The only verbs in this entire scheme are the HTTP methods that are invoked upon these URIs (GET/POST in this case).
Just to hammer this in a little more, URI stands for "Unified Resource Identifier".
Examples of how the above URI scheme would be used
Executing a single task and tracking progress:
POST /task
with the task to execute
GET /task/[id]
until response object complete
has positive value while showing current status/progress. You can also implement updates with websocket if you want to avoid polling.
Executing a task group and tracking progress:
POST /task_group
with the group of tasks to execute
GET /task_group/[groupId]
until response object complete
property has positive value, showing individual task status (3 tasks completed out of 5, for example)