" + + "This is the REST documentation for [TASKANA](http://taskana.pro) - the " + + "world’s first open source solution for Enterprise Task Management." + + "
" + + ""
+ + "**For all Query Parameters:**
Whenever a parameter is an array type,"
+ + " several values can be passed by declaring that parameter multiple times."
+ + "
" + + "Whenever a parameter is a complex type, the attributes of the value-object" + + " can be passed as a json. For example, a complex parameter with the name " + + "\"complex-query-param\" and attributes \"attribute1\" and \"attribute2\" " + + "would be specified in the following way:complex-query-param={\"attribute1\"" + + ":\"value1\",\"attribute2\":\"value2\"}" + + "
" + + "" + + "Whenever a parameter is a value-less type (e.g owner-is-null and " + + "current-user) it is expected to be defined without a value, i.e., it should" + + " be specified as ?parameter and not ?parameter= or ?parameter=someValue" + + "
" + + "" + + "NOTE: HATEOAS support is still in development.Please have a look at " + + "example responses for each resource to determine the available links." + + "
" + + "" + + "TASKANA uses the [HATEOAS](https://restfulapi.net/hateoas/) (Hypermedia" + + " as the Engine of Application State) REST constraint. Most of our resources" + + " contain a _links section which contains navigation links. Besides, helping" + + " to navigate through our REST API, the navigation links also encapsulate the" + + " API. Using HATEOAS allows us to change some endpoints without modifying " + + "your frontend." + + "
" + + "" + + "In order to support multilingual websites, TASKANA uses error codes to " + + "define which error occurred. Additionally, an optional set of message " + + "variables, containing some technical information, is added, so that the " + + "website can describe the error with all details." + + "
" + + "Status Code | " + + "Key | " + + "Message Variables | " + + "
---|---|---|
**400 BAD_REQUEST** | " + + "CLASSIFICATION_SERVICE_LEVEL_MALFORMED | " + + "serviceLevel, classificationKey, domain | " + + "
**400 BAD_REQUEST** | " + + "CUSTOM_HOLIDAY_WRONG_FORMAT | " + + "customHoliday | " + + "
**400 BAD_REQUEST** | " + + "DOMAIN_NOT_FOUND | " + + "domain | " + + "
**400 BAD_REQUEST** | " + + "INVALID_ARGUMENT | " + + "" + + " |
**400 BAD_REQUEST** | " + + "QUERY_PARAMETER_MALFORMED | " + + "malformedQueryParameters | " + + "
**400 BAD_REQUEST** | " + + "TASK_INVALID_CALLBACK_STATE | " + + "taskId, taskCallbackState, requiredCallbackStates | " + + "
**400 BAD_REQUEST** | " + + "TASK_INVALID_OWNER | " + + "taskId, currentUserId | " + + "
**400 BAD_REQUEST** | " + + "TASK_INVALID_STATE | " + + "taskId, taskState, requiredTaskStates | " + + "
**403 FORBIDDEN** | " + + "NOT_AUTHORIZED | " + + "roles, currentUserId | " + + "
**403 FORBIDDEN** | " + + "NOT_AUTHORIZED_ON_TASK_COMMENT | " + + "currentUserId, taskCommentId | " + + "
**403 FORBIDDEN** | " + + "NOT_AUTHORIZED_ON_WORKBASKET_WITH_ID | " + + "currentUserId, workbasketId, requiredPermissions | " + + "
**403 FORBIDDEN** | " + + "NOT_AUTHORIZED_ON_WORKBASKET_WITH_KEY_AND_DOMAIN | " + + "currentUserId, workbasketKey, domain, requiredPermissions | " + + "
**404 NOT_FOUND** | " + + "CLASSIFICATION_WITH_ID_NOT_FOUND | " + + "classificationId | " + + "
**404 NOT_FOUND** | " + + "CLASSIFICATION_WITH_KEY_NOT_FOUND | " + + "classificationKey, domain | " + + "
**404 NOT_FOUND** | " + + "TASK_COMMENT_NOT_FOUND | " + + "taskCommentId | " + + "
**404 NOT_FOUND** | " + + "TASK_NOT_FOUND | " + + "taskId | " + + "
**404 NOT_FOUND** | " + + "USER_NOT_FOUND | " + + "userId | " + + "
**404 NOT_FOUND** | " + + "WORKBASKET_WITH_ID_NOT_FOUND | " + + "workbasketId | " + + "
**404 NOT_FOUND** | " + + "WORKBASKET_WITH_KEY_NOT_FOUND | " + + "workbasketKey, domain | " + + "
**409 CONFLICT** | " + + "ATTACHMENT_ALREADY_EXISTS | " + + "attachmentId, taskId | " + + "
**409 CONFLICT** | " + + "CLASSIFICATION_ALREADY_EXISTS | " + + "classificationKey, domain | " + + "
**409 CONFLICT** | " + + "ENTITY_NOT_UP_TO_DATE | " + + "entityId | " + + "
**409 CONFLICT** | " + + "TASK_ALREADY_EXISTS | " + + "externalTaskId | " + + "
**409 CONFLICT** | " + + "USER_ALREADY_EXISTS | " + + "userID | " + + "
**409 CONFLICT** | " + + "WORKBASKET_ACCESS_ITEM_ALREADY_EXISTS | " + + "accessId, workbasketId | " + + "
**409 CONFLICT** | " + + "WORKBASKET_ALREADY_EXISTS | " + + "workbasketKey, domain | " + + "
**409 CONFLICT** | " + + "WORKBASKET_MARKED_FOR_DELETION | " + + "workbasketId | " + + "
**413 PAYLOAD_TOO_LARGE** | " + + "PAYLOAD_TOO_LARGE | " + + "" + + " |
**423 LOCKED** | " + + "CLASSIFICATION_IN_USE | " + + "classificationKey, domain | " + + "
**423 LOCKED** | " + + "WORKBASKET_IN_USE | " + + "workbasketId | " + + "
**500 INTERNAL_SERVER_ERROR** | " + + "CONNECTION_AUTOCOMMIT_FAILED | " + + "" + + " |
**500 INTERNAL_SERVER_ERROR** | " + + "CONNECTION_NOT_SET | " + + "" + + " |
**500 INTERNAL_SERVER_ERROR** | " + + "CRITICAL_SYSTEM_ERROR | " + + "" + + " |
**500 INTERNAL_SERVER_ERROR** | " + + "DATABASE_UNSUPPORTED | " + + "databaseProductName | " + + "
**500 INTERNAL_SERVER_ERROR** | " + + "UNKNOWN_ERROR | " + + "" + + " |
Key | " + + "Type | " + + "
---|---|
accessId | " + + "String | " + + "
attachmentId | " + + "String | " + + "
classificationId | " + + "String | " + + "
classificationKey | " + + "String | " + + "
currentUserId | " + + "String | " + + "
customHoliday | " + + "String | " + + "
databaseProductName | " + + "String | " + + "
domain | " + + "String | " + + "
externalTaskId | " + + "String | " + + "
historyEventId | " + + "String | " + + "
malformedQueryParameters | " + + "MalformedQueryParameter[] | " + + "
requiredCallbackStates | " + + "CallbackState[] | " + + "
requiredPermissions | " + + "WorkbasketPermission[] | " + + "
requiredTaskStates | " + + "TaskState[] | " + + "
roles | " + + "TaskanaRole[] | " + + "
taskCallbackState | " + + "CallbackState | " + + "
taskCommentId | " + + "String | " + + "
taskId | " + + "String | " + + "
taskState | " + + "TaskState | " + + "
workbasketId | " + + "String | " + + "
workbasketKey | " + + "String | " + + "
The format is ISO-8601. */ + @Schema( + name = "created", + description = + "The creation timestamp of the classification in the system.
The format is ISO-8601.") private Instant created; /** * The timestamp of the last modification. * *
The format is ISO-8601. */ + @Schema( + name = "modified", + description = "The timestamp of the last modification.
The format is ISO-8601."
+ )
private Instant modified;
/** The description of the classification. */
+ @Schema(name = "description", description = "The description of the classification.")
private String description;
public Boolean getIsValidInDomain() {
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/models/ClassificationSummaryPagedRepresentationModel.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/models/ClassificationSummaryPagedRepresentationModel.java
index 5dd36f520..32c7fe74a 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/models/ClassificationSummaryPagedRepresentationModel.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/models/ClassificationSummaryPagedRepresentationModel.java
@@ -1,6 +1,7 @@
package pro.taskana.classification.rest.models;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.beans.ConstructorProperties;
import java.util.Collection;
@@ -17,6 +18,7 @@ public class ClassificationSummaryPagedRepresentationModel
}
/** the embedded classifications. */
+ @Schema(name = "classifications", description = "the embedded classifications.")
@Override
@JsonProperty("classifications")
public @NotNull Collection This is stated according to ISO 8601.
*/
- @NotNull protected String serviceLevel;
+ @Schema(
+ name = "serviceLevel",
+ description =
+ "The service level of the classification. This is stated according to ISO 8601.")
+ @NotNull
+ protected String serviceLevel;
/** The type of classification. Types can be configured in the file 'taskana.properties'. */
+ @Schema(
+ name = "type",
+ description =
+ "The type of classification. Types can be configured in the file 'taskana.properties'.")
protected String type;
/** A custom property with name "1". */
+ @Schema(name = "custom1", description = "A custom property with name \"1\".")
protected String custom1;
/** A custom property with name "2". */
+ @Schema(name = "custom2", description = "A custom property with name \"2\".")
protected String custom2;
/** A custom property with name "3". */
+ @Schema(name = "custom3", description = "A custom property with name \"3\".")
protected String custom3;
/** A custom property with name "4". */
+ @Schema(name = "custom4", description = "A custom property with name \"4\".")
protected String custom4;
/** A custom property with name "5". */
+ @Schema(name = "custom5", description = "A custom property with name \"5\".")
protected String custom5;
/** A custom property with name "6". */
+ @Schema(name = "custom6", description = "A custom property with name \"6\".")
protected String custom6;
/** A custom property with name "7". */
+ @Schema(name = "custom7", description = "A custom property with name \"7\".")
protected String custom7;
/** A custom property with name "8". */
+ @Schema(name = "custom8", description = "A custom property with name \"8\".")
protected String custom8;
public String getClassificationId() {
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/AccessIdController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/AccessIdController.java
index 61cfd52a1..990522ab4 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/AccessIdController.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/AccessIdController.java
@@ -1,7 +1,13 @@
package pro.taskana.common.rest;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.ResponseEntity;
@@ -39,6 +45,25 @@ public class AccessIdController {
* @throws NotAuthorizedException if the current user is not ADMIN or BUSINESS_ADMIN.
* @title Search for Access Id (users and groups)
*/
+ @Operation(
+ summary = "Search for Access Id (users and groups)",
+ description = "This endpoint searches a provided access Id in the configured ldap.",
+ parameters = {
+ @Parameter(
+ name = "search-for",
+ description = "the Access Id which should be searched for.",
+ example = "max",
+ required = true)
+ },
+ responses = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "a list of all found Access Ids",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(implementation = AccessIdRepresentationModel[].class)))
+ })
@GetMapping(path = RestEndpoints.URL_ACCESS_ID)
public ResponseEntity> searchUsersAndGroups(
@RequestParam("search-for") String searchFor)
@@ -64,6 +89,34 @@ public class AccessIdController {
* or ADMIN
* @title Search for Access Id (users) in TASKANA user role
*/
+ @Operation(
+ summary = "Search for Access Id (users) in TASKANA user role",
+ description =
+ "This endpoint searches AccessIds for a provided name or Access Id. It will only search "
+ + "and return users and members of groups which are configured with the requested "
+ + "TASKANA role. This search will only work if the users in the configured LDAP have"
+ + " an attribute that shows their group memberships, e.g. \"memberOf\"",
+ parameters = {
+ @Parameter(
+ name = "search-for",
+ description = "the name or Access Id which should be searched for.",
+ example = "user-1",
+ required = true),
+ @Parameter(
+ name = "role",
+ description = "the role for which all users should be searched for",
+ example = "user",
+ required = true)
+ },
+ responses = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "a list of all found Access Ids (users)",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(implementation = AccessIdRepresentationModel[].class)))
+ })
@GetMapping(path = RestEndpoints.URL_ACCESS_ID_WITH_NAME)
public ResponseEntity
> searchUsersByNameOrAccessIdForRole(
@RequestParam("search-for") String nameOrAccessId, @RequestParam("role") String role)
@@ -90,6 +143,25 @@ public class AccessIdController {
* @throws NotAuthorizedException if the current user is not ADMIN or BUSINESS_ADMIN.
* @title Get groups for Access Id
*/
+ @Operation(
+ summary = "Get groups for Access Id",
+ description = "This endpoint retrieves all groups a given Access Id belongs to.",
+ parameters = {
+ @Parameter(
+ name = "access-id",
+ description = "the Access Id whose groups should be determined.",
+ example = "teamlead-1",
+ required = true)
+ },
+ responses = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "a list of the group Access Ids the requested Access Id belongs to",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(implementation = AccessIdRepresentationModel[].class)))
+ })
@GetMapping(path = RestEndpoints.URL_ACCESS_ID_GROUPS)
public ResponseEntity
> getGroupsByAccessId(
@RequestParam("access-id") String accessId)
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryPagingParameter.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryPagingParameter.java
index 1d6b1a299..03a0ff56c 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryPagingParameter.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryPagingParameter.java
@@ -2,6 +2,7 @@ package pro.taskana.common.rest;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import java.beans.ConstructorProperties;
import java.util.List;
@@ -11,12 +12,26 @@ import pro.taskana.common.rest.models.PageMetadata;
public class QueryPagingParameter
> {
+ public Integer getPage() {
+ return page;
+ }
+
+ public Integer getPageSize() {
+ return pageSize;
+ }
+
/** Request a specific page. Requires the definition of the 'page-size'. */
+ @Schema(
+ name = "page",
+ description = "Request a specific page. Requires the definition of the 'page-size'.")
@JsonProperty("page")
@Min(1)
private final Integer page;
/** Defines the size for each page. This requires a specific requested 'page'. */
+ @Schema(
+ name = "page-size",
+ description = "Defines the size for each page. This requires a specific requested 'page'.")
@JsonProperty("page-size")
@Min(1)
private final Integer pageSize;
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QuerySortParameter.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QuerySortParameter.java
index 76aa1aed6..318dcc40d 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QuerySortParameter.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QuerySortParameter.java
@@ -1,6 +1,7 @@
package pro.taskana.common.rest;
import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import pro.taskana.common.api.BaseQuery;
import pro.taskana.common.api.BaseQuery.SortDirection;
@@ -12,6 +13,11 @@ public class QuerySortParameter
, S extends QuerySortBy
// the javadoc comment for this field is above its getter. This is done to define the type
// parameter S by overriding that getter and allowing spring-auto-rest-docs to properly detect
// the type parameter S.
+ @Schema(
+ name = "sort-by",
+ description =
+ "Sort the result by a given field. Multiple sort values can be declared. When the "
+ + "primary sort value is the same, the second one will be used.")
@JsonProperty("sort-by")
private final List
sortBy;
@@ -20,6 +26,13 @@ public class QuerySortParameter, S extends QuerySortBy
* of sort-by and order declarations have to match. Alternatively the value can be omitted. If
* done so the default sort order (ASCENDING) will be applied to every sort-by value.
*/
+ @Schema(
+ name = "order",
+ description =
+ "The order direction for each sort value. This value requires the use of 'sort-by'. The"
+ + " amount of sort-by and order declarations have to match. Alternatively the value"
+ + " can be omitted. If done so the default sort order (ASCENDING) will be applied to"
+ + " every sort-by value.")
@JsonProperty("order")
private final List
> getDomains() {
@@ -60,6 +77,24 @@ public class TaskanaEngineController {
* classification categories will be returned.
* @return the classification categories for the requested type.
*/
+ @Operation(
+ summary =
+ "This endpoint retrieves the configured classification categories for a specific "
+ + "classification type.",
+ parameters =
+ @Parameter(
+ name = "type",
+ description =
+ "The classification type whose categories should be determined. If not specified "
+ + "all classification categories will be returned."),
+ responses =
+ @ApiResponse(
+ responseCode = "200",
+ description = "The classification categories for the requested type.",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(implementation = String[].class))))
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity
> getClassificationCategories(
@@ -75,6 +110,16 @@ public class TaskanaEngineController {
*
* @return the configured classification types.
*/
+ @Operation(
+ summary = "This endpoint retrieves the configured classification types.",
+ responses =
+ @ApiResponse(
+ responseCode = "200",
+ description = "The configured classification types.",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(implementation = String[].class))))
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_TYPES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity
> getClassificationTypes() {
@@ -87,6 +132,18 @@ public class TaskanaEngineController {
*
* @return the configured classification categories
*/
+ @Operation(
+ summary =
+ "This endpoint retrieves all configured classification categories grouped by each "
+ + "classification type.",
+ responses =
+ @ApiResponse(
+ responseCode = "200",
+ description = "The configured classification categories.",
+ content =
+ @Content(
+ mediaType = MediaTypes.HAL_JSON_VALUE,
+ schema = @Schema(ref = "#/components/schemas/TypeMapSchema"))))
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES_BY_TYPES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity