├── CODE_OF_CONDUCT.md ├── extension ├── spring-boot-starter-camunda-tasklist │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── META-INF │ │ │ │ │ ├── spring │ │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ │ └── spring.factories │ │ │ │ └── tasklist-profiles │ │ │ │ │ ├── simple.yaml │ │ │ │ │ ├── oidc.yaml │ │ │ │ │ └── saas.yaml │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── camunda │ │ │ │ └── tasklist │ │ │ │ └── spring │ │ │ │ ├── ObjectMapperConfiguration.java │ │ │ │ ├── TasklistClientConfigurationProperties.java │ │ │ │ ├── TasklistPropertiesPostProcessor.java │ │ │ │ └── TasklistClientConfiguration.java │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── camunda │ │ │ └── tasklist │ │ │ └── spring │ │ │ ├── TestApp.java │ │ │ ├── TasklistClientConfigurationPropertiesProfileSimpleTest.java │ │ │ ├── BeanNameTest.java │ │ │ ├── TasklistClientConfigurationPropertiesProfileOidcTest.java │ │ │ └── TasklistClientConfigurationPropertiesProfileSaasTest.java │ └── pom.xml ├── client-java │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── camunda │ │ │ │ └── tasklist │ │ │ │ ├── dto │ │ │ │ ├── SearchType.java │ │ │ │ ├── VariableType.java │ │ │ │ ├── TaskState.java │ │ │ │ ├── Variable.java │ │ │ │ ├── DateFilter.java │ │ │ │ ├── Form.java │ │ │ │ ├── TaskList.java │ │ │ │ ├── Pagination.java │ │ │ │ ├── Task.java │ │ │ │ └── TaskSearch.java │ │ │ │ ├── CamundaTasklistConstants.java │ │ │ │ ├── auth │ │ │ │ ├── JwtCredential.java │ │ │ │ ├── Authentication.java │ │ │ │ ├── ErrorCodeHandler.java │ │ │ │ ├── SimpleCredential.java │ │ │ │ ├── TokenResponseMapper.java │ │ │ │ ├── TokenResponse.java │ │ │ │ ├── DefaultErrorCodeHandler.java │ │ │ │ ├── TokenResponseHttpClientResponseHandler.java │ │ │ │ ├── JwtAuthentication.java │ │ │ │ └── SimpleAuthentication.java │ │ │ │ ├── exception │ │ │ │ ├── CompatibilityException.java │ │ │ │ └── TaskListException.java │ │ │ │ ├── CamundaTasklistClientConfiguration.java │ │ │ │ ├── util │ │ │ │ ├── JsonUtils.java │ │ │ │ └── ConverterUtils.java │ │ │ │ ├── TasklistClient.java │ │ │ │ └── CamundaTaskListClientBuilder.java │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── camunda │ │ │ └── tasklist │ │ │ ├── TaskSearchTest.java │ │ │ ├── auth │ │ │ └── TokenResponseTest.java │ │ │ ├── JsonMapperTest.java │ │ │ └── CamundaTasklistClientTest.java │ └── pom.xml ├── pom.xml └── generated │ ├── pom.xml │ ├── tasklist-8.3.0.json │ ├── tasklist-8.3.1.json │ ├── tasklist-8.3.3.json │ └── tasklist-8.4.0.json ├── .github └── workflows │ ├── mvn-build.yml │ ├── backport.yml │ └── mvn-release.yml ├── example ├── bootstrapping-test │ ├── src │ │ ├── test │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── camunda │ │ │ │ └── tasklist │ │ │ │ └── bootstrapping │ │ │ │ └── BootstrappingTest.java │ │ └── main │ │ │ └── java │ │ │ └── io │ │ │ └── camunda │ │ │ └── tasklist │ │ │ └── bootstrapping │ │ │ └── Bootstrapper.java │ └── pom.xml ├── pom.xml └── readme-snippets │ ├── pom.xml │ └── src │ └── main │ └── java │ └── io │ └── camunda │ └── tasklist │ └── example │ └── TasklistClientBootstrapper.java ├── .gitignore ├── renovate.json ├── CONTRIBUTING.md ├── README.md ├── LICENSE └── pom.xml /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ### View the [Camunda Code of Conduct](https://camunda.com/events/code-conduct/) and find ways to report violations. 4 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.camunda.tasklist.spring.TasklistClientConfiguration -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.env.EnvironmentPostProcessor=io.camunda.tasklist.spring.TasklistPropertiesPostProcessor 2 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/SearchType.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | public enum SearchType { 4 | BEFORE, 5 | BEFORE_OR_EQUAL, 6 | AFTER, 7 | AFTER_OR_EQUAL 8 | } 9 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/CamundaTasklistConstants.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | public interface CamundaTasklistConstants { 4 | String CAMUNDA_FORMS_PREFIX = "camunda-forms:bpmn:"; 5 | } 6 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/VariableType.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | public enum VariableType { 4 | STRING, 5 | NUMBER, 6 | BOOLEAN, 7 | LIST, 8 | MAP, 9 | JSONNODE 10 | } 11 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/JwtCredential.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import java.net.URL; 4 | 5 | public record JwtCredential( 6 | String clientId, String clientSecret, String audience, URL authUrl, String scope) {} 7 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/Authentication.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import java.util.Map; 4 | 5 | public interface Authentication { 6 | 7 | Map getTokenHeader(); 8 | 9 | void resetToken(); 10 | } 11 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/resources/tasklist-profiles/simple.yaml: -------------------------------------------------------------------------------- 1 | tasklist: 2 | client: 3 | profile: simple 4 | enabled: true 5 | base-url: http://localhost:8082 6 | session-timeout: PT10M 7 | username: demo 8 | password: demo 9 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/ErrorCodeHandler.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import org.apache.hc.core5.http.ClassicHttpResponse; 4 | 5 | public interface ErrorCodeHandler { 6 | RuntimeException handleError(ClassicHttpResponse response); 7 | } 8 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/resources/tasklist-profiles/oidc.yaml: -------------------------------------------------------------------------------- 1 | tasklist: 2 | client: 3 | profile: oidc 4 | enabled: true 5 | base-url: http://localhost:8082 6 | auth-url: http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token 7 | audience: tasklist-api 8 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/resources/tasklist-profiles/saas.yaml: -------------------------------------------------------------------------------- 1 | tasklist: 2 | client: 3 | profile: saas 4 | enabled: true 5 | base-url: https://${tasklist.client.region}.tasklist.camunda.io/${tasklist.client.cluster-id} 6 | auth-url: https://login.cloud.camunda.io/oauth/token 7 | audience: tasklist.camunda.io 8 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/SimpleCredential.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import java.net.URL; 4 | import java.time.Duration; 5 | 6 | /** Contains credential for particular product. Used for Simple authentication. */ 7 | public record SimpleCredential( 8 | String username, String password, URL baseUrl, Duration sessionTimeout) {} 9 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/exception/CompatibilityException.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.exception; 2 | 3 | public class CompatibilityException extends RuntimeException { 4 | public CompatibilityException(String message) { 5 | super(message); 6 | } 7 | 8 | public CompatibilityException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/mvn-build.yml: -------------------------------------------------------------------------------- 1 | name: Build via Maven and run tests 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v6 10 | - name: Set up Java environment 11 | uses: actions/setup-java@v5 12 | with: 13 | java-version: '21' 14 | distribution: 'adopt' 15 | cache: maven 16 | - name: Build with Maven 17 | run: mvn verify -PcheckFormat -B 18 | -------------------------------------------------------------------------------- /example/bootstrapping-test/src/test/java/io/camunda/tasklist/bootstrapping/BootstrappingTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.bootstrapping; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import io.camunda.tasklist.CamundaTaskListClient; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class BootstrappingTest { 9 | @Test 10 | void shouldRun() { 11 | CamundaTaskListClient camundaTaskListClient = new Bootstrapper().create(); 12 | assertNotNull(camundaTaskListClient); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/exception/TaskListException.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.exception; 2 | 3 | public class TaskListException extends Exception { 4 | 5 | public TaskListException() { 6 | super(); 7 | } 8 | 9 | public TaskListException(Exception e) { 10 | super(e); 11 | } 12 | 13 | public TaskListException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskListException(String message, Exception e) { 18 | super(message, e); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | target 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | .sts4-cache 17 | bin/ 18 | !**/src/main/**/bin/ 19 | !**/src/test/**/bin/ 20 | 21 | ### IntelliJ IDEA ### 22 | .idea 23 | *.iws 24 | *.iml 25 | *.ipr 26 | out/ 27 | !**/src/main/**/out/ 28 | !**/src/test/**/out/ 29 | 30 | ### NetBeans ### 31 | /nbproject/private/ 32 | /nbbuild/ 33 | /dist/ 34 | /nbdist/ 35 | /.nb-gradle/ 36 | 37 | ### VS Code ### 38 | .vscode/ 39 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/TaskState.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum TaskState { 6 | CREATED("CREATED"), 7 | COMPLETED("COMPLETED"), 8 | CANCELED("CANCELED"), 9 | FAILED("FAILED"); 10 | 11 | private final String rawValue; 12 | 13 | TaskState(String rawValue) { 14 | this.rawValue = rawValue; 15 | } 16 | 17 | public String getRawValue() { 18 | return rawValue; 19 | } 20 | 21 | public static TaskState fromJson(@JsonProperty("rawValue") String rawValue) { 22 | return valueOf(rawValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/test/java/io/camunda/tasklist/spring/TestApp.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import io.camunda.client.CamundaClient; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | @SpringBootApplication 9 | public class TestApp { 10 | public static void main(String[] args) { 11 | SpringApplication.run(TestApp.class, args); 12 | } 13 | 14 | @Bean 15 | public CamundaClient camundaClient() { 16 | return CamundaClient.newClient(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extension/client-java/src/test/java/io/camunda/tasklist/TaskSearchTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import io.camunda.tasklist.dto.TaskSearch; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class TaskSearchTest { 9 | @Test 10 | void shouldCloneTaskSearch() { 11 | TaskSearch taskSearch = new TaskSearch().fetchVariable("foo"); 12 | TaskSearch clone = taskSearch.clone(); 13 | assertThat(clone).isNotSameAs(taskSearch); 14 | assertThat(clone.getIncludeVariables()).hasSize(1); 15 | assertThat(clone.getIncludeVariables().get(0).getName()).isEqualTo("foo"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/java/io/camunda/tasklist/spring/ObjectMapperConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | @ConditionalOnMissingBean(ObjectMapper.class) 10 | public class ObjectMapperConfiguration { 11 | @Bean 12 | @ConditionalOnMissingBean 13 | public ObjectMapper objectMapper() { 14 | return new ObjectMapper(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /extension/client-java/src/test/java/io/camunda/tasklist/auth/TokenResponseTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import io.camunda.tasklist.auth.TokenResponseMapper.JacksonTokenResponseMapper; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class TokenResponseTest { 10 | @Test 11 | void shouldIgnoreUnknownFields() { 12 | TokenResponseMapper tokenResponseMapper = new JacksonTokenResponseMapper(new ObjectMapper()); 13 | TokenResponse tokenResponse = tokenResponseMapper.readToken("{\"weird_field\":123}"); 14 | assertThat(tokenResponse).isNotNull(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-root 7 | 8.8.0-SNAPSHOT 8 | 9 | tasklist-client-example-parent 10 | pom 11 | Camunda Tasklist Client Example Parent 12 | 13 | bootstrapping-test 14 | readme-snippets 15 | 16 | 17 | -------------------------------------------------------------------------------- /extension/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-root 7 | 8.8.0-SNAPSHOT 8 | 9 | tasklist-client-parent 10 | pom 11 | Camunda Tasklist Client Parent 12 | 13 | generated 14 | client-java 15 | spring-boot-starter-camunda-tasklist 16 | 17 | 18 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/CamundaTasklistClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import io.camunda.client.CamundaClient; 4 | import io.camunda.tasklist.auth.Authentication; 5 | import java.net.URL; 6 | import java.util.List; 7 | 8 | public record CamundaTasklistClientConfiguration( 9 | ApiVersion apiVersion, 10 | Authentication authentication, 11 | URL baseUrl, 12 | CamundaClient camundaClient, 13 | DefaultProperties defaultProperties) { 14 | public record DefaultProperties( 15 | boolean returnVariables, 16 | boolean loadTruncatedVariables, 17 | boolean useCamundaUserTasks, 18 | List tenantIds) {} 19 | 20 | public enum ApiVersion { 21 | v1, 22 | v2 23 | } 24 | 25 | public static List DEFAULT_TENANT_IDS = List.of(""); 26 | } 27 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/Variable.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | public class Variable { 4 | private String id; 5 | 6 | private String name; 7 | 8 | private Object value; 9 | 10 | private VariableType type; 11 | 12 | public String getId() { 13 | return id; 14 | } 15 | 16 | public void setId(String id) { 17 | this.id = id; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public Object getValue() { 29 | return value; 30 | } 31 | 32 | public void setValue(Object value) { 33 | this.value = value; 34 | } 35 | 36 | public VariableType getType() { 37 | return type; 38 | } 39 | 40 | public void setType(VariableType type) { 41 | this.type = type; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/TokenResponseMapper.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | public interface TokenResponseMapper { 7 | TokenResponse readToken(String token); 8 | 9 | public class JacksonTokenResponseMapper implements TokenResponseMapper { 10 | private final ObjectMapper objectMapper; 11 | 12 | public JacksonTokenResponseMapper(ObjectMapper objectMapper) { 13 | this.objectMapper = objectMapper; 14 | } 15 | 16 | @Override 17 | public TokenResponse readToken(String token) { 18 | try { 19 | return objectMapper.readValue(token, TokenResponse.class); 20 | } catch (JsonProcessingException e) { 21 | throw new RuntimeException("Error while reading token " + token, e); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/util/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.util; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.camunda.tasklist.exception.TaskListException; 6 | import java.io.IOException; 7 | 8 | public class JsonUtils { 9 | 10 | private JsonUtils() {} 11 | 12 | private static ObjectMapper mapper; 13 | 14 | public static JsonNode toJsonNode(String json) throws IOException { 15 | if (mapper == null) { 16 | mapper = new ObjectMapper(); 17 | } 18 | return mapper.readTree(json); 19 | } 20 | 21 | public static String toJsonString(Object object) throws TaskListException { 22 | if (mapper == null) { 23 | mapper = new ObjectMapper(); 24 | } 25 | try { 26 | return mapper.writeValueAsString(object); 27 | } catch (IOException e) { 28 | throw new TaskListException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/DateFilter.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.OffsetDateTime; 5 | import java.time.ZoneId; 6 | 7 | public class DateFilter { 8 | private OffsetDateTime from; 9 | 10 | private OffsetDateTime to; 11 | 12 | public DateFilter(OffsetDateTime from, OffsetDateTime to) { 13 | this.from = from; 14 | this.to = to; 15 | } 16 | 17 | public DateFilter(LocalDateTime from, LocalDateTime to) { 18 | this.from = from.atZone(ZoneId.systemDefault()).toOffsetDateTime(); 19 | this.to = to.atZone(ZoneId.systemDefault()).toOffsetDateTime(); 20 | } 21 | 22 | public OffsetDateTime getFrom() { 23 | return from; 24 | } 25 | 26 | public void setFrom(OffsetDateTime from) { 27 | this.from = from; 28 | } 29 | 30 | public OffsetDateTime getTo() { 31 | return to; 32 | } 33 | 34 | public void setTo(OffsetDateTime to) { 35 | this.to = to; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/readme-snippets/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-example-parent 7 | 8.8.0-SNAPSHOT 8 | 9 | readme-snippets 10 | Camunda Tasklist Client Readme Snippets 11 | 12 | 13 | io.camunda 14 | camunda-tasklist-client-java 15 | 16 | 17 | com.fasterxml.jackson.core 18 | jackson-databind 19 | 20 | 21 | io.camunda 22 | camunda-client-java 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/bootstrapping-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-example-parent 7 | 8.8.0-SNAPSHOT 8 | 9 | bootstrapping-test 10 | Camunda Tasklist Client Bootstrapping Test 11 | 12 | 13 | io.camunda 14 | camunda-tasklist-client-java 15 | 16 | 17 | io.camunda 18 | camunda-client-java 19 | 20 | 21 | org.junit.jupiter 22 | junit-jupiter-api 23 | test 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended", 4 | "schedule:automergeNonOfficeHours", 5 | ":automergeAll" 6 | ], 7 | "labels": [ 8 | "dependencies" 9 | ], 10 | "packageRules": [ 11 | { 12 | "description": "Never update Tasklist dependencies", 13 | "enabled": false, 14 | "matchPackageNames": [ 15 | "io.camunda:tasklist*{/,}**" 16 | ] 17 | }, 18 | { 19 | "description": "Never update camunda dependencies minor and major after a release", 20 | "matchPackageNames": [ 21 | "io.camunda:*{/,}**" 22 | ], 23 | "matchBaseBranches": [ 24 | "/^release/.*/" 25 | ], 26 | "matchUpdateTypes": [ 27 | "major", 28 | "minor" 29 | ], 30 | "enabled": false 31 | }, 32 | { 33 | "matchManagers": [ 34 | "maven" 35 | ], 36 | "description": "Exclude SNAPSHOT versions, renovate may suggest them for pre-release values.", 37 | "allowedVersions": "!/-SNAPSHOT$/", 38 | "matchPackageNames": [ 39 | "/.*/" 40 | ] 41 | } 42 | ], 43 | "baseBranchPatterns": [ 44 | "main", 45 | "/^release/.*/" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/TokenResponse.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class TokenResponse { 8 | 9 | @JsonProperty("access_token") 10 | private String accessToken; 11 | 12 | private String scope; 13 | 14 | @JsonProperty("expires_in") 15 | private Integer expiresIn; 16 | 17 | @JsonProperty("token_type") 18 | private String tokenType; 19 | 20 | TokenResponse() {} 21 | 22 | public String getAccessToken() { 23 | return accessToken; 24 | } 25 | 26 | public void setAccessToken(String accessToken) { 27 | this.accessToken = accessToken; 28 | } 29 | 30 | public String getScope() { 31 | return scope; 32 | } 33 | 34 | public void setScope(String scope) { 35 | this.scope = scope; 36 | } 37 | 38 | public Integer getExpiresIn() { 39 | return expiresIn; 40 | } 41 | 42 | public void setExpiresIn(Integer expiresIn) { 43 | this.expiresIn = expiresIn; 44 | } 45 | 46 | public String getTokenType() { 47 | return tokenType; 48 | } 49 | 50 | public void setTokenType(String tokenType) { 51 | this.tokenType = tokenType; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/test/java/io/camunda/tasklist/spring/TasklistClientConfigurationPropertiesProfileSimpleTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import static io.camunda.tasklist.spring.TasklistClientConfigurationProperties.Profile.*; 4 | import static org.assertj.core.api.Assertions.*; 5 | 6 | import java.net.MalformedURLException; 7 | import java.net.URI; 8 | import java.time.Duration; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | @SpringBootTest(properties = {"tasklist.client.profile=simple"}) 14 | public class TasklistClientConfigurationPropertiesProfileSimpleTest { 15 | @Autowired TasklistClientConfigurationProperties properties; 16 | 17 | @Test 18 | void shouldApplyProfiles() throws MalformedURLException { 19 | assertThat(properties.profile()).isEqualTo(simple); 20 | assertThat(properties.username()).isEqualTo("demo"); 21 | assertThat(properties.password()).isEqualTo("demo"); 22 | assertThat(properties.baseUrl()).isEqualTo(URI.create("http://localhost:8082").toURL()); 23 | assertThat(properties.enabled()).isEqualTo(true); 24 | assertThat(properties.sessionTimeout()).isEqualTo(Duration.ofMinutes(10)); 25 | assertThat(properties.defaults()).isNotNull(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/java/io/camunda/tasklist/spring/TasklistClientConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import java.net.URL; 4 | import java.time.Duration; 5 | import java.util.List; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | 9 | @ConfigurationProperties("tasklist.client") 10 | public record TasklistClientConfigurationProperties( 11 | // generic properties 12 | Profile profile, 13 | Boolean enabled, 14 | URL baseUrl, 15 | @DefaultValue ClientDefaults defaults, 16 | // simple auth properties 17 | String username, 18 | String password, 19 | Duration sessionTimeout, 20 | // oidc auth properties 21 | String clientId, 22 | String clientSecret, 23 | URL authUrl, 24 | String audience, 25 | String scope, 26 | // saas auth properies 27 | String region, 28 | String clusterId) { 29 | public enum Profile { 30 | simple, 31 | oidc, 32 | saas, 33 | v2 34 | } 35 | 36 | public record ClientDefaults( 37 | @DefaultValue("true") boolean returnVariables, 38 | @DefaultValue("true") boolean loadTruncatedVariables, 39 | @DefaultValue("true") boolean useZeebeUserTasks, 40 | @DefaultValue("") List tenantIds) {} 41 | } 42 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/Form.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | public class Form { 4 | private String id; 5 | 6 | private String processDefinitionId; 7 | 8 | private String schema; 9 | 10 | private Long version; 11 | 12 | private String tenantId; 13 | 14 | private Boolean isDeleted; 15 | 16 | public String getId() { 17 | return id; 18 | } 19 | 20 | public void setId(String id) { 21 | this.id = id; 22 | } 23 | 24 | public String getProcessDefinitionId() { 25 | return processDefinitionId; 26 | } 27 | 28 | public void setProcessDefinitionId(String processDefinitionId) { 29 | this.processDefinitionId = processDefinitionId; 30 | } 31 | 32 | public String getSchema() { 33 | return schema; 34 | } 35 | 36 | public void setSchema(String schema) { 37 | this.schema = schema; 38 | } 39 | 40 | public Long getVersion() { 41 | return version; 42 | } 43 | 44 | public void setVersion(Long version) { 45 | this.version = version; 46 | } 47 | 48 | public String getTenantId() { 49 | return tenantId; 50 | } 51 | 52 | public void setTenantId(String tenantId) { 53 | this.tenantId = tenantId; 54 | } 55 | 56 | public Boolean getIsDeleted() { 57 | return isDeleted; 58 | } 59 | 60 | public void setIsDeleted(Boolean isDeleted) { 61 | this.isDeleted = isDeleted; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/TaskList.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | 6 | public class TaskList implements Iterable { 7 | 8 | private List items; 9 | 10 | private TaskSearch search; 11 | 12 | public List getItems() { 13 | return items; 14 | } 15 | 16 | public TaskList setItems(List items) { 17 | this.items = items; 18 | return this; 19 | } 20 | 21 | public Task first() { 22 | if (items != null && !items.isEmpty()) { 23 | return items.get(0); 24 | } 25 | return null; 26 | } 27 | 28 | public Task get(int index) { 29 | if (items != null && !items.isEmpty()) { 30 | return items.get(index); 31 | } 32 | return null; 33 | } 34 | 35 | public Task last() { 36 | if (items != null && !items.isEmpty()) { 37 | return items.get(items.size() - 1); 38 | } 39 | return null; 40 | } 41 | 42 | public int size() { 43 | if (items != null) { 44 | return items.size(); 45 | } 46 | return 0; 47 | } 48 | 49 | public TaskSearch getSearch() { 50 | return search; 51 | } 52 | 53 | public TaskList setSearch(TaskSearch search) { 54 | this.search = search; 55 | return this; 56 | } 57 | 58 | @Override 59 | public Iterator iterator() { 60 | return items.iterator(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/DefaultErrorCodeHandler.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.io.Reader; 7 | import java.io.StringWriter; 8 | import org.apache.hc.core5.http.ClassicHttpResponse; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class DefaultErrorCodeHandler implements ErrorCodeHandler { 13 | private static final Logger LOG = LoggerFactory.getLogger(DefaultErrorCodeHandler.class); 14 | 15 | @Override 16 | public RuntimeException handleError(ClassicHttpResponse response) { 17 | String message = 18 | "Unsuccessful response: Code " 19 | + response.getCode() 20 | + (response.getReasonPhrase() == null ? "" : " " + response.getReasonPhrase()); 21 | try (InputStream content = response.getEntity().getContent()) { 22 | if (content != null) { 23 | StringWriter writer = new StringWriter(); 24 | Reader reader = new InputStreamReader(content); 25 | reader.transferTo(writer); 26 | String errorBody = writer.toString(); 27 | return new RuntimeException(message + ", body: " + errorBody); 28 | } 29 | } catch (IOException e) { 30 | LOG.debug("Error while reading error response", e); 31 | } 32 | return new RuntimeException(message); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/test/java/io/camunda/tasklist/spring/BeanNameTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import io.camunda.tasklist.CamundaTaskListClient; 6 | import io.camunda.tasklist.CamundaTasklistClientConfiguration; 7 | import io.camunda.tasklist.auth.Authentication; 8 | import java.util.stream.Stream; 9 | import org.junit.jupiter.api.DynamicTest; 10 | import org.junit.jupiter.api.TestFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.context.ApplicationContext; 14 | 15 | @SpringBootTest(properties = "tasklist.client.profile=simple") 16 | public class BeanNameTest { 17 | @Autowired ApplicationContext applicationContext; 18 | 19 | @TestFactory 20 | Stream shouldHaveBeanName() { 21 | return Stream.of( 22 | applicationContext.getBeanNamesForType(Authentication.class), 23 | applicationContext.getBeanNamesForType(CamundaTaskListClient.class), 24 | applicationContext.getBeanNamesForType(CamundaTasklistClientConfiguration.class)) 25 | .map(s -> DynamicTest.dynamicTest(s[0], () -> testBeanName(s))); 26 | } 27 | 28 | private void testBeanName(String[] beanNames) { 29 | assertThat(beanNames).hasSize(1); 30 | assertThat(beanNames[0]).containsIgnoringCase("tasklist"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/backport.yml: -------------------------------------------------------------------------------- 1 | name: Backport labeled merged pull requests 2 | on: 3 | pull_request: 4 | types: [closed] 5 | issue_comment: 6 | types: [created] 7 | permissions: 8 | contents: write # so it can comment 9 | pull-requests: write # so it can create pull requests 10 | jobs: 11 | backport: 12 | name: Create backport PRs 13 | runs-on: ubuntu-latest 14 | # Only run when pull request is merged 15 | # or when a comment starting with `/backport` is created by someone other than the 16 | # https://github.com/backport-action bot user (user id: 97796249). Note that if you use your 17 | # own PAT as `github_token`, that you should replace this id with yours. 18 | if: > 19 | ( 20 | github.event_name == 'pull_request' && 21 | github.event.pull_request.merged 22 | ) || ( 23 | github.event_name == 'issue_comment' && 24 | github.event.issue.pull_request && 25 | startsWith(github.event.comment.body, '/backport') 26 | ) 27 | steps: 28 | - uses: actions/checkout@v6 29 | - name: Create backport PRs 30 | uses: korthout/backport-action@v4 31 | with: 32 | pull_description: |- 33 | # Description 34 | Backport of #${pull_number} to `${target_branch}`. 35 | 36 | relates to ${issue_refs} 37 | 38 | add_author_as_assignee: true 39 | experimental: > 40 | { 41 | "conflict_resolution": "draft_commit_conflicts" 42 | } 43 | -------------------------------------------------------------------------------- /example/bootstrapping-test/src/main/java/io/camunda/tasklist/bootstrapping/Bootstrapper.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.bootstrapping; 2 | 3 | import static io.camunda.tasklist.CamundaTasklistClientConfiguration.*; 4 | 5 | import io.camunda.tasklist.CamundaTaskListClient; 6 | import io.camunda.tasklist.CamundaTasklistClientConfiguration; 7 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.ApiVersion; 8 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.DefaultProperties; 9 | import io.camunda.tasklist.auth.SimpleAuthentication; 10 | import io.camunda.tasklist.auth.SimpleCredential; 11 | import java.net.MalformedURLException; 12 | import java.net.URI; 13 | import java.time.Duration; 14 | 15 | public class Bootstrapper { 16 | public CamundaTaskListClient create() { 17 | try { 18 | return new CamundaTaskListClient( 19 | new CamundaTasklistClientConfiguration( 20 | ApiVersion.v1, 21 | new SimpleAuthentication( 22 | new SimpleCredential( 23 | "demo", 24 | "demo", 25 | URI.create("http://localhost:8082").toURL(), 26 | Duration.ofMinutes(10))), 27 | URI.create("http://localhost:8082").toURL(), 28 | null, 29 | new DefaultProperties(true, true, false, DEFAULT_TENANT_IDS))); 30 | } catch (MalformedURLException e) { 31 | throw new RuntimeException("Error while bootstrapping tasklist client", e); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/test/java/io/camunda/tasklist/spring/TasklistClientConfigurationPropertiesProfileOidcTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import static io.camunda.tasklist.spring.TasklistClientConfigurationProperties.Profile.*; 4 | import static org.assertj.core.api.Assertions.*; 5 | 6 | import java.net.MalformedURLException; 7 | import java.net.URI; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | @SpringBootTest( 13 | properties = { 14 | "tasklist.client.profile=oidc", 15 | "tasklist.client.client-id=def", 16 | "tasklist.client.client-secret=ghi" 17 | }) 18 | public class TasklistClientConfigurationPropertiesProfileOidcTest { 19 | @Autowired TasklistClientConfigurationProperties properties; 20 | 21 | @Test 22 | void shouldApplyProfiles() throws MalformedURLException { 23 | assertThat(properties.profile()).isEqualTo(oidc); 24 | assertThat(properties.clientId()).isEqualTo("def"); 25 | assertThat(properties.clientSecret()).isEqualTo("ghi"); 26 | assertThat(properties.baseUrl()).isEqualTo(URI.create("http://localhost:8082").toURL()); 27 | assertThat(properties.enabled()).isEqualTo(true); 28 | assertThat(properties.authUrl()) 29 | .isEqualTo( 30 | URI.create( 31 | "http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token") 32 | .toURL()); 33 | assertThat(properties.defaults()).isNotNull(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/test/java/io/camunda/tasklist/spring/TasklistClientConfigurationPropertiesProfileSaasTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import static io.camunda.tasklist.spring.TasklistClientConfigurationProperties.Profile.*; 4 | import static org.assertj.core.api.Assertions.*; 5 | 6 | import java.net.MalformedURLException; 7 | import java.net.URI; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | @SpringBootTest( 13 | properties = { 14 | "tasklist.client.profile=saas", 15 | "tasklist.client.cluster-id=abc", 16 | "tasklist.client.region=bru-2", 17 | "tasklist.client.client-id=def", 18 | "tasklist.client.client-secret=ghi" 19 | }) 20 | public class TasklistClientConfigurationPropertiesProfileSaasTest { 21 | @Autowired TasklistClientConfigurationProperties properties; 22 | 23 | @Test 24 | void shouldApplyProfiles() throws MalformedURLException { 25 | assertThat(properties.profile()).isEqualTo(saas); 26 | assertThat(properties.clientId()).isEqualTo("def"); 27 | assertThat(properties.clientSecret()).isEqualTo("ghi"); 28 | assertThat(properties.baseUrl()) 29 | .isEqualTo(URI.create("https://bru-2.tasklist.camunda.io/abc").toURL()); 30 | assertThat(properties.enabled()).isEqualTo(true); 31 | assertThat(properties.authUrl()) 32 | .isEqualTo(URI.create("https://login.cloud.camunda.io/oauth/token").toURL()); 33 | assertThat(properties.defaults()).isNotNull(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/TokenResponseHttpClientResponseHandler.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import org.apache.hc.core5.http.ClassicHttpResponse; 7 | import org.apache.hc.core5.http.HttpEntity; 8 | import org.apache.hc.core5.http.io.HttpClientResponseHandler; 9 | import org.apache.hc.core5.http.io.entity.EntityUtils; 10 | 11 | public class TokenResponseHttpClientResponseHandler 12 | implements HttpClientResponseHandler { 13 | private final ObjectMapper objectMapper; 14 | private final ErrorCodeHandler errorCodeHandler; 15 | 16 | public TokenResponseHttpClientResponseHandler( 17 | ObjectMapper objectMapper, ErrorCodeHandler errorCodeHandler) { 18 | this.objectMapper = objectMapper; 19 | this.errorCodeHandler = errorCodeHandler; 20 | } 21 | 22 | public TokenResponseHttpClientResponseHandler(ObjectMapper objectMapper) { 23 | this(objectMapper, new DefaultErrorCodeHandler()); 24 | } 25 | 26 | @Override 27 | public TokenResponse handleResponse(ClassicHttpResponse response) throws IOException { 28 | TokenResponse resp; 29 | if (200 <= response.getCode() && response.getCode() <= 299) { 30 | HttpEntity entity = response.getEntity(); 31 | String tmp = new String(entity.getContent().readAllBytes(), StandardCharsets.UTF_8); 32 | resp = objectMapper.readValue(tmp, TokenResponse.class); 33 | EntityUtils.consume(entity); 34 | return resp; 35 | } else { 36 | throw errorCodeHandler.handleError(response); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/java/io/camunda/tasklist/spring/TasklistPropertiesPostProcessor.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import io.camunda.tasklist.spring.TasklistClientConfigurationProperties.Profile; 4 | import java.io.IOException; 5 | import java.util.List; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.env.EnvironmentPostProcessor; 8 | import org.springframework.boot.env.YamlPropertySourceLoader; 9 | import org.springframework.core.env.ConfigurableEnvironment; 10 | import org.springframework.core.env.PropertySource; 11 | import org.springframework.core.io.ClassPathResource; 12 | 13 | public class TasklistPropertiesPostProcessor implements EnvironmentPostProcessor { 14 | private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); 15 | 16 | @Override 17 | public void postProcessEnvironment( 18 | ConfigurableEnvironment environment, SpringApplication application) { 19 | try { 20 | Profile profile = environment.getProperty("tasklist.client.profile", Profile.class); 21 | if (profile == null) { 22 | return; 23 | } 24 | loadProperties("tasklist-profiles/" + determinePropertiesFile(profile), environment); 25 | } catch (Exception e) { 26 | throw new IllegalStateException("Error while post processing camunda properties", e); 27 | } 28 | } 29 | 30 | private void loadProperties(String propertiesFile, ConfigurableEnvironment environment) 31 | throws IOException { 32 | ClassPathResource resource = new ClassPathResource(propertiesFile); 33 | List> props = loader.load(propertiesFile, resource); 34 | for (PropertySource prop : props) { 35 | environment.getPropertySources().addLast(prop); // lowest priority 36 | } 37 | } 38 | 39 | private String determinePropertiesFile(Profile clientMode) { 40 | switch (clientMode) { 41 | case oidc -> { 42 | return "oidc.yaml"; 43 | } 44 | case saas -> { 45 | return "saas.yaml"; 46 | } 47 | case simple -> { 48 | return "simple.yaml"; 49 | } 50 | } 51 | throw new IllegalStateException("Unknown client mode " + clientMode); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /extension/client-java/src/test/java/io/camunda/tasklist/JsonMapperTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import io.camunda.client.api.JsonMapper; 6 | import io.camunda.client.impl.CamundaObjectMapper; 7 | import java.util.List; 8 | import java.util.Map; 9 | import org.junit.jupiter.api.Test; 10 | 11 | public class JsonMapperTest { 12 | @Test 13 | void shouldDeserializeToObjectNumber() { 14 | String json = "123"; 15 | JsonMapper jsonMapper = new CamundaObjectMapper(); 16 | Object fromJson = jsonMapper.fromJson(json, Object.class); 17 | assertThat(fromJson).isInstanceOf(Number.class); 18 | } 19 | 20 | @Test 21 | void shouldDeserializeToObjectString() { 22 | String json = "\"abc\""; 23 | JsonMapper jsonMapper = new CamundaObjectMapper(); 24 | Object fromJson = jsonMapper.fromJson(json, Object.class); 25 | assertThat(fromJson).isInstanceOf(String.class); 26 | } 27 | 28 | @Test 29 | void shouldDeserializeToObjectNull() { 30 | String json = "null"; 31 | JsonMapper jsonMapper = new CamundaObjectMapper(); 32 | Object fromJson = jsonMapper.fromJson(json, Object.class); 33 | assertThat(fromJson).isNull(); 34 | } 35 | 36 | @Test 37 | void shouldDeserializeToObjectArray() { 38 | String json = "[\"abc\"]"; 39 | JsonMapper jsonMapper = new CamundaObjectMapper(); 40 | Object fromJson = jsonMapper.fromJson(json, Object.class); 41 | assertThat(fromJson).isInstanceOf(List.class); 42 | List fromJsonList = (List) fromJson; 43 | assertThat(fromJsonList).containsExactly("abc"); 44 | } 45 | 46 | @Test 47 | void shouldDeserializeToObjectObject() { 48 | String json = "{\"foo\":\"bar\"}"; 49 | JsonMapper jsonMapper = new CamundaObjectMapper(); 50 | Object fromJson = jsonMapper.fromJson(json, Object.class); 51 | assertThat(fromJson).isInstanceOf(Map.class); 52 | Map fromJsonMap = (Map) fromJson; 53 | assertThat(fromJsonMap).containsExactly(entry("foo", "bar")); 54 | } 55 | 56 | @Test 57 | void shouldDeserializeToObjectBoolean() { 58 | String json = "false"; 59 | JsonMapper jsonMapper = new CamundaObjectMapper(); 60 | Object fromJson = jsonMapper.fromJson(json, Object.class); 61 | assertThat(fromJson).isInstanceOf(Boolean.class); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/mvn-release.yml: -------------------------------------------------------------------------------- 1 | # If this workflow is triggered by a push to either main or release branches then it 2 | # deploys a SNAPSHOT 3 | # If this workflow is triggered by publishing a Release, it 4 | # deploys a RELEASE with the selected version 5 | # updates the project version by incrementing the patch version 6 | # commits the version update change to the repository's branch that triggered the workflow. 7 | name: Deploy artifacts with Maven 8 | 9 | on: 10 | push: 11 | branches: 12 | - main 13 | - release/** 14 | release: 15 | types: [ published ] 16 | jobs: 17 | publish: 18 | runs-on: ubuntu-latest 19 | steps: 20 | 21 | - name: Check out code 22 | uses: actions/checkout@v6 23 | 24 | - name: Set up Java environment 25 | uses: actions/setup-java@v5 26 | with: 27 | java-version: '21' 28 | distribution: 'adopt' 29 | cache: maven 30 | gpg-private-key: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_SEC }} 31 | gpg-passphrase: MAVEN_CENTRAL_GPG_PASSPHRASE 32 | 33 | - name: Deploy SNAPSHOT / Release 34 | uses: camunda-community-hub/community-action-maven-release@v2 35 | with: 36 | release-version: ${{ github.event.release.tag_name }} 37 | nexus-usr: ${{ secrets.NEXUS_USR }} 38 | nexus-psw: ${{ secrets.NEXUS_PSW }} 39 | sonatype-central-portal-usr: ${{ secrets.COMMUNITY_HUB_MAVEN_CENTRAL_CP_USR }} 40 | sonatype-central-portal-psw: ${{ secrets.COMMUNITY_HUB_MAVEN_CENTRAL_CP_PSW }} 41 | maven-gpg-passphrase: ${{ secrets.MAVEN_CENTRAL_GPG_SIGNING_KEY_PASSPHRASE }} 42 | maven-auto-release-after-close: true 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | branch: ${{ github.event.release.target_commitish || github.ref_name }} 45 | id: release 46 | 47 | - if: github.event.release 48 | name: Attach artifacts to GitHub Release (Release only) 49 | uses: actions/upload-release-asset@v1 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | with: 53 | upload_url: ${{ github.event.release.upload_url }} 54 | asset_path: ${{ steps.release.outputs.artifacts_archive_path }} 55 | asset_name: ${{ steps.release.outputs.artifacts_archive_path }} 56 | asset_content_type: application/zip 57 | -------------------------------------------------------------------------------- /extension/client-java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-parent 7 | 8.8.0-SNAPSHOT 8 | 9 | camunda-tasklist-client-java 10 | Camunda Tasklist Client Java 11 | 12 | 13 | org.slf4j 14 | slf4j-api 15 | provided 16 | 17 | 18 | io.camunda 19 | camunda-tasklist-client-generated 20 | 21 | 22 | io.camunda 23 | camunda-client-java 24 | 25 | 26 | com.fasterxml.jackson.datatype 27 | jackson-datatype-jsr310 28 | 29 | 30 | com.fasterxml.jackson.core 31 | jackson-databind 32 | 33 | 34 | com.fasterxml.jackson.core 35 | jackson-core 36 | 37 | 38 | com.fasterxml.jackson.core 39 | jackson-annotations 40 | 41 | 42 | org.apache.httpcomponents.core5 43 | httpcore5 44 | 45 | 46 | org.apache.httpcomponents.client5 47 | httpclient5 48 | 49 | 50 | org.junit.jupiter 51 | junit-jupiter-api 52 | test 53 | 54 | 55 | org.wiremock 56 | wiremock-standalone 57 | test 58 | 59 | 60 | com.auth0 61 | java-jwt 62 | test 63 | 64 | 65 | org.assertj 66 | assertj-core 67 | test 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/Pagination.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import io.camunda.tasklist.generated.model.TaskOrderBy; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class Pagination { 8 | 9 | private Integer pageSize; 10 | 11 | private List search; 12 | 13 | private SearchType searchType; 14 | 15 | private List sort; 16 | 17 | public Integer getPageSize() { 18 | return pageSize; 19 | } 20 | 21 | public Pagination setPageSize(Integer pageSize) { 22 | this.pageSize = pageSize; 23 | return this; 24 | } 25 | 26 | public List getSearch() { 27 | return search; 28 | } 29 | 30 | public Pagination setSearch(List search) { 31 | this.search = search; 32 | return this; 33 | } 34 | 35 | public SearchType getSearchType() { 36 | return searchType; 37 | } 38 | 39 | public Pagination setSearchType(SearchType searchType) { 40 | this.searchType = searchType; 41 | return this; 42 | } 43 | 44 | public List getSort() { 45 | return sort; 46 | } 47 | 48 | public Pagination setSort(List sort) { 49 | this.sort = sort; 50 | return this; 51 | } 52 | 53 | public static class Builder { 54 | 55 | private Integer pageSize; 56 | 57 | private List search; 58 | 59 | private SearchType searchType; 60 | 61 | private List sort; 62 | 63 | public Builder pageSize(Integer pageSize) { 64 | this.pageSize = pageSize; 65 | return this; 66 | } 67 | 68 | public Builder after(List search) { 69 | this.search = search; 70 | this.searchType = SearchType.AFTER; 71 | return this; 72 | } 73 | 74 | public Builder before(List search) { 75 | this.search = search; 76 | this.searchType = SearchType.BEFORE; 77 | return this; 78 | } 79 | 80 | public Builder beforeOrEqual(List search) { 81 | this.search = search; 82 | this.searchType = SearchType.BEFORE_OR_EQUAL; 83 | return this; 84 | } 85 | 86 | public Builder afterOrEqual(List search) { 87 | this.search = search; 88 | this.searchType = SearchType.AFTER_OR_EQUAL; 89 | return this; 90 | } 91 | 92 | public Builder sortBy(TaskOrderBy.FieldEnum field, TaskOrderBy.OrderEnum order) { 93 | if (sort == null) { 94 | sort = new ArrayList<>(); 95 | } 96 | sort.add(new TaskOrderBy().field(field).order(order)); 97 | return this; 98 | } 99 | 100 | public Pagination build() { 101 | return new Pagination() 102 | .setPageSize(pageSize) 103 | .setSearch(search) 104 | .setSearchType(searchType) 105 | .setSort(sort); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/JwtAuthentication.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import java.net.URISyntaxException; 5 | import java.time.LocalDateTime; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import org.apache.hc.client5.http.classic.methods.HttpPost; 10 | import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; 11 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; 12 | import org.apache.hc.client5.http.impl.classic.HttpClients; 13 | import org.apache.hc.core5.http.NameValuePair; 14 | import org.apache.hc.core5.http.message.BasicNameValuePair; 15 | import org.apache.hc.core5.util.Asserts; 16 | 17 | public class JwtAuthentication implements Authentication { 18 | private final JwtCredential jwtCredential; 19 | private final TokenResponseHttpClientResponseHandler responseHandler; 20 | private String token; 21 | private LocalDateTime timeout; 22 | 23 | public JwtAuthentication( 24 | JwtCredential jwtCredential, TokenResponseHttpClientResponseHandler responseHandler) { 25 | this.jwtCredential = jwtCredential; 26 | this.responseHandler = responseHandler; 27 | } 28 | 29 | public JwtAuthentication(JwtCredential jwtCredential) { 30 | this(jwtCredential, new TokenResponseHttpClientResponseHandler(new ObjectMapper())); 31 | } 32 | 33 | @Override 34 | public Map getTokenHeader() { 35 | if (token == null || timeout == null || timeout.isBefore(LocalDateTime.now())) { 36 | TokenResponse response = retrieveToken(); 37 | token = response.getAccessToken(); 38 | timeout = LocalDateTime.now().plusSeconds(response.getExpiresIn()).minusSeconds(30); 39 | } 40 | return Map.of("Authorization", "Bearer " + token); 41 | } 42 | 43 | @Override 44 | public void resetToken() { 45 | this.token = null; 46 | this.timeout = null; 47 | } 48 | 49 | private TokenResponse retrieveToken() { 50 | try (CloseableHttpClient client = HttpClients.createSystem()) { 51 | HttpPost request = buildRequest(); 52 | TokenResponse tokenResponse = client.execute(request, responseHandler); 53 | Asserts.notNull(tokenResponse.getAccessToken(), "access_token is null"); 54 | Asserts.notNull(tokenResponse.getExpiresIn(), "expires_in is null"); 55 | return tokenResponse; 56 | } catch (Exception e) { 57 | throw new RuntimeException("Failed to retrieve token for Tasklist authentication", e); 58 | } 59 | } 60 | 61 | private HttpPost buildRequest() throws URISyntaxException { 62 | HttpPost httpPost = new HttpPost(jwtCredential.authUrl().toURI()); 63 | httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); 64 | List formParams = new ArrayList<>(); 65 | formParams.add(new BasicNameValuePair("grant_type", "client_credentials")); 66 | formParams.add(new BasicNameValuePair("client_id", jwtCredential.clientId())); 67 | formParams.add(new BasicNameValuePair("client_secret", jwtCredential.clientSecret())); 68 | formParams.add(new BasicNameValuePair("audience", jwtCredential.audience())); 69 | if (jwtCredential.scope() != null && !jwtCredential.scope().isEmpty()) { 70 | formParams.add(new BasicNameValuePair("scope", jwtCredential.scope())); 71 | } 72 | httpPost.setEntity(new UrlEncodedFormEntity(formParams)); 73 | return httpPost; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-parent 7 | 8.8.0-SNAPSHOT 8 | 9 | spring-boot-starter-camunda-tasklist 10 | Spring Boot Starter Camunda Tasklist Client 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-configuration-processor 19 | true 20 | 21 | 22 | io.camunda 23 | camunda-client-java 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-autoconfigure 28 | 29 | 30 | org.springframework 31 | spring-core 32 | 33 | 34 | org.springframework 35 | spring-beans 36 | 37 | 38 | org.springframework 39 | spring-context 40 | 41 | 42 | org.springframework.boot 43 | spring-boot 44 | 45 | 46 | com.fasterxml.jackson.core 47 | jackson-databind 48 | 49 | 50 | io.camunda 51 | camunda-tasklist-client-java 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-test 61 | test 62 | 63 | 64 | org.junit.jupiter 65 | junit-jupiter-api 66 | test 67 | 68 | 69 | org.assertj 70 | assertj-core 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-dependency-plugin 80 | 81 | 82 | org.springframework.boot:spring-boot-starter 83 | org.springframework.boot:spring-boot-starter-test 84 | org.springframework.boot:spring-boot-configuration-processor 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. 4 | 5 | Please note that this project is released with a [Contributor Code of Conduct](https://github.com/camunda-community-hub/community/blob/main/CODE_OF_CONDUCT.MD). By participating in this project you agree to abide by its terms. 6 | 7 | ## Issues and PRs 8 | 9 | If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them. 10 | 11 | We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR. 12 | 13 | ## Submitting a pull request 14 | 15 | Please feel free to fork this repository and open a pull request to update this documentation. 16 | 17 | Work in Progress pull requests are also welcome to get feedback early on, or if there is something blocking you that you would like an additional person to take a look at. 18 | 19 | ## Non-Code Camunda community extension contributions 20 | 21 | If you are interested in contributing to any Camunda Community Hub extension, there are a variety of ways to get involved without having to have coding experience: These include issue triage, writing documentation, and much, much more. 22 | 23 | ### Event-Based community roles 24 | 25 | * Host or organize a Camunda Community Meetup 26 | * Present your work with Camunda at Meetups 27 | * Start a new Camunda event near you 28 | 29 | ### Community education and onboarding 30 | 31 | * Help new contributors get started working with the Hub, finding a repository to contribute to, forking a repo, and making their first pull request 32 | * Getting people involved in writing documentation and issue triage 33 | * Spreading the word about the Camunda Community Hub to the broader open source community 34 | * Answering questions on Slack, Discourse, GitHub Project boards, and the Camunda Cloud community forums 35 | * Create community content 36 | * Write a blog post about your experience with Camunda, how you built a Community Hub Extension, etc. 37 | * Be a guest on our Camunda Nation podcast 38 | * Write walkthroughs, how-to guides, and more 39 | 40 | ### Issue triage 41 | 42 | * Labelling issues in Camunda Community Extension repositories 43 | * Helping to triage issues by following up where needed, closing stale issues, and more 44 | * For more information on how to get started with issue triage and labelling, including what issue labels and pull request automation we use in the Camunda Community Hub, please read the [documentation](https://github.com/camunda-community-hub/community/blob/main/issue-triage.md) 45 | 46 | ### Post-Code specific contributions 47 | 48 | These are roles that are not code-based, but require some knowledge in that you will need to have an understanding of the Camunda Community Extension ecosystem, the Camunda codebase, or general programming knowledge. 49 | 50 | ### Help project maintainers write release notes for their extension 51 | 52 | * GitHub management (tags, repositories, etc.) 53 | * Mentoring new contributors and ensuring they have the tools they need to succeed when contributing to a Camunda Community Hub open source project 54 | * Writing new or editing existing documentation 55 | * Updating communication guidelines and expectations 56 | 57 | ## Resources 58 | 59 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 60 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 61 | - [GitHub Help](https://help.github.com) 62 | - [CODE_OF_CONDUCT](https://github.com/camunda-community-hub/community/blob/main/CODE_OF_CONDUCT.MD) 63 | -------------------------------------------------------------------------------- /extension/spring-boot-starter-camunda-tasklist/src/main/java/io/camunda/tasklist/spring/TasklistClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.spring; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.camunda.client.CamundaClient; 5 | import io.camunda.tasklist.CamundaTaskListClient; 6 | import io.camunda.tasklist.CamundaTasklistClientConfiguration; 7 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.ApiVersion; 8 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.DefaultProperties; 9 | import io.camunda.tasklist.auth.Authentication; 10 | import io.camunda.tasklist.auth.JwtAuthentication; 11 | import io.camunda.tasklist.auth.JwtCredential; 12 | import io.camunda.tasklist.auth.SimpleAuthentication; 13 | import io.camunda.tasklist.auth.SimpleCredential; 14 | import io.camunda.tasklist.auth.TokenResponseHttpClientResponseHandler; 15 | import io.camunda.tasklist.spring.TasklistClientConfigurationProperties.Profile; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 19 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Import; 22 | 23 | @EnableConfigurationProperties({TasklistClientConfigurationProperties.class}) 24 | @ConditionalOnProperty(value = "tasklist.client.enabled", matchIfMissing = true) 25 | @Import(ObjectMapperConfiguration.class) 26 | public class TasklistClientConfiguration { 27 | private final TasklistClientConfigurationProperties properties; 28 | private final ObjectMapper objectMapper; 29 | 30 | @Autowired 31 | public TasklistClientConfiguration( 32 | TasklistClientConfigurationProperties properties, ObjectMapper objectMapper) { 33 | this.properties = properties; 34 | this.objectMapper = objectMapper; 35 | } 36 | 37 | @Bean 38 | @ConditionalOnMissingBean 39 | public CamundaTaskListClient camundaTasklistClient( 40 | CamundaTasklistClientConfiguration configuration) { 41 | return new CamundaTaskListClient(configuration); 42 | } 43 | 44 | @Bean 45 | @ConditionalOnMissingBean 46 | public CamundaTasklistClientConfiguration tasklistClientConfiguration( 47 | Authentication authentication, @Autowired(required = false) CamundaClient camundaClient) { 48 | return new CamundaTasklistClientConfiguration( 49 | properties.profile() == Profile.v2 ? ApiVersion.v2 : ApiVersion.v1, 50 | authentication, 51 | properties.baseUrl(), 52 | camundaClient, 53 | new DefaultProperties( 54 | properties.defaults().returnVariables(), 55 | properties.defaults().loadTruncatedVariables(), 56 | properties.defaults().useZeebeUserTasks(), 57 | properties.defaults().tenantIds())); 58 | } 59 | 60 | @Bean 61 | @ConditionalOnMissingBean 62 | public Authentication tasklistAuthentication() { 63 | if (properties.profile() == null) { 64 | throw new IllegalStateException("'tasklist.client.profile' is required"); 65 | } 66 | switch (properties.profile()) { 67 | case simple -> { 68 | return new SimpleAuthentication( 69 | new SimpleCredential( 70 | properties.username(), 71 | properties.password(), 72 | properties.baseUrl(), 73 | properties.sessionTimeout())); 74 | } 75 | case oidc, saas -> { 76 | return new JwtAuthentication( 77 | new JwtCredential( 78 | properties.clientId(), 79 | properties.clientSecret(), 80 | properties.audience(), 81 | properties.authUrl(), 82 | properties.scope()), 83 | new TokenResponseHttpClientResponseHandler(objectMapper)); 84 | } 85 | default -> throw new IllegalStateException("Unsupported profile: " + properties.profile()); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /extension/generated/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.camunda 6 | tasklist-client-parent 7 | 8.8.0-SNAPSHOT 8 | 9 | camunda-tasklist-client-generated 10 | Camunda Tasklist Client Generated 11 | 12 | 13 | 14 | org.apache.httpcomponents.client5 15 | httpclient5 16 | 17 | 18 | org.apache.httpcomponents.core5 19 | httpcore5 20 | provided 21 | 22 | 23 | 24 | com.fasterxml.jackson.core 25 | jackson-core 26 | 27 | 28 | com.fasterxml.jackson.core 29 | jackson-annotations 30 | 31 | 32 | com.fasterxml.jackson.core 33 | jackson-databind 34 | 35 | 36 | com.fasterxml.jackson.datatype 37 | jackson-datatype-jsr310 38 | 39 | 40 | org.openapitools 41 | jackson-databind-nullable 42 | 43 | 44 | jakarta.annotation 45 | jakarta.annotation-api 46 | provided 47 | 48 | 49 | 50 | org.junit.jupiter 51 | junit-jupiter-api 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-dependency-plugin 61 | 62 | 63 | org.junit.jupiter:junit-jupiter-api 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.openapitools 72 | openapi-generator-maven-plugin 73 | 74 | 75 | 76 | generate 77 | 78 | 79 | ${project.basedir}/tasklist-8.7.0.json 80 | java 81 | true 82 | 83 | true 84 | io.camunda.tasklist.generated 85 | io.camunda.tasklist.generated.api 86 | io.camunda.tasklist.generated.invoker 87 | io.camunda.tasklist.generated.model 88 | apache-httpclient 89 | true 90 | java8 91 | ${project.build.directory}/generated-test-sources/openapi 92 | true 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/TasklistClient.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | public interface TasklistClient { 8 | // Form 9 | @Deprecated(since = "8.8") 10 | Form getForm(String formId, String processDefinitionKey, Long version); 11 | 12 | @Deprecated(since = "8.8") 13 | default Form getForm(String formId, String processDefinitionKey) { 14 | return getForm(formId, processDefinitionKey, null); 15 | } 16 | 17 | FormV2 getFormForTask(String userTaskKey); 18 | 19 | FormV2 getStartFormForProcess(String processDefinitionKey); 20 | 21 | // Task 22 | 23 | void saveDraftVariables(String taskId, List variables); 24 | 25 | List searchTaskVariables(String taskId, VariableSearch variableSearch); 26 | 27 | List searchTasks(TaskSearch taskSearch); 28 | 29 | Optional unassignTask(String taskId); 30 | 31 | Optional completeTask(String taskId, List variables); 32 | 33 | Optional assignTask(String taskId, TaskAssignment taskAssignment); 34 | 35 | Task getTask(String taskId); 36 | 37 | // Variables 38 | 39 | Variable getVariable(String variableId); 40 | 41 | enum Implementation { 42 | JOB_WORKER, 43 | ZEEBE_USER_TASK 44 | } 45 | 46 | enum TaskState { 47 | CREATED, 48 | COMPLETED, 49 | CANCELED, 50 | FAILED, 51 | ASSIGNING, 52 | UPDATING, 53 | COMPLETING, 54 | CANCELING, 55 | UNKNOWN_ENUM_VALUE 56 | } 57 | 58 | // types 59 | 60 | record Form( 61 | String id, 62 | String processDefinitionKey, 63 | String title, 64 | String schema, 65 | Long version, 66 | String tenantId, 67 | Boolean isDeleted) {} 68 | 69 | record FormV2(String tenantId, String formId, Object schema, Long version, String formKey) {} 70 | 71 | record RequestVariable(String name, String value) {} 72 | 73 | record VariableFromSearch( 74 | String id, 75 | String name, 76 | String value, 77 | Boolean isValueTruncated, 78 | String previewValue, 79 | VariableDraftFromSearch draft) { 80 | public record VariableDraftFromSearch( 81 | String value, Boolean isValueTruncated, String previewValue) {} 82 | } 83 | 84 | record Variable(String id, String name, String value, VariableDraft draft, String tenantId) { 85 | public record VariableDraft(String value) {} 86 | } 87 | 88 | record VariableSearch(List variableNames, List includeVariables) {} 89 | 90 | record IncludeVariable(String name, Boolean alwaysReturnFullValue) {} 91 | 92 | record TaskFromSearch( 93 | String id, 94 | String name, 95 | String taskDefinitionId, 96 | String processName, 97 | OffsetDateTime creationDate, 98 | OffsetDateTime completionDate, 99 | String assignee, 100 | TaskState taskState, 101 | Boolean isFirst, 102 | String formKey, 103 | String formId, 104 | Boolean isFormEmbedded, 105 | String processDefinitionKey, 106 | String processInstanceKey, 107 | String tenantId, 108 | OffsetDateTime dueDate, 109 | OffsetDateTime followUpDate, 110 | List candidateGroups, 111 | List candidateUsers, 112 | List variables, 113 | Implementation implementation, 114 | Integer priority) {} 115 | 116 | record Task( 117 | String id, 118 | String name, 119 | String taskDefinitionId, 120 | String processName, 121 | OffsetDateTime creationDate, 122 | OffsetDateTime completionDate, 123 | String assignee, 124 | TaskState taskState, 125 | String formKey, 126 | String formId, 127 | Boolean isFormEmbedded, 128 | String processDefinitionKey, 129 | String processInstanceKey, 130 | String tenantId, 131 | OffsetDateTime dueDate, 132 | OffsetDateTime followUpDate, 133 | List candidateGroups, 134 | List candidateUsers, 135 | Implementation implementation, 136 | Integer priority) {} 137 | 138 | record TaskSearch( 139 | TaskState state, 140 | Boolean assigned, 141 | String assignee, 142 | List assignees, 143 | String taskDefinitionId, 144 | String candidateGroup, 145 | List candidateGroups, 146 | String candidateUser, 147 | List candidateUsers, 148 | String processDefinitionKey, 149 | String processInstanceKey, 150 | Integer pageSize, 151 | DateRange followUpDate, 152 | DateRange dueDate, 153 | List taskVariables, 154 | List tenantIds, 155 | List sort, 156 | List searchAfter, 157 | List searchAfterOrEqual, 158 | List searchBefore, 159 | List searchBeforeOrEqual, 160 | String after, 161 | String before, 162 | List includeVariables, 163 | Implementation implementation, 164 | Priority priority) { 165 | 166 | public record DateRange(OffsetDateTime from, OffsetDateTime to) {} 167 | 168 | public record TaskVariable(String name, String value, Operator operator) { 169 | public enum Operator { 170 | eq 171 | } 172 | } 173 | 174 | public record Sort(Field field, Order order) { 175 | public enum Field { 176 | completionTime, 177 | creationTime, 178 | followUpDate, 179 | dueDate, 180 | priority, 181 | name 182 | } 183 | 184 | public enum Order { 185 | ASC, 186 | DESC 187 | } 188 | } 189 | 190 | public record Priority(Integer eq, Integer gte, Integer gt, Integer lt, Integer lte) {} 191 | } 192 | 193 | record TaskAssignment(String assignee, Boolean allowOverrideAssignment) {} 194 | } 195 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/auth/SimpleAuthentication.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.auth; 2 | 3 | import java.lang.invoke.MethodHandles; 4 | import java.time.LocalDateTime; 5 | import java.util.*; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.Stream; 8 | import org.apache.hc.client5.http.classic.methods.HttpPost; 9 | import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; 10 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; 11 | import org.apache.hc.client5.http.impl.classic.HttpClients; 12 | import org.apache.hc.core5.http.ClassicHttpResponse; 13 | import org.apache.hc.core5.http.Header; 14 | import org.apache.hc.core5.http.NameValuePair; 15 | import org.apache.hc.core5.http.ProtocolException; 16 | import org.apache.hc.core5.http.message.BasicNameValuePair; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | public class SimpleAuthentication implements Authentication { 21 | private static final Set CSRF_HEADER_CANDIDATES = 22 | Set.of("X-CSRF-TOKEN", "TASKLIST-X-CSRF-TOKEN", "OPERATE-X-CSRF-TOKEN"); 23 | private static final Set SESSION_COOKIE_CANDIDATES = 24 | Set.of("TASKLIST-SESSION", "OPERATE-SESSION"); 25 | private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); 26 | 27 | private final SimpleCredential simpleCredential; 28 | private SimpleAuthToken token; 29 | 30 | public SimpleAuthentication(SimpleCredential simpleCredential) { 31 | this.simpleCredential = simpleCredential; 32 | } 33 | 34 | private SimpleAuthToken retrieveToken() { 35 | try (CloseableHttpClient client = HttpClients.createSystem()) { 36 | HttpPost request = buildRequest(simpleCredential); 37 | SimpleAuthToken simpleAuthToken = 38 | client.execute( 39 | request, 40 | response -> { 41 | if (response.getCode() > 299) { 42 | throw new RuntimeException( 43 | "Unable to login, response code " + response.getCode()); 44 | } 45 | String csrfTokenCandidate = null; 46 | String csrfTokenHeaderName = null; 47 | Header csrfTokenHeader = findCsrfTokenHeader(response); 48 | if (csrfTokenHeader != null) { 49 | csrfTokenCandidate = csrfTokenHeader.getValue(); 50 | csrfTokenHeaderName = csrfTokenHeader.getName(); 51 | } 52 | Header[] cookieHeaders = response.getHeaders("Set-Cookie"); 53 | String sessionCookie = null; 54 | String csrfCookie = null; 55 | for (Header cookieHeader : cookieHeaders) { 56 | for (String sessionCookieName : SESSION_COOKIE_CANDIDATES) { 57 | if (cookieHeader.getValue().startsWith(sessionCookieName)) { 58 | sessionCookie = cookieHeader.getValue(); 59 | } 60 | } 61 | for (String candidate : CSRF_HEADER_CANDIDATES) { 62 | if (cookieHeader.getValue().startsWith(candidate)) { 63 | csrfCookie = cookieHeader.getValue(); 64 | } 65 | } 66 | } 67 | return new SimpleAuthToken( 68 | sessionCookie, 69 | csrfCookie, 70 | csrfTokenCandidate, 71 | csrfTokenHeaderName, 72 | LocalDateTime.now().plus(simpleCredential.sessionTimeout())); 73 | }); 74 | if (simpleAuthToken.sessionCookie() == null) { 75 | throw new RuntimeException( 76 | "Unable to authenticate due to missing Set-Cookie TASKLIST-SESSION or OPERATE-SESSION"); 77 | } 78 | if (simpleAuthToken.csrfToken() == null) { 79 | LOG.debug("No CSRF token found"); 80 | } 81 | if (simpleAuthToken.csrfCookie() == null) { 82 | LOG.debug("No CSRF cookie found"); 83 | } 84 | return simpleAuthToken; 85 | } catch (Exception e) { 86 | throw new RuntimeException("Unable to authenticate", e); 87 | } 88 | } 89 | 90 | private Header findCsrfTokenHeader(ClassicHttpResponse response) throws ProtocolException { 91 | if (token != null) { 92 | return response.getHeader(token.csrfTokenHeaderName()); 93 | } 94 | for (String candidate : CSRF_HEADER_CANDIDATES) { 95 | if (response.containsHeader(candidate)) { 96 | return response.getHeader(candidate); 97 | } 98 | } 99 | return null; 100 | } 101 | 102 | private HttpPost buildRequest(SimpleCredential simpleCredential) { 103 | HttpPost httpPost = new HttpPost(simpleCredential.baseUrl().toString() + "/api/login"); 104 | List params = new ArrayList<>(); 105 | params.add(new BasicNameValuePair("username", simpleCredential.username())); 106 | params.add(new BasicNameValuePair("password", simpleCredential.password())); 107 | httpPost.setEntity(new UrlEncodedFormEntity(params)); 108 | return httpPost; 109 | } 110 | 111 | @Override 112 | public Map getTokenHeader() { 113 | if (token == null || token.sessionTimeout().isBefore(LocalDateTime.now())) { 114 | token = retrieveToken(); 115 | } 116 | Map headers = new HashMap<>(); 117 | if (token.csrfToken() != null) { 118 | headers.put(token.csrfTokenHeaderName(), token.csrfToken()); 119 | } 120 | headers.put( 121 | "Cookie", 122 | Stream.of(token.sessionCookie(), token.csrfCookie()) 123 | .filter(Objects::nonNull) 124 | .collect(Collectors.joining(";"))); 125 | return headers; 126 | } 127 | 128 | @Override 129 | public void resetToken() { 130 | token = null; 131 | } 132 | 133 | private record SimpleAuthToken( 134 | String sessionCookie, 135 | String csrfCookie, 136 | String csrfToken, 137 | String csrfTokenHeaderName, 138 | LocalDateTime sessionTimeout) {} 139 | } 140 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/Task.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.List; 5 | 6 | public class Task { 7 | private String id; 8 | 9 | private String name; 10 | 11 | private String processName; 12 | 13 | private String processDefinitionKey; 14 | 15 | private String processInstanceKey; 16 | 17 | private String assignee; 18 | 19 | private String creationDate; 20 | 21 | private String completionDate; 22 | 23 | private TaskState taskState; 24 | 25 | private List candidateUsers; 26 | 27 | private List candidateGroups; 28 | 29 | private OffsetDateTime followUpDate; 30 | 31 | private OffsetDateTime dueDate; 32 | 33 | private String formKey; 34 | 35 | private String formId; 36 | 37 | private Long formVersion; 38 | 39 | private Boolean isFormEmbedded; 40 | 41 | private String taskDefinitionId; 42 | 43 | private List sortValues; 44 | 45 | private Boolean isFirst; 46 | 47 | private String tenantId; 48 | 49 | private Integer priority; 50 | 51 | private List variables; 52 | private Implementation implementation; 53 | 54 | public Boolean getFormEmbedded() { 55 | return isFormEmbedded; 56 | } 57 | 58 | public void setFormEmbedded(Boolean formEmbedded) { 59 | isFormEmbedded = formEmbedded; 60 | } 61 | 62 | public Boolean getFirst() { 63 | return isFirst; 64 | } 65 | 66 | public void setFirst(Boolean first) { 67 | isFirst = first; 68 | } 69 | 70 | public Implementation getImplementation() { 71 | return implementation; 72 | } 73 | 74 | public void setImplementation(Implementation implementation) { 75 | this.implementation = implementation; 76 | } 77 | 78 | public String getId() { 79 | return id; 80 | } 81 | 82 | public void setId(String id) { 83 | this.id = id; 84 | } 85 | 86 | public String getName() { 87 | return name; 88 | } 89 | 90 | public void setName(String name) { 91 | this.name = name; 92 | } 93 | 94 | public String getProcessName() { 95 | return processName; 96 | } 97 | 98 | public void setProcessName(String processName) { 99 | this.processName = processName; 100 | } 101 | 102 | public String getAssignee() { 103 | return assignee; 104 | } 105 | 106 | public void setAssignee(String assignee) { 107 | this.assignee = assignee; 108 | } 109 | 110 | public String getCreationDate() { 111 | return creationDate; 112 | } 113 | 114 | public void setCreationDate(String creationDate) { 115 | this.creationDate = creationDate; 116 | } 117 | 118 | public String getCompletionDate() { 119 | return completionDate; 120 | } 121 | 122 | public void setCompletionDate(String completionDate) { 123 | this.completionDate = completionDate; 124 | } 125 | 126 | public TaskState getTaskState() { 127 | return taskState; 128 | } 129 | 130 | public void setTaskState(TaskState taskState) { 131 | this.taskState = taskState; 132 | } 133 | 134 | public List getSortValues() { 135 | return sortValues; 136 | } 137 | 138 | public void setSortValues(List sortValues) { 139 | this.sortValues = sortValues; 140 | } 141 | 142 | public Boolean getIsFirst() { 143 | return isFirst; 144 | } 145 | 146 | public void setIsFirst(Boolean isFirst) { 147 | this.isFirst = isFirst; 148 | } 149 | 150 | public List getCandidateGroups() { 151 | return candidateGroups; 152 | } 153 | 154 | public void setCandidateGroups(List candidateGroups) { 155 | this.candidateGroups = candidateGroups; 156 | } 157 | 158 | public String getFormKey() { 159 | return formKey; 160 | } 161 | 162 | public void setFormKey(String formKey) { 163 | this.formKey = formKey; 164 | } 165 | 166 | public String getFormId() { 167 | return formId; 168 | } 169 | 170 | public void setFormId(String formId) { 171 | this.formId = formId; 172 | } 173 | 174 | public Long getFormVersion() { 175 | return formVersion; 176 | } 177 | 178 | public void setFormVersion(Long formVersion) { 179 | this.formVersion = formVersion; 180 | } 181 | 182 | public Boolean getIsFormEmbedded() { 183 | return isFormEmbedded; 184 | } 185 | 186 | public void setIsFormEmbedded(Boolean isFormEmbedded) { 187 | this.isFormEmbedded = isFormEmbedded; 188 | } 189 | 190 | public String getProcessDefinitionKey() { 191 | return processDefinitionKey; 192 | } 193 | 194 | public void setProcessDefinitionKey(String processDefinitionId) { 195 | this.processDefinitionKey = processDefinitionId; 196 | } 197 | 198 | public String getTaskDefinitionId() { 199 | return taskDefinitionId; 200 | } 201 | 202 | public void setTaskDefinitionId(String taskDefinitionId) { 203 | this.taskDefinitionId = taskDefinitionId; 204 | } 205 | 206 | public String getProcessInstanceKey() { 207 | return processInstanceKey; 208 | } 209 | 210 | public void setProcessInstanceKey(String processInstanceKey) { 211 | this.processInstanceKey = processInstanceKey; 212 | } 213 | 214 | public List getCandidateUsers() { 215 | return candidateUsers; 216 | } 217 | 218 | public void setCandidateUsers(List candidateUsers) { 219 | this.candidateUsers = candidateUsers; 220 | } 221 | 222 | public OffsetDateTime getFollowUpDate() { 223 | return followUpDate; 224 | } 225 | 226 | public void setFollowUpDate(OffsetDateTime followUpDate) { 227 | this.followUpDate = followUpDate; 228 | } 229 | 230 | public OffsetDateTime getDueDate() { 231 | return dueDate; 232 | } 233 | 234 | public void setDueDate(OffsetDateTime dueDate) { 235 | this.dueDate = dueDate; 236 | } 237 | 238 | public String getTenantId() { 239 | return tenantId; 240 | } 241 | 242 | public void setTenantId(String tenantId) { 243 | this.tenantId = tenantId; 244 | } 245 | 246 | public Integer getPriority() { 247 | return priority; 248 | } 249 | 250 | public void setPriority(Integer priority) { 251 | this.priority = priority; 252 | } 253 | 254 | public List getVariables() { 255 | return variables; 256 | } 257 | 258 | public void setVariables(List variables) { 259 | this.variables = variables; 260 | } 261 | 262 | public enum Implementation { 263 | JOB_WORKER, 264 | ZEEBE_USER_TASK 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/util/ConverterUtils.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 9 | import io.camunda.tasklist.TasklistClient; 10 | import io.camunda.tasklist.TasklistClient.RequestVariable; 11 | import io.camunda.tasklist.TasklistClient.TaskFromSearch; 12 | import io.camunda.tasklist.TasklistClient.VariableFromSearch; 13 | import io.camunda.tasklist.dto.Form; 14 | import io.camunda.tasklist.dto.Task; 15 | import io.camunda.tasklist.dto.TaskSearch; 16 | import io.camunda.tasklist.dto.Variable; 17 | import io.camunda.tasklist.dto.VariableType; 18 | import io.camunda.tasklist.exception.TaskListException; 19 | import io.camunda.tasklist.generated.model.TaskSearchRequest; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.Function; 24 | 25 | public class ConverterUtils { 26 | 27 | private static ObjectMapper objectMapper = null; 28 | 29 | private ConverterUtils() {} 30 | 31 | public static Variable toVariable(TasklistClient.Variable var) throws JsonProcessingException { 32 | return buildVariable(var.id(), var.name(), var.value()); 33 | } 34 | 35 | private static Variable buildVariable(String id, String name, String value) 36 | throws JsonProcessingException { 37 | Variable result = new Variable(); 38 | result.setName(name); 39 | result.setId(id); 40 | JsonNode nodeValue = getObjectMapper().readTree(value); 41 | if (nodeValue.canConvertToLong()) { 42 | result.setValue(nodeValue.asLong()); 43 | result.setType(VariableType.NUMBER); 44 | return result; 45 | } 46 | if (nodeValue.isBoolean()) { 47 | result.setValue(nodeValue.asBoolean()); 48 | result.setType(VariableType.BOOLEAN); 49 | return result; 50 | } 51 | if (nodeValue.isTextual()) { 52 | result.setValue(nodeValue.textValue()); 53 | result.setType(VariableType.STRING); 54 | return result; 55 | } 56 | if (nodeValue.isArray()) { 57 | result.setValue(getObjectMapper().convertValue(nodeValue, new TypeReference>() {})); 58 | result.setType(VariableType.LIST); 59 | return result; 60 | } 61 | result.setValue( 62 | getObjectMapper().convertValue(nodeValue, new TypeReference>() {})); 63 | result.setType(VariableType.MAP); 64 | return result; 65 | } 66 | 67 | public static Variable improveVariable(VariableFromSearch var) throws JsonProcessingException { 68 | return buildVariable(var.id(), var.name(), var.value()); 69 | } 70 | 71 | public static List toVariables(List variables) 72 | throws TaskListException { 73 | try { 74 | List result = null; 75 | 76 | if (variables != null) { 77 | result = new ArrayList<>(); 78 | 79 | for (VariableFromSearch var : variables) { 80 | result.add(improveVariable(var)); 81 | } 82 | } 83 | return result; 84 | } catch (JsonProcessingException e) { 85 | throw new TaskListException(e); 86 | } 87 | } 88 | 89 | public static Task toTask(Object sourceTask, List variables) throws TaskListException { 90 | try { 91 | Task task = 92 | getObjectMapper().readValue(getObjectMapper().writeValueAsString(sourceTask), Task.class); 93 | if (variables != null) { 94 | task.setVariables(variables); 95 | } else if (task.getVariables() != null && !task.getVariables().isEmpty()) { 96 | List improvedList = new ArrayList<>(); 97 | for (Variable v : task.getVariables()) { 98 | improvedList.add(buildVariable(v.getId(), v.getName(), (String) v.getValue())); 99 | } 100 | task.setVariables(improvedList); 101 | } 102 | 103 | return task; 104 | } catch (JsonProcessingException e) { 105 | throw new TaskListException(e); 106 | } 107 | } 108 | 109 | public static List toTasks(List tasks) throws TaskListException { 110 | List result = new ArrayList<>(); 111 | for (TaskFromSearch task : tasks) { 112 | result.add(toTask(task, null)); 113 | } 114 | return result; 115 | } 116 | 117 | public static List toVariableInput(Map variablesMap) 118 | throws TaskListException { 119 | try { 120 | List variables = new ArrayList<>(); 121 | for (Map.Entry entry : variablesMap.entrySet()) { 122 | if (entry.getValue() != null) { 123 | variables.add( 124 | new RequestVariable( 125 | entry.getKey(), getObjectMapper().writeValueAsString(entry.getValue()))); 126 | } 127 | } 128 | return variables; 129 | } catch (JsonProcessingException e) { 130 | throw new TaskListException(e); 131 | } 132 | } 133 | 134 | public static Form toForm(TasklistClient.Form form) throws TaskListException { 135 | try { 136 | return getObjectMapper().readValue(getObjectMapper().writeValueAsString(form), Form.class); 137 | } catch (JsonProcessingException e) { 138 | throw new TaskListException(e); 139 | } 140 | } 141 | 142 | public static TaskSearchRequest toTaskSearchRequest(TaskSearch taskSearch) 143 | throws TaskListException { 144 | try { 145 | return getObjectMapper() 146 | .readValue(getObjectMapper().writeValueAsString(taskSearch), TaskSearchRequest.class); 147 | } catch (JsonProcessingException e) { 148 | throw new TaskListException(e); 149 | } 150 | } 151 | 152 | private static ObjectMapper getObjectMapper() { 153 | if (objectMapper == null) { 154 | objectMapper = new ObjectMapper(); 155 | objectMapper.registerModule(new JavaTimeModule()); 156 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 157 | } 158 | return objectMapper; 159 | } 160 | 161 | public static List mapIfPresent(List list, Function mapper) { 162 | if (list == null) { 163 | return null; 164 | } 165 | return list.stream().map(mapper).toList(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /extension/client-java/src/test/java/io/camunda/tasklist/CamundaTasklistClientTest.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 4 | import static io.camunda.tasklist.CamundaTasklistClientConfiguration.*; 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | import com.auth0.jwt.JWT; 8 | import com.auth0.jwt.algorithms.Algorithm; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.github.tomakehurst.wiremock.junit5.WireMockTest; 11 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.ApiVersion; 12 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.DefaultProperties; 13 | import io.camunda.tasklist.auth.Authentication; 14 | import io.camunda.tasklist.auth.JwtAuthentication; 15 | import io.camunda.tasklist.auth.JwtCredential; 16 | import io.camunda.tasklist.auth.SimpleAuthentication; 17 | import io.camunda.tasklist.auth.SimpleCredential; 18 | import io.camunda.tasklist.auth.TokenResponseHttpClientResponseHandler; 19 | import io.camunda.tasklist.dto.TaskList; 20 | import io.camunda.tasklist.dto.TaskSearch; 21 | import io.camunda.tasklist.exception.TaskListException; 22 | import java.net.MalformedURLException; 23 | import java.net.URI; 24 | import java.net.URL; 25 | import java.time.Duration; 26 | import java.time.Instant; 27 | import java.util.Map; 28 | import org.junit.jupiter.api.Test; 29 | import wiremock.com.fasterxml.jackson.databind.node.JsonNodeFactory; 30 | 31 | @WireMockTest(httpPort = 14682) 32 | public class CamundaTasklistClientTest { 33 | public static final int PORT = 14682; 34 | public static final String BASE_URL = "http://localhost:" + PORT; 35 | private static final String ACCESS_TOKEN = 36 | JWT.create().withExpiresAt(Instant.now().plusSeconds(300)).sign(Algorithm.none()); 37 | 38 | private static URL baseUrl() { 39 | try { 40 | return URI.create(BASE_URL).toURL(); 41 | } catch (MalformedURLException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | @Test 47 | public void shouldThrowIfZeebeClientNullAndUseZeebeUserTasks() { 48 | CamundaTasklistClientConfiguration configuration = 49 | new CamundaTasklistClientConfiguration( 50 | ApiVersion.v1, 51 | new MockAuthentication(), 52 | baseUrl(), 53 | null, 54 | new DefaultProperties(false, false, true, DEFAULT_TENANT_IDS)); 55 | IllegalStateException assertionError = 56 | assertThrows(IllegalStateException.class, () -> new CamundaTaskListClient(configuration)); 57 | assertEquals( 58 | "CamundaClient is required when using Camunda user tasks", assertionError.getMessage()); 59 | } 60 | 61 | @Test 62 | public void shouldNotThrowIfZeebeClientNullAndNotUseZeebeUserTasks() { 63 | CamundaTasklistClientConfiguration configuration = 64 | new CamundaTasklistClientConfiguration( 65 | ApiVersion.v1, 66 | new MockAuthentication(), 67 | baseUrl(), 68 | null, 69 | new DefaultProperties(false, false, false, DEFAULT_TENANT_IDS)); 70 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 71 | assertNotNull(client); 72 | } 73 | 74 | @Test 75 | void shouldAuthenticateUsingSimpleAuth() throws MalformedURLException, TaskListException { 76 | stubFor( 77 | post("/api/login") 78 | .withFormParam("username", equalTo("demo")) 79 | .withFormParam("password", equalTo("demo")) 80 | .willReturn( 81 | ok().withHeader("Set-Cookie", "TASKLIST-SESSION=3205A03818447100591792E774DB8AF6") 82 | .withHeader( 83 | "Set-Cookie", 84 | "TASKLIST-X-CSRF-TOKEN=139196d4-7768-451c-aa66-078e1ed74785"))); 85 | stubFor(post("/v1/tasks/search").willReturn(ok().withBody("[]"))); 86 | CamundaTasklistClientConfiguration configuration = 87 | new CamundaTasklistClientConfiguration( 88 | ApiVersion.v1, 89 | new SimpleAuthentication( 90 | new SimpleCredential( 91 | "demo", "demo", URI.create(BASE_URL).toURL(), Duration.ofMinutes(10))), 92 | baseUrl(), 93 | null, 94 | new DefaultProperties(false, false, false, DEFAULT_TENANT_IDS)); 95 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 96 | assertNotNull(client); 97 | TaskList tasks = client.getTasks(new TaskSearch()); 98 | assertNotNull(tasks); 99 | assertEquals(0, tasks.size()); 100 | } 101 | 102 | @Test 103 | void shouldAuthenticateUsingJwt() throws MalformedURLException, TaskListException { 104 | stubFor( 105 | post("/token") 106 | .withFormParam("client_id", equalTo("abc")) 107 | .withFormParam("client_secret", equalTo("abc")) 108 | .withFormParam("audience", equalTo("tasklist-api")) 109 | .withFormParam("grant_type", equalTo("client_credentials")) 110 | .willReturn( 111 | ok().withJsonBody( 112 | JsonNodeFactory.instance 113 | .objectNode() 114 | .put("access_token", ACCESS_TOKEN) 115 | .put("expires_in", 300)))); 116 | stubFor( 117 | post("/v1/tasks/search") 118 | .withHeader("Authorization", equalTo("Bearer " + ACCESS_TOKEN)) 119 | .willReturn(ok().withBody("[]"))); 120 | CamundaTasklistClientConfiguration configuration = 121 | new CamundaTasklistClientConfiguration( 122 | ApiVersion.v1, 123 | new JwtAuthentication( 124 | new JwtCredential( 125 | "abc", "abc", "tasklist-api", URI.create(BASE_URL + "/token").toURL(), null), 126 | new TokenResponseHttpClientResponseHandler(new ObjectMapper())), 127 | baseUrl(), 128 | null, 129 | new DefaultProperties(false, false, false, DEFAULT_TENANT_IDS)); 130 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 131 | assertNotNull(client); 132 | TaskList tasks = client.getTasks(new TaskSearch()); 133 | assertNotNull(tasks); 134 | assertEquals(0, tasks.size()); 135 | } 136 | 137 | private static class MockAuthentication implements Authentication { 138 | @Override 139 | public Map getTokenHeader() { 140 | return Map.of("token", "token"); 141 | } 142 | 143 | @Override 144 | public void resetToken() {} 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /example/readme-snippets/src/main/java/io/camunda/tasklist/example/TasklistClientBootstrapper.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.example; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.camunda.client.CamundaClient; 5 | import io.camunda.tasklist.CamundaTaskListClient; 6 | import io.camunda.tasklist.CamundaTasklistClientConfiguration; 7 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.ApiVersion; 8 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.DefaultProperties; 9 | import io.camunda.tasklist.auth.JwtAuthentication; 10 | import io.camunda.tasklist.auth.JwtCredential; 11 | import io.camunda.tasklist.auth.SimpleAuthentication; 12 | import io.camunda.tasklist.auth.SimpleCredential; 13 | import io.camunda.tasklist.auth.TokenResponseHttpClientResponseHandler; 14 | import java.net.MalformedURLException; 15 | import java.net.URI; 16 | import java.net.URL; 17 | import java.time.Duration; 18 | import java.util.List; 19 | 20 | public interface TasklistClientBootstrapper { 21 | static CamundaClient camundaClient() { 22 | return CamundaClient.newClientBuilder().build(); 23 | } 24 | 25 | CamundaTaskListClient createTasklistClient() throws MalformedURLException; 26 | 27 | class SimpleAuthTasklistClientBootstrapper implements TasklistClientBootstrapper { 28 | 29 | @Override 30 | public CamundaTaskListClient createTasklistClient() throws MalformedURLException { 31 | // properties you need to provide 32 | ApiVersion apiVersion = ApiVersion.v1; 33 | String username = "demo"; 34 | String password = "demo"; 35 | URL tasklistUrl = URI.create("http://localhost:8082").toURL(); 36 | boolean returnVariables = false; 37 | boolean loadTruncatedVariables = false; 38 | boolean useZeebeUserTasks = true; 39 | List tenantIds = List.of(""); 40 | // if you are using camunda user tasks, you require a camunda client as well 41 | CamundaClient camundaClient = camundaClient(); 42 | // bootstrapping 43 | SimpleCredential credentials = 44 | new SimpleCredential(username, password, tasklistUrl, Duration.ofMinutes(10)); 45 | SimpleAuthentication authentication = new SimpleAuthentication(credentials); 46 | CamundaTasklistClientConfiguration configuration = 47 | new CamundaTasklistClientConfiguration( 48 | apiVersion, 49 | authentication, 50 | tasklistUrl, 51 | camundaClient, 52 | new DefaultProperties( 53 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 54 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 55 | return client; 56 | } 57 | } 58 | 59 | class IdentityAuthTasklistClientBootstrapper implements TasklistClientBootstrapper { 60 | @Override 61 | public CamundaTaskListClient createTasklistClient() throws MalformedURLException { 62 | // properties you need to provide 63 | ApiVersion apiVersion = ApiVersion.v1; 64 | String clientId = ""; 65 | String clientSecret = ""; 66 | String audience = "tasklist-api"; 67 | String scope = ""; // can be omitted if not required 68 | URL tasklistUrl = URI.create("http://localhost:8082").toURL(); 69 | URL authUrl = 70 | URI.create( 71 | "http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token") 72 | .toURL(); 73 | boolean returnVariables = false; 74 | boolean loadTruncatedVariables = false; 75 | boolean useZeebeUserTasks = true; 76 | List tenantIds = List.of(""); 77 | // if you are using camunda user tasks, you require a camunda client as well 78 | CamundaClient camundaClient = camundaClient(); 79 | // bootstrapping 80 | JwtCredential credentials = 81 | new JwtCredential(clientId, clientSecret, audience, authUrl, scope); 82 | ObjectMapper objectMapper = new ObjectMapper(); 83 | TokenResponseHttpClientResponseHandler clientResponseHandler = 84 | new TokenResponseHttpClientResponseHandler(objectMapper); 85 | JwtAuthentication authentication = new JwtAuthentication(credentials, clientResponseHandler); 86 | CamundaTasklistClientConfiguration configuration = 87 | new CamundaTasklistClientConfiguration( 88 | apiVersion, 89 | authentication, 90 | tasklistUrl, 91 | camundaClient, 92 | new DefaultProperties( 93 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 94 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 95 | return client; 96 | } 97 | } 98 | 99 | class SaasTasklistClientBootstrapper implements TasklistClientBootstrapper { 100 | @Override 101 | public CamundaTaskListClient createTasklistClient() throws MalformedURLException { 102 | // properties you need to provide 103 | ApiVersion apiVersion = ApiVersion.v1; 104 | String region = ""; 105 | String clusterId = ""; 106 | String clientId = ""; 107 | String clientSecret = ""; 108 | boolean returnVariables = false; 109 | boolean loadTruncatedVariables = false; 110 | boolean useZeebeUserTasks = true; 111 | List tenantIds = List.of(""); 112 | // if you are using camunda user tasks, you require a camunda client as well 113 | CamundaClient camundaClient = camundaClient(); 114 | // bootstrapping 115 | URL tasklistUrl = 116 | URI.create("https://" + region + ".tasklist.camunda.io/" + clusterId).toURL(); 117 | URL authUrl = URI.create("https://login.cloud.camunda.io/oauth/token").toURL(); 118 | JwtCredential credentials = 119 | new JwtCredential(clientId, clientSecret, "tasklist.camunda.io", authUrl, null); 120 | ObjectMapper objectMapper = new ObjectMapper(); 121 | TokenResponseHttpClientResponseHandler clientResponseHandler = 122 | new TokenResponseHttpClientResponseHandler(objectMapper); 123 | JwtAuthentication authentication = new JwtAuthentication(credentials, clientResponseHandler); 124 | CamundaTasklistClientConfiguration configuration = 125 | new CamundaTasklistClientConfiguration( 126 | apiVersion, 127 | authentication, 128 | tasklistUrl, 129 | camundaClient, 130 | new DefaultProperties( 131 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 132 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 133 | return client; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/CamundaTaskListClientBuilder.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist; 2 | 3 | import static io.camunda.tasklist.CamundaTasklistClientConfiguration.*; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import io.camunda.client.CamundaClient; 7 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.ApiVersion; 8 | import io.camunda.tasklist.CamundaTasklistClientConfiguration.DefaultProperties; 9 | import io.camunda.tasklist.auth.Authentication; 10 | import io.camunda.tasklist.auth.JwtAuthentication; 11 | import io.camunda.tasklist.auth.JwtCredential; 12 | import io.camunda.tasklist.auth.TokenResponseHttpClientResponseHandler; 13 | import io.camunda.tasklist.exception.TaskListException; 14 | import java.net.MalformedURLException; 15 | import java.net.URI; 16 | import java.net.URL; 17 | import java.time.Duration; 18 | 19 | @Deprecated 20 | public class CamundaTaskListClientBuilder { 21 | private Authentication authentication; 22 | private URL tasklistUrl; 23 | private CamundaClient camundaClient; 24 | private ApiVersion apiVersion = ApiVersion.v2; 25 | private DefaultProperties defaultProperties = 26 | new DefaultProperties(false, false, false, DEFAULT_TENANT_IDS); 27 | 28 | public CamundaTaskListClientBuilder authentication(Authentication authentication) { 29 | if (authentication != null) { 30 | this.authentication = authentication; 31 | } 32 | return this; 33 | } 34 | 35 | @Deprecated 36 | public CamundaTaskListClientBuilder taskListUrl(String taskListUrl) { 37 | try { 38 | return taskListUrl(URI.create(taskListUrl).toURL()); 39 | } catch (MalformedURLException e) { 40 | throw new RuntimeException("Error while creating url from '" + taskListUrl + "'", e); 41 | } 42 | } 43 | 44 | public CamundaTaskListClientBuilder taskListUrl(URL taskListUrl) { 45 | if (taskListUrl != null) { 46 | this.tasklistUrl = taskListUrl; 47 | } 48 | return this; 49 | } 50 | 51 | @Deprecated(forRemoval = true) 52 | public CamundaTaskListClientBuilder zeebeClient(CamundaClient camundaClient) { 53 | return camundaClient(camundaClient); 54 | } 55 | 56 | public CamundaTaskListClientBuilder camundaClient(CamundaClient camundaClient) { 57 | useCamundaUserTasks(); 58 | this.camundaClient = camundaClient; 59 | return this; 60 | } 61 | 62 | /** 63 | * Default behaviour will be to get variables along with tasks. Default value is false. Can also 64 | * be overwritten in the getTasks methods 65 | * 66 | * @return the builder 67 | */ 68 | public CamundaTaskListClientBuilder shouldReturnVariables() { 69 | this.defaultProperties = 70 | new DefaultProperties( 71 | true, 72 | defaultProperties.loadTruncatedVariables(), 73 | defaultProperties.useCamundaUserTasks(), 74 | defaultProperties.tenantIds()); 75 | return this; 76 | } 77 | 78 | public CamundaTaskListClientBuilder shouldLoadTruncatedVariables() { 79 | this.defaultProperties = 80 | new DefaultProperties( 81 | defaultProperties.returnVariables(), 82 | true, 83 | defaultProperties.useCamundaUserTasks(), 84 | defaultProperties.tenantIds()); 85 | return this; 86 | } 87 | 88 | @Deprecated(forRemoval = true) 89 | public CamundaTaskListClientBuilder alwaysReconnect() { 90 | return this; 91 | } 92 | 93 | /** 94 | * Force cookie expiration after some time (default 3mn). Only usefull with SimpleAuthentication 95 | */ 96 | @Deprecated(forRemoval = true) 97 | public CamundaTaskListClientBuilder cookieExpiration(Duration cookieExpiration) { 98 | return this; 99 | } 100 | 101 | /** 102 | * Enable when using zeebe user tasks (only relevant for >8.5). Will require presence of a zeebe 103 | * client 104 | */ 105 | @Deprecated(forRemoval = true) 106 | public CamundaTaskListClientBuilder useZeebeUserTasks() { 107 | return useCamundaUserTasks(); 108 | } 109 | 110 | /** 111 | * Enable when using zeebe user tasks (only relevant for >8.5). Will require presence of a zeebe 112 | * client 113 | */ 114 | public CamundaTaskListClientBuilder useCamundaUserTasks() { 115 | this.defaultProperties = 116 | new DefaultProperties( 117 | defaultProperties.returnVariables(), 118 | defaultProperties.loadTruncatedVariables(), 119 | true, 120 | defaultProperties.tenantIds()); 121 | return this; 122 | } 123 | 124 | public CamundaTaskListClientBuilder apiVersion(ApiVersion apiVersion) { 125 | if (apiVersion != null) { 126 | this.apiVersion = apiVersion; 127 | } 128 | return this; 129 | } 130 | 131 | public CamundaTaskListClient build() throws TaskListException { 132 | CamundaTasklistClientConfiguration configuration = 133 | new CamundaTasklistClientConfiguration( 134 | apiVersion, authentication, tasklistUrl, camundaClient, defaultProperties); 135 | return new CamundaTaskListClient(configuration); 136 | } 137 | 138 | public CamundaTaskListClientBuilder selfManagedAuthentication( 139 | String clientId, String clientSecret, String keycloakUrl) { 140 | return selfManagedAuthentication(clientId, clientSecret, "tasklist-api", keycloakUrl); 141 | } 142 | 143 | public CamundaTaskListClientBuilder selfManagedAuthentication( 144 | String clientId, String clientSecret, String audience, String keycloakUrl) { 145 | return selfManagedAuthentication(clientId, clientSecret, "tasklist-api", null, keycloakUrl); 146 | } 147 | 148 | public CamundaTaskListClientBuilder selfManagedAuthentication( 149 | String clientId, String clientSecret, String audience, String scope, String authUrl) { 150 | try { 151 | authentication = 152 | new JwtAuthentication( 153 | new JwtCredential( 154 | clientId, clientSecret, audience, URI.create(authUrl).toURL(), scope), 155 | new TokenResponseHttpClientResponseHandler(new ObjectMapper())); 156 | } catch (MalformedURLException e) { 157 | throw new RuntimeException("Error while parsing keycloak url", e); 158 | } 159 | return this; 160 | } 161 | 162 | public CamundaTaskListClientBuilder saaSAuthentication(String clientId, String clientSecret) { 163 | try { 164 | authentication = 165 | new JwtAuthentication( 166 | new JwtCredential( 167 | clientId, 168 | clientSecret, 169 | "tasklist.camunda.io", 170 | URI.create("https://login.cloud.camunda.io/oauth/token").toURL(), 171 | null), 172 | new TokenResponseHttpClientResponseHandler(new ObjectMapper())); 173 | } catch (MalformedURLException e) { 174 | throw new RuntimeException("Error while parsing token url", e); 175 | } 176 | return this; 177 | } 178 | 179 | private String formatUrl(String url) { 180 | if (url.endsWith("/")) { 181 | return url.substring(0, url.length() - 1); 182 | } 183 | return url; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /extension/client-java/src/main/java/io/camunda/tasklist/dto/TaskSearch.java: -------------------------------------------------------------------------------- 1 | package io.camunda.tasklist.dto; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.camunda.tasklist.dto.Task.Implementation; 5 | import io.camunda.tasklist.exception.TaskListException; 6 | import io.camunda.tasklist.generated.model.IncludeVariable; 7 | import io.camunda.tasklist.generated.model.TaskByVariables; 8 | import io.camunda.tasklist.generated.model.TaskByVariables.OperatorEnum; 9 | import io.camunda.tasklist.util.JsonUtils; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class TaskSearch implements Cloneable { 15 | private String candidateGroup; 16 | private List candidateGroups; 17 | private String assignee; 18 | private List assignees; 19 | private String candidateUser; 20 | private List candidateUsers; 21 | private Boolean assigned; 22 | private TaskState state; 23 | private String processDefinitionKey; 24 | private String processInstanceKey; 25 | private String taskDefinitionId; 26 | private List taskVariables; 27 | private List tenantIds; 28 | private Boolean withVariables; 29 | private DateFilter followUpDate; 30 | private DateFilter dueDate; 31 | private List includeVariables; 32 | private Pagination pagination; 33 | private Implementation implementation; 34 | private Priority priority; 35 | private String before; 36 | private String after; 37 | 38 | public String getBefore() { 39 | return before; 40 | } 41 | 42 | public void setBefore(String before) { 43 | this.before = before; 44 | } 45 | 46 | public String getAfter() { 47 | return after; 48 | } 49 | 50 | public void setAfter(String after) { 51 | this.after = after; 52 | } 53 | 54 | public List getAssignees() { 55 | return assignees; 56 | } 57 | 58 | public void setAssignees(List assignees) { 59 | this.assignees = assignees; 60 | } 61 | 62 | public Priority getPriority() { 63 | return priority; 64 | } 65 | 66 | public void setPriority(Priority priority) { 67 | this.priority = priority; 68 | } 69 | 70 | public Implementation getImplementation() { 71 | return implementation; 72 | } 73 | 74 | public void setImplementation(Implementation implementation) { 75 | this.implementation = implementation; 76 | } 77 | 78 | public List getCandidateGroups() { 79 | return candidateGroups; 80 | } 81 | 82 | public TaskSearch setCandidateGroups(List candidateGroups) { 83 | this.candidateGroups = candidateGroups; 84 | return this; 85 | } 86 | 87 | public List getCandidateUsers() { 88 | return candidateUsers; 89 | } 90 | 91 | public TaskSearch setCandidateUsers(List candidateUsers) { 92 | this.candidateUsers = candidateUsers; 93 | return this; 94 | } 95 | 96 | public TaskSearch setWithVariables(Boolean withVariables) { 97 | this.withVariables = withVariables; 98 | return this; 99 | } 100 | 101 | public String getCandidateUser() { 102 | return candidateUser; 103 | } 104 | 105 | public TaskSearch setCandidateUser(String candidateUser) { 106 | this.candidateUser = candidateUser; 107 | return this; 108 | } 109 | 110 | public DateFilter getFollowUpDate() { 111 | return followUpDate; 112 | } 113 | 114 | public TaskSearch setFollowUpDate(DateFilter followUpDate) { 115 | this.followUpDate = followUpDate; 116 | return this; 117 | } 118 | 119 | public DateFilter getDueDate() { 120 | return dueDate; 121 | } 122 | 123 | public TaskSearch setDueDate(DateFilter dueDate) { 124 | this.dueDate = dueDate; 125 | return this; 126 | } 127 | 128 | public String getCandidateGroup() { 129 | return candidateGroup; 130 | } 131 | 132 | public TaskSearch setCandidateGroup(String candidateGroup) { 133 | this.candidateGroup = candidateGroup; 134 | return this; 135 | } 136 | 137 | public String getAssignee() { 138 | return assignee; 139 | } 140 | 141 | public TaskSearch setAssignee(String assignee) { 142 | this.assignee = assignee; 143 | return this; 144 | } 145 | 146 | public Boolean getAssigned() { 147 | return assigned; 148 | } 149 | 150 | public TaskSearch setAssigned(Boolean assigned) { 151 | this.assigned = assigned; 152 | return this; 153 | } 154 | 155 | public TaskState getState() { 156 | return state; 157 | } 158 | 159 | public TaskSearch setState(TaskState state) { 160 | this.state = state; 161 | return this; 162 | } 163 | 164 | public String getProcessDefinitionKey() { 165 | return processDefinitionKey; 166 | } 167 | 168 | public TaskSearch setProcessDefinitionKey(String processDefinitionId) { 169 | this.processDefinitionKey = processDefinitionId; 170 | return this; 171 | } 172 | 173 | public String getProcessInstanceKey() { 174 | return processInstanceKey; 175 | } 176 | 177 | public TaskSearch setProcessInstanceKey(String processInstanceId) { 178 | this.processInstanceKey = processInstanceId; 179 | return this; 180 | } 181 | 182 | public String getTaskDefinitionId() { 183 | return taskDefinitionId; 184 | } 185 | 186 | public TaskSearch setTaskDefinitionId(String taskDefinitionId) { 187 | this.taskDefinitionId = taskDefinitionId; 188 | return this; 189 | } 190 | 191 | public List getTaskVariables() { 192 | return taskVariables; 193 | } 194 | 195 | public TaskSearch setTaskVariables(List taskVariables) { 196 | this.taskVariables = taskVariables; 197 | return this; 198 | } 199 | 200 | public TaskSearch addVariableFilter(String variableName, Object variableValue) 201 | throws TaskListException { 202 | return this.addVariableFilter( 203 | new TaskByVariables() 204 | .name(variableName) 205 | .value(JsonUtils.toJsonString(variableValue)) 206 | .operator(OperatorEnum.EQ)); 207 | } 208 | 209 | public TaskSearch addVariableFilter(TaskByVariables variableFilter) { 210 | if (this.taskVariables == null) { 211 | this.taskVariables = new ArrayList<>(); 212 | } 213 | this.taskVariables.add(variableFilter); 214 | 215 | return this; 216 | } 217 | 218 | public List getTenantIds() { 219 | return tenantIds; 220 | } 221 | 222 | public TaskSearch setTenantIds(List tenantIds) { 223 | this.tenantIds = tenantIds; 224 | return this; 225 | } 226 | 227 | public TaskSearch addTenantId(String tenantId) { 228 | if (this.tenantIds == null) { 229 | this.tenantIds = new ArrayList<>(); 230 | } 231 | this.tenantIds.add(tenantId); 232 | return this; 233 | } 234 | 235 | public Boolean getWithVariables() { 236 | return withVariables; 237 | } 238 | 239 | public boolean isWithVariables() { 240 | return withVariables != null && withVariables; 241 | } 242 | 243 | public TaskSearch setWithVariables(boolean withVariables) { 244 | this.withVariables = withVariables; 245 | return this; 246 | } 247 | 248 | public List getIncludeVariables() { 249 | return includeVariables; 250 | } 251 | 252 | public TaskSearch setIncludeVariables(List includeVariables) { 253 | this.includeVariables = includeVariables; 254 | return this; 255 | } 256 | 257 | public TaskSearch fetchVariable(String variable) { 258 | return fetchVariable(variable, true); 259 | } 260 | 261 | public TaskSearch fetchVariable(String variable, boolean alwaysReturnFullValue) { 262 | if (this.includeVariables == null) { 263 | this.includeVariables = new ArrayList<>(); 264 | } 265 | IncludeVariable iv = new IncludeVariable(); 266 | iv.setName(variable); 267 | iv.alwaysReturnFullValue(alwaysReturnFullValue); 268 | this.includeVariables.add(iv); 269 | return this; 270 | } 271 | 272 | public Pagination getPagination() { 273 | return pagination; 274 | } 275 | 276 | public TaskSearch setPagination(Pagination pagination) { 277 | this.pagination = pagination; 278 | return this; 279 | } 280 | 281 | public record Priority(Integer eq, Integer gte, Integer gt, Integer lt, Integer lte) {} 282 | 283 | @Override 284 | public TaskSearch clone() { 285 | try { 286 | ObjectMapper objectMapper = new ObjectMapper(); 287 | byte[] bytes = objectMapper.writeValueAsBytes(this); 288 | return objectMapper.readValue(bytes, TaskSearch.class); 289 | } catch (IOException e) { 290 | throw new RuntimeException("Error while cloning TaskSearch", e); 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Community Extension](https://img.shields.io/badge/Community%20Extension-An%20open%20source%20community%20maintained%20project-FF4700)](https://github.com/camunda-community-hub/community) 2 | ![Compatible with: Camunda Platform 8](https://img.shields.io/badge/Compatible%20with-Camunda%20Platform%208-0072Ce) 3 | [![](https://img.shields.io/badge/Lifecycle-Incubating-blue)](https://github.com/Camunda-Community-Hub/community/blob/main/extension-lifecycle.md#incubating-) 4 | 5 | # Camunda TaskList Client 6 | 7 | This project was intially designed to simplify communication between a java backend and the Camunda 8 task list GraphQL APIs. Since GraphQL APIs are now deprecated, this client is now targeting REST endpoints. Contributions through PR are welcome! 8 | 9 | :information_source: 8.3+ Relesases of this client are generated against Rest endpoints. 10 | 11 | :information_source: **8.3.3.3 changes the way to build authentication and client. Please check the following documentation** 12 | 13 | ## How to build the client 14 | 15 | ### Spring Boot 16 | 17 | Add the dependency to your project: 18 | 19 | ```xml 20 | 21 | io.camunda 22 | spring-boot-starter-camunda-tasklist 23 | ${version.tasklist-client} 24 | 25 | ``` 26 | 27 | This client is compatible with the Camunda v2 API. To use it, please configure: 28 | 29 | ```yaml 30 | tasklist: 31 | client: 32 | profile: v2 33 | ``` 34 | 35 | Configure a Camunda Tasklist client with simple authentication: 36 | 37 | ```yaml 38 | tasklist: 39 | client: 40 | profile: simple 41 | ``` 42 | 43 | To adjust the (meaningful) default properties, you can also override them: 44 | 45 | ```yaml 46 | tasklist: 47 | client: 48 | profile: simple 49 | enabled: true 50 | base-url: http://localhost:8082 51 | session-timeout: PT10M 52 | username: demo 53 | password: demo 54 | ``` 55 | 56 | 57 | Configure a Camunda Tasklist client with identity authentication: 58 | 59 | ```yaml 60 | tasklist: 61 | client: 62 | profile: oidc 63 | client-id: 64 | client-secret: 65 | scope: # optional 66 | ``` 67 | 68 | To adjust the (meaningful) default properties, you can also override them: 69 | 70 | ```yaml 71 | tasklist: 72 | client: 73 | profile: oidc 74 | enabled: true 75 | base-url: http://localhost:8082 76 | auth-url: http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token 77 | audience: tasklist-api 78 | client-id: 79 | client-secret: 80 | scope: # optional 81 | ``` 82 | 83 | Configure a Camunda Tasklist client for Saas: 84 | 85 | ```yaml 86 | tasklist: 87 | client: 88 | profile: saas 89 | region: 90 | cluster-id: 91 | client-id: 92 | client-secret: 93 | ``` 94 | 95 | ```yaml 96 | tasklist: 97 | client: 98 | profile: saas 99 | enabled: true 100 | base-url: https://${tasklist.client.region}.tasklist.camunda.io/${tasklist.client.cluster-id} 101 | auth-url: https://login.cloud.camunda.io/oauth/token 102 | audience: tasklist.camunda.io 103 | region: 104 | cluster-id: 105 | client-id: 106 | client-secret: 107 | ``` 108 | 109 | Configure defaults that influence the client behavior: 110 | 111 | ```yaml 112 | tasklist: 113 | client: 114 | defaults: 115 | load-truncated-variables: true 116 | return-variables: true 117 | use-zeebe-user-tasks: true 118 | tenant-ids: 119 | - 120 | ``` 121 | 122 | ### Plain Java 123 | 124 | Add the dependency to your project: 125 | 126 | ```xml 127 | 128 | io.camunda 129 | camunda-tasklist-client-java 130 | ${version.tasklist-client} 131 | 132 | ``` 133 | 134 | Build a Camunda Tasklist client with simple authentication: 135 | 136 | ```java 137 | // properties you need to provide 138 | ApiVersion apiVersion = ApiVersion.v1; 139 | String username = "demo"; 140 | String password = "demo"; 141 | URL tasklistUrl = URI.create("http://localhost:8082").toURL(); 142 | boolean returnVariables = false; 143 | boolean loadTruncatedVariables = false; 144 | boolean useZeebeUserTasks = true; 145 | List tenantIds = List.of(""); 146 | // if you are using camunda user tasks, you require a camunda client as well 147 | CamundaClient camundaClient = camundaClient(); 148 | // bootstrapping 149 | SimpleCredential credentials = 150 | new SimpleCredential(username, password, tasklistUrl, Duration.ofMinutes(10)); 151 | SimpleAuthentication authentication = new SimpleAuthentication(credentials); 152 | CamundaTasklistClientConfiguration configuration = 153 | new CamundaTasklistClientConfiguration( 154 | apiVersion, 155 | authentication, 156 | tasklistUrl, 157 | camundaClient, 158 | new DefaultProperties( 159 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 160 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 161 | ``` 162 | 163 | Build a Camunda Tasklist client with identity authentication: 164 | 165 | ```java 166 | // properties you need to provide 167 | ApiVersion apiVersion = ApiVersion.v1; 168 | String clientId = ""; 169 | String clientSecret = ""; 170 | String audience = "tasklist-api"; 171 | String scope = ""; // can be omitted if not required 172 | URL tasklistUrl = URI.create("http://localhost:8082").toURL(); 173 | URL authUrl = 174 | URI.create( 175 | "http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token") 176 | .toURL(); 177 | boolean returnVariables = false; 178 | boolean loadTruncatedVariables = false; 179 | boolean useZeebeUserTasks = true; 180 | List tenantIds = List.of(""); 181 | // if you are using camunda user tasks, you require a camunda client as well 182 | CamundaClient camundaClient = camundaClient(); 183 | // bootstrapping 184 | JwtCredential credentials = 185 | new JwtCredential(clientId, clientSecret, audience, authUrl, scope); 186 | ObjectMapper objectMapper = new ObjectMapper(); 187 | TokenResponseHttpClientResponseHandler clientResponseHandler = 188 | new TokenResponseHttpClientResponseHandler(objectMapper); 189 | JwtAuthentication authentication = new JwtAuthentication(credentials, clientResponseHandler); 190 | CamundaTasklistClientConfiguration configuration = 191 | new CamundaTasklistClientConfiguration( 192 | apiVersion, 193 | authentication, 194 | tasklistUrl, 195 | camundaClient, 196 | new DefaultProperties( 197 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 198 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 199 | ``` 200 | 201 | Build a Camunda Tasklist client for Saas: 202 | 203 | ```java 204 | // properties you need to provide 205 | ApiVersion apiVersion = ApiVersion.v1; 206 | String region = ""; 207 | String clusterId = ""; 208 | String clientId = ""; 209 | String clientSecret = ""; 210 | boolean returnVariables = false; 211 | boolean loadTruncatedVariables = false; 212 | boolean useZeebeUserTasks = true; 213 | List tenantIds = List.of(""); 214 | // if you are using camunda user tasks, you require a camunda client as well 215 | CamundaClient camundaClient = camundaClient(); 216 | // bootstrapping 217 | URL tasklistUrl = 218 | URI.create("https://" + region + ".tasklist.camunda.io/" + clusterId).toURL(); 219 | URL authUrl = URI.create("https://login.cloud.camunda.io/oauth/token").toURL(); 220 | JwtCredential credentials = 221 | new JwtCredential(clientId, clientSecret, "tasklist.camunda.io", authUrl, null); 222 | ObjectMapper objectMapper = new ObjectMapper(); 223 | TokenResponseHttpClientResponseHandler clientResponseHandler = 224 | new TokenResponseHttpClientResponseHandler(objectMapper); 225 | JwtAuthentication authentication = new JwtAuthentication(credentials, clientResponseHandler); 226 | CamundaTasklistClientConfiguration configuration = 227 | new CamundaTasklistClientConfiguration( 228 | apiVersion, 229 | authentication, 230 | tasklistUrl, 231 | camundaClient, 232 | new DefaultProperties( 233 | returnVariables, loadTruncatedVariables, useZeebeUserTasks, tenantIds)); 234 | CamundaTaskListClient client = new CamundaTaskListClient(configuration); 235 | ``` 236 | 237 | :information_source: **shouldReturnVariables()** will read variables along with tasks. This is not the recommended approach but rather a commodity. In real project implementation, we would recommend to load task variables only when required. 238 | 239 | :information_source: **shouldLoadTruncatedVariables()** will execute a second call to read the variable if its value was truncated in the initial search. 240 | 241 | ## Make some queries 242 | ```java 243 | //get tasks from a process instance (TaskSearch can take many more parameters) 244 | TaskSearch ts = new TaskSearch().setProcessInstanceKey("2251799818839086"); 245 | TaskList tasksFromInstance = client.getTasks(ts); 246 | 247 | //get tasks from process variables 248 | ts = new TaskSearch().addVariableFilter("riskLevels", List.of("yellow", "yellow")).addVariableFilter("age", 30); 249 | TaskList tasksFromVariableSearch = client.getTasks(ts); 250 | 251 | //get tasks assigned to demo 252 | TaskList tasks = client.getAssigneeTasks("demo", TaskState.CREATED, null); 253 | for(Task task : tasks) { 254 | client.unclaim(task.getId()); 255 | } 256 | //get tasks associated with group "toto" 257 | tasks = client.getGroupTasks("toto", null, null); 258 | 259 | //get 10 completed tasks without their variables (last parameter) associated with group "toto", assigned (second parameter) to paul (thrid parameter) 260 | tasks = client.getTasks("toto", true, "paul", TaskState.COMPLETED, 10, false); 261 | 262 | //navigate after, before, afterOrEqual to previous result. 263 | tasks = client.after(tasks); 264 | tasks = client.before(tasks); 265 | tasks = client.afterOrEqual(tasks); 266 | 267 | //get unassigned tasks 268 | tasks = client.getTasks(false, null, null); 269 | for(Task task : tasks) { 270 | //assign task to paul 271 | client.claim(task.getId(), "paul"); 272 | } 273 | for(Task task : tasks) { 274 | //complete task with variables 275 | client.completeTask(task.getId(), Map.of("key", "value")); 276 | } 277 | 278 | //get a single task 279 | task = client.getTask("1"); 280 | 281 | //get form schema 282 | String formKey = task.getFormKey(); 283 | String formId = formKey.substring(formKey.lastIndexOf(":")+1); 284 | String processDefinitionId = task.getProcessDefinitionId(); 285 | 286 | Form form = client.getForm(formId, processDefinitionId); 287 | String schema = form.getSchema(); 288 | ``` 289 | 290 | # Note 291 | A similar library is available for the Operate API of Camunda Platform here: 292 | [camunda-operate-client-java](https://github.com/camunda-community-hub/camunda-operate-client-java) 293 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /extension/generated/tasklist-8.3.0.json: -------------------------------------------------------------------------------- 1 | {"openapi":"3.0.1","info":{"title":"Tasklist webapp API","description":"Tasklist is a ready-to-use API application to rapidly implement business processes alongside user tasks in Zeebe.","contact":{"url":"https://www.camunda.com"},"license":{"name":"License","url":"https://docs.camunda.io/docs/reference/licenses/"},"version":"v1"},"servers":[{"url":"https://bru-2.tasklist.camunda.io/yourClusterId","description":"Generated server url"}],"security":[{"cookie":[],"bearer-key":[]}],"tags":[{"name":"Form","description":"API to query forms"},{"name":"Variables","description":"API to query variables"},{"name":"Task","description":"API to query and manage tasks"}],"paths":{"/v1/tasks/{taskId}/variables":{"post":{"tags":["Task"],"summary":"Saves draft variables for a task.","description":"This operation validates the task and draft variables, deletes existing draft variables for the task, and then checks for new draft variables. If a new variable's `name` matches an existing one but the `value` differs, it is saved. In case of duplicate draft variable names, the last variable's value is kept.","operationId":"saveDraftTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveVariablesRequest"}}},"required":true},"responses":{"204":{"description":"On success returned"},"400":{"description":"An error is returned when the task is not active (not in the `CREATED` state).
An error is returned if the task was not claimed (assigned) before, except the case when JWT authentication token used.
An error is returned if the task is not assigned to the current user, except the case when JWT authentication token used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error is returned if an unexpected error occurs while persisting draft task variables.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/variables/search":{"post":{"tags":["Task"],"summary":"Returns the list of task variables","description":"This method returns a list of task variables for the specified `taskId` and `variableName`.
If the request body is not provided or if the `variableNames` parameter in the request is empty, all variables associated with the task will be returned.","operationId":"searchTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariablesSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VariableSearchResponse"}}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/search":{"post":{"tags":["Task"],"summary":"Returns the list of tasks that satisfy search request params","description":"Returns the list of tasks that satisfy search request params.
NOTE: Only one of `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]`search options must be present in request.","operationId":"searchTasks","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSearchResponse"}}}}},"400":{"description":"An error is returned when more than one search parameters among `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]` are present in request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/unassign":{"patch":{"tags":["Task"],"summary":"Unassign a task with provided id. Returns the task.","description":"Unassign a task with `taskId`.","operationId":"unassignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/complete":{"patch":{"tags":["Task"],"summary":"Complete a task with taskId and optional variables. Returns the task.","description":"Complete a task with `taskId` and optional `variables`","operationId":"completeTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCompleteRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.
An error is returned if the task is not assigned to the current user.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/assign":{"patch":{"tags":["Task"],"summary":"Assign a task with id to assignee. Returns the task.","description":"Assign a task with `taskId` to `assignee` or the active user.","operationId":"assignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"When using REST API with JWT authentication token following request body parameters may be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAssignRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned when task was already assigned, except the case when JWT authentication token used and `allowOverrideAssignment = true`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"An error is returned when user doesn't have the permission to assign another user to this task.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/variables/{variableId}":{"get":{"tags":["Variables"],"summary":"Get the variable details by variable id.","operationId":"getVariableById","parameters":[{"name":"variableId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariableResponse"}}}},"404":{"description":"An error is returned when the variable with the `variableId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}":{"get":{"tags":["Task"],"summary":"Get one task by id. Returns task or error when task does not exist.","operationId":"getTaskById","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{formId}":{"get":{"tags":["Form"],"summary":"Get the form details by form id and processDefinitionKey.","description":"Get the form details by `formId` and `processDefinitionKey` required query param.","operationId":"getForm","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string"}},{"name":"processDefinitionKey","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"404":{"description":"An error is returned when the form with the `formId` and `processDefinitionKey` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"},"instance":{"type":"string"}}},"SaveVariablesRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"VariableInputDTO":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"VariablesSearchRequest":{"type":"object","properties":{"variableNames":{"type":"array","items":{"type":"string"}}}},"DraftSearchVariableValue":{"type":"object","properties":{"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"}}},"VariableSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftSearchVariableValue"}}},"DateFilter":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"TaskByVariables":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"operator":{"type":"string","enum":["eq"]}}},"TaskOrderBy":{"type":"object","properties":{"field":{"type":"string","enum":["completionTime","creationTime","followUpDate","dueDate"]},"order":{"type":"string","enum":["ASC","DESC"]}}},"TaskSearchRequest":{"type":"object","properties":{"state":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"assigned":{"type":"boolean"},"assignee":{"type":"string"},"taskDefinitionId":{"type":"string"},"candidateGroup":{"type":"string"},"candidateUser":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"pageSize":{"type":"integer","format":"int32"},"followUpDate":{"$ref":"#/components/schemas/DateFilter"},"dueDate":{"$ref":"#/components/schemas/DateFilter"},"taskVariables":{"type":"array","items":{"$ref":"#/components/schemas/TaskByVariables"}},"sort":{"type":"array","items":{"$ref":"#/components/schemas/TaskOrderBy"}},"searchAfter":{"type":"array","items":{"type":"string"}},"searchAfterOrEqual":{"type":"array","items":{"type":"string"}},"searchBefore":{"type":"array","items":{"type":"string"}},"searchBeforeOrEqual":{"type":"array","items":{"type":"string"}}}},"TaskSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"sortValues":{"type":"array","items":{"type":"string"}},"isFirst":{"type":"boolean"},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskCompleteRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"TaskAssignRequest":{"type":"object","properties":{"assignee":{"type":"string","description":"When using a JWT token, the assignee parameter is NOT optional when called directly from the API.\nThe system will not be able to detect the assignee from the JWT token, therefore the assignee parameter needs to be\nexplicitly passed in this instance."},"allowOverrideAssignment":{"type":"boolean","description":"When `true` the task that is already assigned may be assigned again. Otherwise the task\nmust be first unassign and only then assign again. (Default: `true`)"}}},"DraftVariableValue":{"type":"object","properties":{"value":{"type":"string"}}},"VariableResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftVariableValue"}}},"FormResponse":{"type":"object","properties":{"id":{"type":"string"},"processDefinitionKey":{"type":"string"},"title":{"type":"string"},"schema":{"type":"string"}}}},"securitySchemes":{"cookie":{"type":"apiKey","name":"TASKLIST-SESSION","in":"cookie"},"bearer-key":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} -------------------------------------------------------------------------------- /extension/generated/tasklist-8.3.1.json: -------------------------------------------------------------------------------- 1 | {"openapi":"3.0.1","info":{"title":"Tasklist webapp API","description":"Tasklist is a ready-to-use API application to rapidly implement business processes alongside user tasks in Zeebe.","contact":{"url":"https://www.camunda.com"},"license":{"name":"License","url":"https://docs.camunda.io/docs/reference/licenses/"},"version":"v1"},"servers":[{"url":"https://bru-2.tasklist.camunda.io/yourClusterId","description":"Generated server url"}],"security":[{"cookie":[],"bearer-key":[]}],"tags":[{"name":"Form","description":"API to query forms"},{"name":"Variables","description":"API to query variables"},{"name":"Task","description":"API to query and manage tasks"}],"paths":{"/v1/tasks/{taskId}/variables":{"post":{"tags":["Task"],"summary":"Saves draft variables for a task.","description":"This operation validates the task and draft variables, deletes existing draft variables for the task, and then checks for new draft variables. If a new variable's `name` matches an existing one but the `value` differs, it is saved. In case of duplicate draft variable names, the last variable's value is kept.","operationId":"saveDraftTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveVariablesRequest"}}},"required":true},"responses":{"204":{"description":"On success returned"},"400":{"description":"An error is returned when the task is not active (not in the `CREATED` state).
An error is returned if the task was not claimed (assigned) before, except the case when JWT authentication token used.
An error is returned if the task is not assigned to the current user, except the case when JWT authentication token used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error is returned if an unexpected error occurs while persisting draft task variables.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/variables/search":{"post":{"tags":["Task"],"summary":"Returns the list of task variables","description":"This method returns a list of task variables for the specified `taskId` and `variableName`.
If the request body is not provided or if the `variableNames` parameter in the request is empty, all variables associated with the task will be returned.","operationId":"searchTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariablesSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VariableSearchResponse"}}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/search":{"post":{"tags":["Task"],"summary":"Returns the list of tasks that satisfy search request params","description":"Returns the list of tasks that satisfy search request params.
NOTE: Only one of `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]`search options must be present in request.","operationId":"searchTasks","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSearchResponse"}}}}},"400":{"description":"An error is returned when more than one search parameters among `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]` are present in request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/unassign":{"patch":{"tags":["Task"],"summary":"Unassign a task with provided id. Returns the task.","description":"Unassign a task with `taskId`.","operationId":"unassignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/complete":{"patch":{"tags":["Task"],"summary":"Complete a task with taskId and optional variables. Returns the task.","description":"Complete a task with `taskId` and optional `variables`","operationId":"completeTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCompleteRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.
An error is returned if the task is not assigned to the current user.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/assign":{"patch":{"tags":["Task"],"summary":"Assign a task with id to assignee. Returns the task.","description":"Assign a task with `taskId` to `assignee` or the active user.","operationId":"assignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"When using REST API with JWT authentication token following request body parameters may be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAssignRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned when task was already assigned, except the case when JWT authentication token used and `allowOverrideAssignment = true`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"An error is returned when user doesn't have the permission to assign another user to this task.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/variables/{variableId}":{"get":{"tags":["Variables"],"summary":"Get the variable details by variable id.","operationId":"getVariableById","parameters":[{"name":"variableId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariableResponse"}}}},"404":{"description":"An error is returned when the variable with the `variableId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}":{"get":{"tags":["Task"],"summary":"Get one task by id. Returns task or error when task does not exist.","operationId":"getTaskById","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{formId}":{"get":{"tags":["Form"],"summary":"Get the form details by form id and processDefinitionKey.","description":"Get the form details by `formId` and `processDefinitionKey` required query param.","operationId":"getForm","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string"}},{"name":"processDefinitionKey","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"404":{"description":"An error is returned when the form with the `formId` and `processDefinitionKey` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"},"instance":{"type":"string"}}},"SaveVariablesRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"VariableInputDTO":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"VariablesSearchRequest":{"type":"object","properties":{"variableNames":{"type":"array","items":{"type":"string"}}}},"DraftSearchVariableValue":{"type":"object","properties":{"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"}}},"VariableSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftSearchVariableValue"}}},"DateFilter":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"TaskByVariables":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"operator":{"type":"string","enum":["eq"]}}},"TaskOrderBy":{"type":"object","properties":{"field":{"type":"string","enum":["completionTime","creationTime","followUpDate","dueDate"]},"order":{"type":"string","enum":["ASC","DESC"]}}},"TaskSearchRequest":{"type":"object","properties":{"state":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"assigned":{"type":"boolean"},"assignee":{"type":"string"},"taskDefinitionId":{"type":"string"},"candidateGroup":{"type":"string"},"candidateUser":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"pageSize":{"type":"integer","format":"int32"},"followUpDate":{"$ref":"#/components/schemas/DateFilter"},"dueDate":{"$ref":"#/components/schemas/DateFilter"},"taskVariables":{"type":"array","items":{"$ref":"#/components/schemas/TaskByVariables"}},"tenantIds":{"type":"array","items":{"type":"string"}},"sort":{"type":"array","items":{"$ref":"#/components/schemas/TaskOrderBy"}},"searchAfter":{"type":"array","items":{"type":"string"}},"searchAfterOrEqual":{"type":"array","items":{"type":"string"}},"searchBefore":{"type":"array","items":{"type":"string"}},"searchBeforeOrEqual":{"type":"array","items":{"type":"string"}}}},"TaskSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"sortValues":{"type":"array","items":{"type":"string"}},"isFirst":{"type":"boolean"},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskCompleteRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"TaskAssignRequest":{"type":"object","properties":{"assignee":{"type":"string","description":"When using a JWT token, the assignee parameter is NOT optional when called directly from the API.\nThe system will not be able to detect the assignee from the JWT token, therefore the assignee parameter needs to be\nexplicitly passed in this instance."},"allowOverrideAssignment":{"type":"boolean","description":"When `true` the task that is already assigned may be assigned again. Otherwise the task\nmust be first unassign and only then assign again. (Default: `true`)"}}},"DraftVariableValue":{"type":"object","properties":{"value":{"type":"string"}}},"VariableResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftVariableValue"},"tenantId":{"type":"string"}}},"FormResponse":{"type":"object","properties":{"id":{"type":"string"},"processDefinitionKey":{"type":"string"},"title":{"type":"string"},"schema":{"type":"string"},"tenantId":{"type":"string"}}}},"securitySchemes":{"cookie":{"type":"apiKey","name":"TASKLIST-SESSION","in":"cookie"},"bearer-key":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} -------------------------------------------------------------------------------- /extension/generated/tasklist-8.3.3.json: -------------------------------------------------------------------------------- 1 | {"openapi":"3.0.1","info":{"title":"Tasklist webapp API","description":"Tasklist is a ready-to-use API application to rapidly implement business processes alongside user tasks in Zeebe.","contact":{"url":"https://www.camunda.com"},"license":{"name":"License","url":"https://docs.camunda.io/docs/reference/licenses/"},"version":"v1"},"servers":[{"url":"https://bru-2.tasklist.camunda.io/becfaacc-8705-42eb-b37c-67d33174c469","description":"Generated server url"}],"security":[{"cookie":[],"bearer-key":[]}],"tags":[{"name":"Form","description":"API to query forms"},{"name":"Variables","description":"API to query variables"},{"name":"Task","description":"API to query and manage tasks"}],"paths":{"/v1/tasks/{taskId}/variables":{"post":{"tags":["Task"],"summary":"Saves draft variables for a task.","description":"This operation validates the task and draft variables, deletes existing draft variables for the task, and then checks for new draft variables. If a new variable's `name` matches an existing one but the `value` differs, it is saved. In case of duplicate draft variable names, the last variable's value is kept.","operationId":"saveDraftTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveVariablesRequest"}}},"required":true},"responses":{"204":{"description":"On success returned"},"400":{"description":"An error is returned when the task is not active (not in the `CREATED` state).
An error is returned if the task was not claimed (assigned) before, except the case when JWT authentication token used.
An error is returned if the task is not assigned to the current user, except the case when JWT authentication token used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error is returned if an unexpected error occurs while persisting draft task variables.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/variables/search":{"post":{"tags":["Task"],"summary":"Returns the list of task variables","description":"This method returns a list of task variables for the specified `taskId` and `variableName`.
If the request body is not provided or if the `variableNames` parameter in the request is empty, all variables associated with the task will be returned.","operationId":"searchTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariablesSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VariableSearchResponse"}}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/search":{"post":{"tags":["Task"],"summary":"Returns the list of tasks that satisfy search request params","description":"Returns the list of tasks that satisfy search request params.
NOTE: Only one of `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]`search options must be present in request.","operationId":"searchTasks","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSearchResponse"}}}}},"400":{"description":"An error is returned when more than one search parameters among `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]` are present in request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/unassign":{"patch":{"tags":["Task"],"summary":"Unassign a task with provided id. Returns the task.","description":"Unassign a task with `taskId`.","operationId":"unassignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/complete":{"patch":{"tags":["Task"],"summary":"Complete a task with taskId and optional variables. Returns the task.","description":"Complete a task with `taskId` and optional `variables`","operationId":"completeTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCompleteRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.
An error is returned if the task is not assigned to the current user.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/assign":{"patch":{"tags":["Task"],"summary":"Assign a task with id to assignee. Returns the task.","description":"Assign a task with `taskId` to `assignee` or the active user.","operationId":"assignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"When using REST API with JWT authentication token following request body parameters may be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAssignRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned when task was already assigned, except the case when JWT authentication token used and `allowOverrideAssignment = true`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"An error is returned when user doesn't have the permission to assign another user to this task.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/variables/{variableId}":{"get":{"tags":["Variables"],"summary":"Get the variable details by variable id.","operationId":"getVariableById","parameters":[{"name":"variableId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariableResponse"}}}},"404":{"description":"An error is returned when the variable with the `variableId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}":{"get":{"tags":["Task"],"summary":"Get one task by id. Returns task or error when task does not exist.","operationId":"getTaskById","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{formId}":{"get":{"tags":["Form"],"summary":"Get the form details by form id and processDefinitionKey.","description":"Get the form details by `formId` and `processDefinitionKey` required query param.","operationId":"getForm","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string"}},{"name":"processDefinitionKey","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"404":{"description":"An error is returned when the form with the `formId` and `processDefinitionKey` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"},"instance":{"type":"string"}}},"SaveVariablesRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"VariableInputDTO":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"VariablesSearchRequest":{"type":"object","properties":{"variableNames":{"type":"array","items":{"type":"string"}}}},"DraftSearchVariableValue":{"type":"object","properties":{"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"}}},"VariableSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftSearchVariableValue"}}},"DateFilter":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"TaskByVariables":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"operator":{"type":"string","enum":["eq"]}}},"TaskOrderBy":{"type":"object","properties":{"field":{"type":"string","enum":["completionTime","creationTime","followUpDate","dueDate"]},"order":{"type":"string","enum":["ASC","DESC"]}}},"TaskSearchRequest":{"type":"object","properties":{"state":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"assigned":{"type":"boolean"},"assignee":{"type":"string"},"taskDefinitionId":{"type":"string"},"candidateGroup":{"type":"string"},"candidateUser":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"pageSize":{"type":"integer","format":"int32"},"followUpDate":{"$ref":"#/components/schemas/DateFilter"},"dueDate":{"$ref":"#/components/schemas/DateFilter"},"taskVariables":{"type":"array","items":{"$ref":"#/components/schemas/TaskByVariables"}},"tenantIds":{"type":"array","items":{"type":"string"}},"sort":{"type":"array","items":{"$ref":"#/components/schemas/TaskOrderBy"}},"searchAfter":{"type":"array","items":{"type":"string"}},"searchAfterOrEqual":{"type":"array","items":{"type":"string"}},"searchBefore":{"type":"array","items":{"type":"string"}},"searchBeforeOrEqual":{"type":"array","items":{"type":"string"}}}},"TaskSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"sortValues":{"type":"array","items":{"type":"string"}},"isFirst":{"type":"boolean"},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"formKey":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskCompleteRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"TaskAssignRequest":{"type":"object","properties":{"assignee":{"type":"string","description":"When using a JWT token, the assignee parameter is NOT optional when called directly from the API.\nThe system will not be able to detect the assignee from the JWT token, therefore the assignee parameter needs to be\nexplicitly passed in this instance."},"allowOverrideAssignment":{"type":"boolean","description":"When `true` the task that is already assigned may be assigned again. Otherwise the task\nmust be first unassign and only then assign again. (Default: `true`)"}}},"DraftVariableValue":{"type":"object","properties":{"value":{"type":"string"}}},"VariableResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftVariableValue"},"tenantId":{"type":"string"}}},"FormResponse":{"type":"object","properties":{"id":{"type":"string"},"processDefinitionKey":{"type":"string"},"title":{"type":"string"},"schema":{"type":"string"},"tenantId":{"type":"string"}}}},"securitySchemes":{"cookie":{"type":"apiKey","name":"TASKLIST-SESSION","in":"cookie"},"bearer-key":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} -------------------------------------------------------------------------------- /extension/generated/tasklist-8.4.0.json: -------------------------------------------------------------------------------- 1 | {"openapi":"3.0.1","info":{"title":"Tasklist webapp API","description":"Tasklist is a ready-to-use API application to rapidly implement business processes alongside user tasks in Zeebe.","contact":{"url":"https://www.camunda.com"},"license":{"name":"License","url":"https://docs.camunda.io/docs/reference/licenses/"},"version":"v1"},"servers":[{"url":"tasklistUrl","description":"Generated server url"}],"security":[{"cookie":[],"bearer-key":[]}],"tags":[{"name":"Form","description":"API to query forms"},{"name":"Variables","description":"API to query variables"},{"name":"Task","description":"API to query and manage tasks"}],"paths":{"/v1/tasks/{taskId}/variables":{"post":{"tags":["Task"],"summary":"Saves draft variables for a task.","description":"This operation validates the task and draft variables, deletes existing draft variables for the task, and then checks for new draft variables. If a new variable's `name` matches an existing one but the `value` differs, it is saved. In case of duplicate draft variable names, the last variable's value is kept.","operationId":"saveDraftTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveVariablesRequest"}}},"required":true},"responses":{"204":{"description":"On success returned"},"400":{"description":"An error is returned when the task is not active (not in the `CREATED` state).
An error is returned if the task was not claimed (assigned) before, except the case when JWT authentication token used.
An error is returned if the task is not assigned to the current user, except the case when JWT authentication token used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"An error is returned if an unexpected error occurs while persisting draft task variables.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/variables/search":{"post":{"tags":["Task"],"summary":"Returns the list of task variables","description":"This method returns a list of task variables for the specified `taskId` and `variableName`.
If the request body is not provided or if the `variableNames` parameter in the request is empty, all variables associated with the task will be returned.","operationId":"searchTaskVariables","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariablesSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VariableSearchResponse"}}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/search":{"post":{"tags":["Task"],"summary":"Returns the list of tasks that satisfy search request params","description":"Returns the list of tasks that satisfy search request params.
NOTE: Only one of `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]`search options must be present in request.","operationId":"searchTasks","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSearchRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSearchResponse"}}}}},"400":{"description":"An error is returned when more than one search parameters among `[searchAfter, searchAfterOrEqual, searchBefore, searchBeforeOrEqual]` are present in request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/unassign":{"patch":{"tags":["Task"],"summary":"Unassign a task with provided id. Returns the task.","description":"Unassign a task with `taskId`.","operationId":"unassignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/complete":{"patch":{"tags":["Task"],"summary":"Complete a task with taskId and optional variables. Returns the task.","description":"Complete a task with `taskId` and optional `variables`","operationId":"completeTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCompleteRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned if the task was not claimed (assigned) before.
An error is returned if the task is not assigned to the current user.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}/assign":{"patch":{"tags":["Task"],"summary":"Assign a task with id to assignee. Returns the task.","description":"Assign a task with `taskId` to `assignee` or the active user.","operationId":"assignTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"When using REST API with JWT authentication token following request body parameters may be used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAssignRequest"}}}},"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"400":{"description":"An error is returned when the task is not active (not in the CREATED state).
An error is returned when task was already assigned, except the case when JWT authentication token used and `allowOverrideAssignment = true`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"An error is returned when user doesn't have the permission to assign another user to this task.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/variables/{variableId}":{"get":{"tags":["Variables"],"summary":"Get the variable details by variable id.","operationId":"getVariableById","parameters":[{"name":"variableId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariableResponse"}}}},"404":{"description":"An error is returned when the variable with the `variableId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/tasks/{taskId}":{"get":{"tags":["Task"],"summary":"Get one task by id. Returns task or error when task does not exist.","operationId":"getTaskById","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResponse"}}}},"404":{"description":"An error is returned when the task with the `taskId` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/forms/{formId}":{"get":{"tags":["Form"],"summary":"Get the form details by form id and processDefinitionKey.","description":"Get the form details by `formId` and `processDefinitionKey` required query param. The `version` query param is optional and is used only for deployed forms (if empty, it retrieves the highest version).","operationId":"getForm","parameters":[{"name":"formId","in":"path","required":true,"schema":{"type":"string"}},{"name":"processDefinitionKey","in":"query","required":true,"schema":{"type":"string"}},{"name":"version","in":"query","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"On success returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"404":{"description":"An error is returned when the form with the `formId` and `processDefinitionKey` is not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"},"instance":{"type":"string"}}},"SaveVariablesRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"VariableInputDTO":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"VariablesSearchRequest":{"type":"object","properties":{"variableNames":{"type":"array","items":{"type":"string"}}}},"DraftSearchVariableValue":{"type":"object","properties":{"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"}}},"VariableSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"isValueTruncated":{"type":"boolean"},"previewValue":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftSearchVariableValue"}}},"DateFilter":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"IncludeVariable":{"type":"object","properties":{"name":{"type":"string"}}},"TaskByVariables":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"operator":{"type":"string","enum":["eq"]}}},"TaskOrderBy":{"type":"object","properties":{"field":{"type":"string","enum":["completionTime","creationTime","followUpDate","dueDate"]},"order":{"type":"string","enum":["ASC","DESC"]}}},"TaskSearchRequest":{"type":"object","properties":{"state":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"assigned":{"type":"boolean"},"assignee":{"type":"string"},"taskDefinitionId":{"type":"string"},"candidateGroup":{"type":"string"},"candidateUser":{"type":"string"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"pageSize":{"type":"integer","format":"int32"},"followUpDate":{"$ref":"#/components/schemas/DateFilter"},"dueDate":{"$ref":"#/components/schemas/DateFilter"},"taskVariables":{"type":"array","items":{"$ref":"#/components/schemas/TaskByVariables"}},"tenantIds":{"type":"array","items":{"type":"string"}},"sort":{"type":"array","items":{"$ref":"#/components/schemas/TaskOrderBy"}},"searchAfter":{"type":"array","items":{"type":"string"}},"searchAfterOrEqual":{"type":"array","items":{"type":"string"}},"searchBefore":{"type":"array","items":{"type":"string"}},"searchBeforeOrEqual":{"type":"array","items":{"type":"string"}},"includeVariables":{"type":"array","items":{"$ref":"#/components/schemas/IncludeVariable"}}}},"TaskSearchResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"sortValues":{"type":"array","items":{"type":"string"}},"isFirst":{"type":"boolean"},"formKey":{"type":"string"},"formId":{"type":"string"},"formVersion":{"type":"integer","format":"int64"},"isFormEmbedded":{"type":"boolean"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}},"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableSearchResponse"}}}},"TaskResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"taskDefinitionId":{"type":"string"},"processName":{"type":"string"},"creationDate":{"type":"string"},"completionDate":{"type":"string"},"assignee":{"type":"string"},"taskState":{"type":"string","enum":["CREATED","COMPLETED","CANCELED"]},"formKey":{"type":"string"},"formId":{"type":"string"},"formVersion":{"type":"integer","format":"int64"},"isFormEmbedded":{"type":"boolean"},"processDefinitionKey":{"type":"string"},"processInstanceKey":{"type":"string"},"tenantId":{"type":"string"},"dueDate":{"type":"string","format":"date-time"},"followUpDate":{"type":"string","format":"date-time"},"candidateGroups":{"type":"array","items":{"type":"string"}},"candidateUsers":{"type":"array","items":{"type":"string"}}}},"TaskCompleteRequest":{"type":"object","properties":{"variables":{"type":"array","items":{"$ref":"#/components/schemas/VariableInputDTO"}}}},"TaskAssignRequest":{"type":"object","properties":{"assignee":{"type":"string","description":"When using a JWT token, the assignee parameter is NOT optional when called directly from the API.\nThe system will not be able to detect the assignee from the JWT token, therefore the assignee parameter needs to be\nexplicitly passed in this instance."},"allowOverrideAssignment":{"type":"boolean","description":"When `true` the task that is already assigned may be assigned again. Otherwise the task\nmust be first unassign and only then assign again. (Default: `true`)"}}},"DraftVariableValue":{"type":"object","properties":{"value":{"type":"string"}}},"VariableResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"value":{"type":"string"},"draft":{"$ref":"#/components/schemas/DraftVariableValue"},"tenantId":{"type":"string"}}},"FormResponse":{"type":"object","properties":{"id":{"type":"string"},"processDefinitionKey":{"type":"string"},"title":{"type":"string"},"schema":{"type":"string"},"version":{"type":"integer","format":"int64"},"tenantId":{"type":"string"},"isDeleted":{"type":"boolean"}}}},"securitySchemes":{"cookie":{"type":"apiKey","name":"TASKLIST-SESSION","in":"cookie"},"bearer-key":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.camunda.community 6 | community-hub-release-parent 7 | 2.1.0 8 | 9 | io.camunda 10 | tasklist-client-root 11 | 8.8.0-SNAPSHOT 12 | pom 13 | Camunda Tasklist Client Root 14 | 15 | 16 | Apache License 17 | 18 | 19 | 20 | extension 21 | example 22 | 23 | 24 | scm:git:git@github.com:camunda-community-hub/camunda-tasklist-client-java.git 25 | scm:git:git@github.com:camunda-community-hub/camunda-tasklist-client-java.git 26 | HEAD 27 | https://github.com/camunda-community-hub/camunda-tasklist-client-java 28 | 29 | 30 | UTF-8 31 | ${project.build.sourceEncoding} 32 | 17 33 | ${version.java} 34 | ${java.version} 35 | 4.0.1 36 | 1.9.4 37 | 2.2.0 38 | 2.20.1 39 | 3.15.0 40 | 3.6.2 41 | 1.0.0 42 | 3.1.4 43 | 3.4.0 44 | 3.6.1 45 | 3.5.4 46 | 3.1.0 47 | 3.14.1 48 | 0.11.3 49 | 3.9.0 50 | 3.21.0 51 | 3.8.0 52 | 3.3.1 53 | 2.0.0 54 | 3.4.0 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-dependencies 61 | ${spring-boot.version} 62 | pom 63 | import 64 | 65 | 66 | io.camunda 67 | zeebe-bom 68 | 8.8.8 69 | pom 70 | import 71 | 72 | 73 | io.camunda 74 | camunda-tasklist-client-java 75 | ${project.version} 76 | 77 | 78 | io.camunda.spring 79 | spring-boot-starter-camunda-tasklist 80 | ${project.version} 81 | 82 | 83 | com.fasterxml.jackson 84 | jackson-bom 85 | ${jackson.version} 86 | pom 87 | import 88 | 89 | 90 | com.google.code.findbugs 91 | jsr305 92 | 3.0.2 93 | 94 | 95 | org.openapitools 96 | jackson-databind-nullable 97 | 0.2.8 98 | 99 | 100 | io.camunda 101 | camunda-tasklist-client-generated 102 | ${project.version} 103 | 104 | 105 | com.google.protobuf 106 | protobuf-bom 107 | 4.33.2 108 | pom 109 | import 110 | 111 | 112 | com.google.guava 113 | guava 114 | 33.5.0-jre 115 | 116 | 117 | com.google.errorprone 118 | error_prone_annotations 119 | 2.45.0 120 | 121 | 122 | org.wiremock 123 | wiremock-standalone 124 | 3.13.2 125 | 126 | 127 | com.auth0 128 | java-jwt 129 | 4.5.0 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | org.openapitools 138 | openapi-generator-maven-plugin 139 | 7.18.0 140 | 141 | 142 | org.sonatype.plugins 143 | nexus-staging-maven-plugin 144 | 1.7.0 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-source-plugin 149 | ${plugin.version.maven-source-plugin} 150 | 151 | 152 | attach-sources 153 | 154 | jar 155 | 156 | 157 | 158 | 159 | 160 | org.apache.maven.plugins 161 | maven-assembly-plugin 162 | ${plugin.version.maven-assembly-plugin} 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-resources-plugin 167 | ${plugin.version.maven-resources-plugin} 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-site-plugin 172 | ${plugin.version.maven-site-plugin} 173 | 174 | 175 | org.apache.maven.plugins 176 | maven-dependency-plugin 177 | ${plugin.version.maven-depdendency-plugin} 178 | 179 | 180 | analyze 181 | 182 | analyze-only 183 | 184 | 185 | true 186 | 187 | 188 | 189 | 190 | 191 | org.graalvm.buildtools 192 | native-maven-plugin 193 | ${plugin.version.native-maven-plugin} 194 | 195 | 196 | org.apache.maven.plugins 197 | maven-compiler-plugin 198 | ${plugin.version.maven-compiler-plugin} 199 | 200 | 201 | org.springframework.boot 202 | spring-boot-maven-plugin 203 | ${version.spring-boot} 204 | 205 | 206 | org.apache.maven.plugins 207 | maven-install-plugin 208 | ${plugin.version.maven-install-plugin} 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-shade-plugin 213 | ${plugin.version.maven-shade-plugin} 214 | 215 | 216 | org.apache.maven.plugins 217 | maven-surefire-plugin 218 | ${plugin.version.maven-surefire-plugin} 219 | 220 | true 221 | 222 | 223 | 224 | org.apache.maven.plugins 225 | maven-enforcer-plugin 226 | ${plugin.version.maven-enforcer-plugin} 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | enforce 238 | 239 | 240 | 241 | 242 | 243 | com.diffplug.spotless 244 | spotless-maven-plugin 245 | ${plugin.version.spotless-maven-plugin} 246 | 247 | 248 | 249 | 250 | 251 | 252 | 2 253 | false 254 | true 255 | false 256 | 257 | 258 | 259 | 260 | **/*.md 261 | 262 | 263 | 264 | 265 | 266 | com.google.cloud.functions 267 | function-maven-plugin 268 | ${plugin.version.function-maven-plugin} 269 | 270 | 271 | org.apache.maven.plugins 272 | maven-release-plugin 273 | ${plugin.version.maven-release-plugin} 274 | 275 | 276 | com.github.eirslett 277 | frontend-maven-plugin 278 | ${plugin.version.frontend-maven-plugin} 279 | 280 | 281 | org.eclipse.m2e 282 | lifecycle-mapping 283 | 1.0.0 284 | 285 | 286 | 287 | 288 | 289 | org.apache.maven.plugins 290 | maven-enforcer-plugin 291 | [1.0.0,) 292 | 293 | enforce 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | org.apache.maven.plugins 309 | maven-dependency-plugin 310 | 311 | 312 | org.apache.maven.plugins 313 | maven-enforcer-plugin 314 | 315 | 316 | org.apache.maven.plugins 317 | maven-source-plugin 318 | 319 | 320 | com.diffplug.spotless 321 | spotless-maven-plugin 322 | 323 | 324 | 325 | 326 | 327 | 328 | autoFormat 329 | 330 | true 331 | 332 | 333 | 334 | 335 | com.diffplug.spotless 336 | spotless-maven-plugin 337 | 338 | 339 | spotless-format 340 | 341 | apply 342 | 343 | process-sources 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | checkFormat 353 | 354 | 355 | 356 | com.diffplug.spotless 357 | spotless-maven-plugin 358 | 359 | 360 | spotless-check 361 | 362 | check 363 | 364 | validate 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | --------------------------------------------------------------------------------