Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ea4bd26
TMDPC-2 and TMDPC-3 initial changes (#1550)
skandala-cgi Jan 21, 2026
e74cbe5
Tmdpc 6,7,8 (#1552)
skandala-cgi Jan 21, 2026
a3e7556
Tmdpc 9 (#1553)
skandala-cgi Jan 23, 2026
7171aed
Tmdpc 5 task creation idempotency (#1555)
chrisbellingham1-hmcts Jan 26, 2026
0828943
TMDPC-5 Changes (#1556)
skandala-cgi Jan 27, 2026
b126daf
TMDPC-12: Openapi spec for POST /tasks/terminate (#1557)
chrisbellingham1-hmcts Jan 27, 2026
ab6244d
TMDPC-17: Adapt POST /task/id/claim for api-first approach (#1561)
chrisbellingham1-hmcts Jan 30, 2026
35f0210
Task Termination TMDPC-13 (#1563)
skandala-cgi Feb 3, 2026
b90947d
TMDPC-20 initial commit (#1566)
skandala-cgi Feb 6, 2026
3a1a5a4
TMDPC-21 initial commit (#1570)
skandala-cgi Feb 10, 2026
a4307f7
Ignore code coverage for poc (#1577)
chrisbellingham1-hmcts Feb 17, 2026
0ae3ad3
TMDPC-25: Get Tasks (#1583)
chrisbellingham1-hmcts Mar 3, 2026
bf32ab8
TMDPC-28: Remove unnecessary required constraint from Task Creation A…
chrisbellingham1-hmcts Mar 12, 2026
229d2ff
Sync master (#1597)
chrisbellingham1-hmcts Mar 12, 2026
1ca3ff4
TMDPC-30: Remove is_camunda_task field in favour of external_task_id …
chrisbellingham1-hmcts Mar 25, 2026
a97ab73
Tmdpc 29 search for completable (#1603)
chrisbellingham1-hmcts Mar 25, 2026
1a56ccc
NO-JIRA: Compact poc migrations into single script and namespace to a…
chrisbellingham1-hmcts Apr 7, 2026
df65ecb
TMDPC-34: Add schema rollback script for demo testing (#1620)
chrisbellingham1-hmcts Apr 7, 2026
120e041
TMDPC-32 initial commit (#1621)
skandala-cgi Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ bin/main/application.yaml

/pacts/
/bin/

.openapi-generator-ignore
.openapi-generator/

41 changes: 41 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ plugins {
id 'au.com.dius.pact' version '4.6.17'
id 'net.serenity-bdd.serenity-gradle-plugin' version '4.2.33'
id 'org.flywaydb.flyway' version '9.3.0'
id "org.openapi.generator" version "7.2.0"
}

group = 'uk.gov.hmcts.reform'
Expand Down Expand Up @@ -118,6 +119,45 @@ task integration(type: Test) {
classpath = sourceSets.integrationTest.runtimeClasspath
failFast = true
}
sourceSets {
main {
java {
srcDir("$buildDir/open-api/generated/")
}

}
}
tasks.withType(Checkstyle).configureEach {
exclude("**/open-api/generated/**")
exclude("**/poc/**")
}

openApiGenerate {
generatorName = "spring"
inputSpec = file("$projectDir/src/main/resources/api-specs/openapi.yaml").path
outputDir = file("$buildDir/open-api/generated").path
apiPackage = "uk.gov.hmcts.reform.wataskmanagementapi.poc.api"
modelPackage = "uk.gov.hmcts.reform.wataskmanagementapi.poc.request"
importMappings = [
TaskResource: "uk.gov.hmcts.reform.wataskmanagementapi.entity.TaskResource",
PermissionTypes: "uk.gov.hmcts.reform.wataskmanagementapi.auth.permission.entities.PermissionTypes",
SecurityClassification: "uk.gov.hmcts.reform.wataskmanagementapi.cft.enums.SecurityClassification" ,
TaskSystem: "uk.gov.hmcts.reform.wataskmanagementapi.cft.enums.TaskSystem",
ExecutionType: "uk.gov.hmcts.reform.wataskmanagementapi.cft.enums.ExecutionType"
]


configOptions = [
interfaceOnly: "true",
useSpringBoot3: "true",
openApiNullable : "false",
useBeanValidation: "true"
]
}

tasks.named("compileJava") {
dependsOn 'openApiGenerate'
}

build.dependsOn integration

Expand Down Expand Up @@ -206,6 +246,7 @@ sonarqube {
"src/main/java/uk/gov/hmcts/reform/wataskmanagementapi/clients/**," +
"src/main/java/uk/gov/hmcts/reform/wataskmanagementapi/domain/entities/search/parameter/**," +
"src/main/java/uk/gov/hmcts/reform/wataskmanagementapi/cft/replicarepository/**"
property "sonar.coverage.exclusions", "src/main/java/**"
property "sonar.cpd.exclusions", "src/main/java/uk/gov/hmcts/reform/wataskmanagementapi/services/calendar/**," +
"src/main/java/uk/gov/hmcts/reform/wataskmanagementapi/controllers/TaskActionsController.java,"
}
Expand Down
2 changes: 1 addition & 1 deletion src/functionalTest/resources/application-functional.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ spring:
default_schema: cft_task_db
config:
allowedJurisdictions: ${ALLOWED_JURISDICTIONS:wa,ia,sscs,civil,PUBLICLAW,PRIVATELAW,EMPLOYMENT,ST_CIC}
allowedCaseTypes: ${ALLOWED_CASE_TYPES:asylum,wacasetype,sscs,civil,generalapplication,CARE_SUPERVISION_EPO,PRLAPPS,ET_EnglandWales,ET_EnglandWales_Listings,ET_EnglandWales_Multiple,ET_Scotland,ET_Scotland_Listings,ET_Scotland_Multiple,ET_Admin,privatelaw_exceptionrecord,benefit,CriminalInjuriesCompensation}
allowedCaseTypes: ${ALLOWED_CASE_TYPES:asylum,wacasetype,sscs,civil,generalapplication,CARE_SUPERVISION_EPO,PRLAPPS,ET_EnglandWales,ET_EnglandWales_Listings,ET_EnglandWales_Multiple,ET_Scotland,ET_Scotland_Listings,ET_Scotland_Multiple,ET_Admin,privatelaw_exceptionrecord,benefit}
taskMandatoryFieldCheckEnabled: ${MANDATORY_TASK_FIELD_CHECK_ENABLED:true}
taskMandatoryFields: ${MANDATORY_TASK_FIELDS:taskName,taskId,taskType,dueDateTime,state,securityClassification,title,majorPriority,minorPriority,executionTypeCode,caseId,caseTypeId,caseCategory,caseName,jurisdiction,region,location,created,roleCategory,workTypeResource}
initiationRequestRequiredFields: ${INITIATION_REQUEST_REQUIRED_FIELDS:name,taskType,caseId}
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/integrationTest/resources/application-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ logging:
uk.gov.hmcts.reform: debug
config:
allowedJurisdictions: ${ALLOWED_JURISDICTIONS:wa,ia,sscs,civil,PUBLICLAW,PRIVATELAW,EMPLOYMENT,ST_CIC}
allowedCaseTypes: ${ALLOWED_CASE_TYPES:asylum,wacasetype,sscs,civil,generalapplication,CARE_SUPERVISION_EPO,PRLAPPS,ET_EnglandWales,ET_EnglandWales_Listings,ET_EnglandWales_Multiple,ET_Scotland,ET_Scotland_Listings,ET_Scotland_Multiple,ET_Admin,privatelaw_exceptionrecord,benefit,CriminalInjuriesCompensation}
allowedCaseTypes: ${ALLOWED_CASE_TYPES:asylum,wacasetype,sscs,civil,generalapplication,CARE_SUPERVISION_EPO,PRLAPPS,ET_EnglandWales,ET_EnglandWales_Listings,ET_EnglandWales_Multiple,ET_Scotland,ET_Scotland_Listings,ET_Scotland_Multiple,ET_Admin,privatelaw_exceptionrecord,benefit}
taskMandatoryFieldCheckEnabled: ${MANDATORY_TASK_FIELD_CHECK_ENABLED:true}
taskMandatoryFields: ${MANDATORY_TASK_FIELDS:taskName,taskId,taskType,dueDateTime,state,securityClassification,title,majorPriority,minorPriority,executionTypeCode,caseId,caseTypeId,caseCategory,caseName,jurisdiction,region,location,created,roleCategory,workTypeResource}
initiationRequestRequiredFields: ${INITIATION_REQUEST_REQUIRED_FIELDS:name,taskType,caseId}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.hmcts.reform.wataskmanagementapi.auth.permission.entities;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Optional;
Expand Down Expand Up @@ -45,6 +46,12 @@ public String value() {
return value;
}

@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static PermissionTypes fromJson(String value) {
return from(value)
.orElseThrow(() -> new IllegalArgumentException("Unknown PermissionTypes: " + value));
}

public String taskRoleResourceField() {
return taskRoleResourceField;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package uk.gov.hmcts.reform.wataskmanagementapi.cft.enums;

import com.fasterxml.jackson.annotation.JsonCreator;

import java.util.Optional;

import static java.util.Arrays.stream;
Expand Down Expand Up @@ -52,4 +54,18 @@ public String getName() {
public String getDescription() {
return description;
}

@JsonCreator
public static ExecutionType fromJson(String value) {
return stream(values())
.filter(v ->
v.getValue().equalsIgnoreCase(value)
|| v.getName().equalsIgnoreCase(value)
|| v.name().equalsIgnoreCase(value)
)
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException("Unknown ExecutionType: " + value)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.formLogin(Customizer.withDefaults())
.logout(Customizer.withDefaults())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.GET, "/tasks").permitAll()
.requestMatchers(HttpMethod.POST, "/tasks").permitAll()
.requestMatchers(HttpMethod.POST, "/tasks/terminate").permitAll()
.requestMatchers(HttpMethod.PUT, "/tasks/reconfigure").permitAll()
.requestMatchers("/task-configuration/**").permitAll()
.requestMatchers(HttpMethod.POST, "/task/{\\\\d+}").permitAll()
.requestMatchers(HttpMethod.POST, "/task/{\\\\d+}/initiation").permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
@Slf4j
@ControllerAdvice(basePackages = {
"uk.gov.hmcts.reform.wataskmanagementapi.controllers",
"uk.gov.hmcts.reform.wataskmanagementapi.taskconfiguration.controllers"
"uk.gov.hmcts.reform.wataskmanagementapi.taskconfiguration.controllers",
"uk.gov.hmcts.reform.wataskmanagementapi.poc.controller"
})
@RequestMapping(produces = APPLICATION_PROBLEM_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
@SuppressWarnings({"PMD.ExcessiveImports", "PMD.DataflowAnomalyAnalysis",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.hmcts.reform.wataskmanagementapi.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -16,6 +17,7 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.JdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Type;
Expand Down Expand Up @@ -51,6 +53,8 @@ public class TaskResource implements Serializable {
@EqualsAndHashCode.Include()
@Schema(name = "task_id")
private String taskId;
@Schema(name = "external_task_id")
private String externalTaskId;
@Schema(name = "task_name")
private String taskName;
@Schema(name = "task_type")
Expand Down Expand Up @@ -579,6 +583,15 @@ public void setCaseDeletionTimestamp(OffsetDateTime caseDeletionTimestamp) {
this.caseDeletionTimestamp = caseDeletionTimestamp;
}

public void setExternalTaskId(String externalTaskId) {
this.externalTaskId = externalTaskId;
}

@JsonIgnore
public boolean isCamundaTask() {
return StringUtils.isBlank(this.externalTaskId);
}

public TaskResource(String taskId,
String caseId,
String jurisdiction,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uk.gov.hmcts.reform.wataskmanagementapi.exceptions;

import lombok.Getter;

import java.util.UUID;

@Getter
public class TaskSecondaryKeyConflictException extends RuntimeException {

private static final long serialVersionUID = 4957223859157220692L;

private final UUID externalTaskId;
private final String caseTypeId;

public TaskSecondaryKeyConflictException(UUID externalTaskId, String caseTypeId, Throwable cause) {
super(buildMessage(externalTaskId, caseTypeId), cause);
this.externalTaskId = externalTaskId;
this.caseTypeId = caseTypeId;
}

private static String buildMessage(UUID externalTaskId, String caseTypeId) {
return String.format(
"Task already exists for external_task_id=%s, case_type_id=%s.",
externalTaskId,
caseTypeId
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package uk.gov.hmcts.reform.wataskmanagementapi.poc.controller;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import uk.gov.hmcts.reform.wataskmanagementapi.auth.restrict.ClientAccessControlService;
import uk.gov.hmcts.reform.wataskmanagementapi.controllers.BaseController;
import uk.gov.hmcts.reform.wataskmanagementapi.entity.TaskResource;
import uk.gov.hmcts.reform.wataskmanagementapi.exceptions.v2.GenericForbiddenException;
import uk.gov.hmcts.reform.wataskmanagementapi.exceptions.v2.InvalidRequestException;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.api.TasksApi;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.mapper.GetTasksResponseMapper;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.CreateTaskRequest;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.GetTasksResponse;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.TaskReconfigureRequest;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.TaskReconfigureResponse;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.GetTaskResponseItem;
import uk.gov.hmcts.reform.wataskmanagementapi.poc.request.TerminateTasksRequest;
import uk.gov.hmcts.reform.wataskmanagementapi.services.TaskManagementService;

import java.util.List;

import static uk.gov.hmcts.reform.wataskmanagementapi.exceptions.v2.enums.ErrorMessages.GENERIC_FORBIDDEN_ERROR;

@RequiredArgsConstructor
@Slf4j
@RestController

public class TaskPocController extends BaseController implements TasksApi {

private final TaskManagementService taskManagementService;
private final ClientAccessControlService clientAccessControlService;
private final GetTasksResponseMapper responseMapper;

@Override
public ResponseEntity<GetTasksResponse> getTasks(String serviceAuthorization,
String caseId,
List<String> taskTypes) {
checkExclusiveAccess(serviceAuthorization);
validateGetTasksFilters(caseId, taskTypes);

List<TaskResource> tasks = taskManagementService.getTasks(caseId, taskTypes);
List<GetTaskResponseItem> taskItems = responseMapper.mapToGetTaskItems(tasks);

return ResponseEntity
.ok()
.cacheControl(CacheControl.noCache())
.body(new GetTasksResponse(taskItems, (long) taskItems.size()));
}

@Override
@PostMapping(value = "/tasks", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<uk.gov.hmcts.reform.wataskmanagementapi.entity.TaskResource> createTask(
@RequestHeader(value = "ServiceAuthorization", required = true) String serviceAuthorization,
@Parameter(name = "CreateTaskRequest", description = "", required = true) @RequestBody CreateTaskRequest createTaskRequest
) {
checkExclusiveAccess(serviceAuthorization);
TaskResource savedTask = taskManagementService.addTask(createTaskRequest.getTask());
taskManagementService.updateTaskIndex(savedTask.getTaskId());
return ResponseEntity
.status(HttpStatus.CREATED)
.cacheControl(CacheControl.noCache())
.body(savedTask);
}

@PostMapping(value = "/tasks/terminate", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> terminateTasks(
@NotNull @RequestHeader(value = "ServiceAuthorization", required = true) String serviceAuthorization,
@Parameter(name = "TerminateTasksRequest", description = "", required = true) @Valid @RequestBody TerminateTasksRequest terminateTasksRequest
) {
checkExclusiveAccess(serviceAuthorization);

taskManagementService.terminateTasks(terminateTasksRequest);
return ResponseEntity
.noContent()
.cacheControl(CacheControl.noCache())
.build();
}

@PutMapping(value = "/tasks/reconfigure", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<TaskReconfigureResponse> reconfigureTasks(
@NotNull @Parameter(name = "ServiceAuthorization", description = "Service-to-service authorization token", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "ServiceAuthorization", required = true) String serviceAuthorization,
@Parameter(name = "TaskReconfigureRequest", description = "", required = true) @Valid @RequestBody TaskReconfigureRequest taskReconfigureRequest
) {
checkExclusiveAccess(serviceAuthorization);
TaskReconfigureResponse response = taskManagementService.reconfigureTasks(taskReconfigureRequest);
return ResponseEntity
.ok()
.cacheControl(CacheControl.noCache())
.body(response);

}

private void checkExclusiveAccess(String serviceAuthorization) {
if (!clientAccessControlService.hasExclusiveAccess(serviceAuthorization)) {
throw new GenericForbiddenException(GENERIC_FORBIDDEN_ERROR);
}
}

private void validateGetTasksFilters(String caseId, List<String> taskTypes) {
if (caseId == null && taskTypes == null) {
throw new InvalidRequestException("At least one query/filter parameter must be included in the request.");
}

if (caseId != null && StringUtils.isBlank(caseId)) {
throw new InvalidRequestException("case_id cannot be blank");
}

if (taskTypes != null && taskTypes.isEmpty()) {
throw new InvalidRequestException("task_types cannot be empty");
}

if (taskTypes != null && taskTypes.stream().anyMatch(StringUtils::isBlank)) {
throw new InvalidRequestException("task_types list cannot contain a blank item");
}
}

}

Loading