Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement interfaces and dtos user task search #19625

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

nathansandi
Copy link
Contributor

@nathansandi nathansandi commented Jun 21, 2024

Description

This is the first of 2 PRs of the implementation of Search User Task API. The goal of this PR is prepare the interfaces and implementation, plus the controller for User Task Search

The continuation of the implementation:

  1. feat: Integrate variable filter over user task services #19691 - Integrate the Variables on the User Task Search Service and Controller

The PRs were divided in order to make it easier the review process.

Test example:

{
    "filter": {
        "taskState":"CREATED",
        "processInstanceKey": 2251799813685379,
    },
    "sort":[
        {
            "field":"creationTime",
            "order":"desc"
        }
    ],
    "page": {
        "limit": 20
    }
}

Related issues

closes ##19211

@github-actions github-actions bot added the component/zeebe Related to the Zeebe component/team label Jun 21, 2024
@nathansandi nathansandi changed the title feat: implement interfaces and dtos user task search [wip] feat: implement interfaces and dtos user task search Jun 25, 2024
@nathansandi nathansandi marked this pull request as ready for review June 25, 2024 15:12
@nathansandi nathansandi self-assigned this Jun 26, 2024
## Description

This is the second of three PRs for the implementation of the Search
User Task API. The goal of this PR is to set up the mappers and the
controller for the User Task Context. The DTOs have been updated
according to the requirements identified in the previous PR.

The overall implementation is divided into three PRs to facilitate the
review process:

1. [PR 1] - #19625 - Prepare the
interfaces and implementation, and set up the preparation for the API
Mapper and Controller for the User Task Context.
2. **PR 2 (this PR)** - Prepare the Mappers and User Task Controller.
3. [PR 3] - #19691 - Integrate
the Variables on the User Task Search Service and Controller.

From PR 2, it will be possible to conduct end-to-end tests using
builders or the API call.

From this PR is already possible to test the User Task Search only over
the API

For example:
```
{
    "filter": {
        "taskState":"CREATED",
        "processInstanceKey": 2251799813685379,
    },
    "sort":[
        {
            "field":"creationTime",
            "order":"desc"
        }
    ],
    "page": {
        "limit": 20
    }
}
```


## Related issues

closes [#19211](#19211)
@github-actions github-actions bot added component/operate Related to the Operate component/team component/tasklist Related to the Tasklist component/team labels Jun 26, 2024
@nathansandi nathansandi requested a review from tmetzke June 26, 2024 13:54
consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserTaskSearchQueryResponse> searchUserTasks(
@RequestBody(required = false) final UserTaskSearchQueryRequest query) {
return SearchQueryRequestMapper.toUserTaskQuery(query == null? new UserTaskSearchQueryRequest() : query)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 You could also allow to pass a null into the toUserTaskQuery method and handle it there gracefully. Just as an alternative.

@RequestBody(required = false) final UserTaskSearchQueryRequest query) {
return SearchQueryRequestMapper.toUserTaskQuery(query == null? new UserTaskSearchQueryRequest() : query)
.fold(
(Function<? super UserTaskQuery, ? extends ResponseEntity<UserTaskSearchQueryResponse>>)
Copy link
Member

@tmetzke tmetzke Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Why is this necessary? Does the compiler fail otherwise? Just wondering as it looks a bit odd. I think the controller method should rather return ResponseEntity<Object> because it can return a UserTaskSearchQueryResponse or a ProblemDetail inside the ResponseEntity.

Copy link
Contributor Author

@nathansandi nathansandi Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The casting of the lambda in the fold method is necessary due to type inference limitations in Java generics, especially when dealing with Either types and lambdas. The fold method is designed to take two functions: one to handle the Left value and another to handle the Right value. Without the explicit cast, the compiler may not be able to infer the correct types for these functions.

However, the casting makes the code harder to read and understand. To avoid this, we can adjust the method signature to return ResponseEntity, which can encapsulate both UserTaskSearchQueryResponse and ProblemDetail.

I have no stronger opinion over the strategy here. :)

} catch (final Throwable e) {
final var problemDetail =
RestErrorMapper.createProblemDetail(
HttpStatus.BAD_REQUEST, e.getMessage(), "Failed to execute UserTask Search Query");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Is this really going to be a 400 response each time? At this point, can you still differentiate based on the error what went wrong? Will it rather be non-retriable errors, so 500 responses?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true, I believe 400 can be filtered by Validation Exception, and other ones can be handled by 500. I applied the suggestion over here 960e604

@nathansandi
Copy link
Contributor Author

nathansandi commented Jun 27, 2024

Thank you for the review @tmetzke , I have applied the most of the suggestions over this commit 960e604

@nathansandi nathansandi requested a review from tmetzke June 27, 2024 11:44
Comment on lines +53 to +73
// TODO move this to a shared utility module

public static <T> List<T> addValuesToList(final List<T> list, final List<T> values) {
final List<T> result;
if (list == null) {
result = Objects.requireNonNull(values);
} else {
result = new ArrayList<>(list);
result.addAll(values);
}
return result;
}

public static <T> List<T> collectValues(final T value, final T... values) {
final List<T> collectedValues = new ArrayList<>();
collectedValues.add(value);
if (values != null && values.length > 0) {
collectedValues.addAll(Arrays.asList(values));
}
return collectedValues;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 There already is a CollectionUtil in the Java client that you should be able to use instead.

Comment on lines +165 to +169
// Return an Either.Left with a ProblemDetail for unknown sortBy field
final ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST, "Unknown sortBy field: " + field);
return Either.left(problemDetail);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 How about we collect input validation errors like we do in RequestMapper.toUserTaskUpdateRequest? If we return on the first error directly like you do now, users will only receive this input as invalid. If the order is also invalid, they will only learn that in a follow-up request after correcting the field name.


public static Either<ProblemDetail, List<UserTaskItem>> toUserTasks(
final List<UserTaskEntity> tasks) {
return Either.right(
Copy link
Member

@tmetzke tmetzke Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 This never returns a Either.Left so you can simply return the success type and simplify the code.

tasks.stream().map(SearchQueryResponseMapper::toUserTask).map(Either::get).toList());
}

public static Either<ProblemDetail, UserTaskItem> toUserTask(final UserTaskEntity t) {
Copy link
Member

@tmetzke tmetzke Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 This never returns a Either.Left so you can simply return the success type and simplify the code.

Comment on lines +156 to +158
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_PROBLEM_JSON))
.body(problemDetail);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 You can use RestErrorMapper.mapProblemToResponse for this.

Comment on lines +165 to +167
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_PROBLEM_JSON))
.body(problemDetail);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 You can use RestErrorMapper.mapProblemToResponse for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/operate Related to the Operate component/team component/tasklist Related to the Tasklist component/team component/zeebe Related to the Zeebe component/team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants