Skip to content

Commit 0ce8449

Browse files
authored
Bael 7622: Bulk and Batch APi operations (#16733)
* bulk api * bulk and batch api * bulk and batch api refacotring * bulk and batch api impl * Refactor code with exception handling, generic fixes and other fix * refactor code * refactor code * refactor code * refactor code * restructure packaging * restructure code * add unit tests * refactor tests * refactor tests * refactor * refactor * http status change as per standards * fixed test cases * fix test case * refactoring * code review fixes * code review fixes * code review fix * code review fix * test fixed * optional code updated * refactored code based on suggestions
1 parent df5276d commit 0ce8449

File tree

16 files changed

+729
-0
lines changed

16 files changed

+729
-0
lines changed

spring-web-modules/spring-rest-http-3/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
<groupId>com.fasterxml.jackson.dataformat</groupId>
2525
<artifactId>jackson-dataformat-xml</artifactId>
2626
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter-validation</artifactId>
30+
</dependency>
2731
</dependencies>
2832

2933
<properties>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.baeldung.bulkandbatchapi.controller;
2+
3+
import com.baeldung.bulkandbatchapi.request.Address;
4+
import com.baeldung.bulkandbatchapi.service.AddressService;
5+
import com.baeldung.bulkandbatchapi.request.Customer;
6+
import com.baeldung.bulkandbatchapi.service.CustomerService;
7+
import com.baeldung.bulkandbatchapi.request.BatchRequest;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import jakarta.validation.Valid;
10+
import jakarta.validation.constraints.Size;
11+
import org.springframework.http.HttpMethod;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.validation.annotation.Validated;
15+
import org.springframework.web.bind.annotation.*;
16+
17+
import java.util.List;
18+
19+
@RestController
20+
@RequestMapping("/api")
21+
@Validated
22+
public class BatchController {
23+
24+
private final CustomerService customerService;
25+
private final AddressService addressService;
26+
private final ObjectMapper objectMapper;
27+
28+
public BatchController(CustomerService customerService, AddressService addressService, ObjectMapper objectMapper) {
29+
this.customerService = customerService;
30+
this.addressService = addressService;
31+
this.objectMapper = objectMapper;
32+
}
33+
34+
@PostMapping(path = "/batch")
35+
public ResponseEntity<String> batchUpdateCustomerWithAddress(@RequestBody @Valid @Size(min = 1, max = 20) List<BatchRequest> batchRequests) {
36+
batchRequests.forEach(batchRequest -> {
37+
if (batchRequest.getMethod().equals(HttpMethod.POST) && batchRequest.getRelativeUrl().equals("/address")) {
38+
addressService.createAddress(objectMapper.convertValue(batchRequest.getData(), Address.class));
39+
} else if (batchRequest.getMethod().equals(HttpMethod.PATCH) && batchRequest.getRelativeUrl().equals("/customer")) {
40+
customerService.updateCustomer(objectMapper.convertValue(batchRequest.getData(), Customer.class));
41+
}
42+
});
43+
44+
return new ResponseEntity<>("Batch update is processed", HttpStatus.OK);
45+
}
46+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.baeldung.bulkandbatchapi.controller;
2+
3+
import com.baeldung.bulkandbatchapi.request.BulkActionType;
4+
import com.baeldung.bulkandbatchapi.request.Customer;
5+
import com.baeldung.bulkandbatchapi.request.CustomerBulkRequest;
6+
import com.baeldung.bulkandbatchapi.response.BulkStatus;
7+
import com.baeldung.bulkandbatchapi.response.CustomerBulkResponse;
8+
import com.baeldung.bulkandbatchapi.service.CustomerService;
9+
import jakarta.validation.Valid;
10+
import jakarta.validation.constraints.Size;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.validation.annotation.Validated;
14+
import org.springframework.web.bind.annotation.*;
15+
16+
import java.util.*;
17+
import java.util.function.Function;
18+
19+
import static java.util.stream.Collectors.toList;
20+
21+
@RestController
22+
@RequestMapping("/api")
23+
@Validated
24+
public class BulkController {
25+
26+
private final CustomerService customerService;
27+
private final EnumMap<BulkActionType, Function<Customer, Optional<Customer>>> bulkActionFuncMap = new EnumMap<>(BulkActionType.class);
28+
29+
public BulkController(CustomerService customerService) {
30+
this.customerService = customerService;
31+
bulkActionFuncMap.put(BulkActionType.CREATE, customerService::createCustomer);
32+
bulkActionFuncMap.put(BulkActionType.UPDATE, customerService::updateCustomer);
33+
bulkActionFuncMap.put(BulkActionType.DELETE, customerService::deleteCustomer);
34+
}
35+
36+
@PostMapping(path = "/customers")
37+
public ResponseEntity<List<Customer>> createCustomers(@RequestHeader(value = "X-ActionType") String actionType, @RequestBody @Valid @Size(min = 1, max = 20) List<Customer> customers) {
38+
List<Customer> customerList = actionType.equals("bulk") ? customerService.createCustomers(customers) :
39+
Collections.singletonList(customerService.createCustomer(customers.get(0)).orElse(null));
40+
41+
return new ResponseEntity<>(customerList, HttpStatus.CREATED);
42+
}
43+
44+
@PostMapping(path = "/customers/bulk")
45+
public ResponseEntity<List<CustomerBulkResponse>> bulkProcessCustomers(@RequestBody @Valid @Size(min = 1, max = 20) List<CustomerBulkRequest> customerBulkRequests) {
46+
List<CustomerBulkResponse> customerBulkResponseList = new ArrayList<>();
47+
48+
customerBulkRequests.forEach(customerBulkRequest -> {
49+
List<Customer> customers = customerBulkRequest.getCustomers().stream()
50+
.map(bulkActionFuncMap.get(customerBulkRequest.getBulkActionType()))
51+
.filter(Optional::isPresent)
52+
.map(Optional::get)
53+
.collect(toList());
54+
55+
BulkStatus bulkStatus = getBulkStatus(customerBulkRequest.getCustomers(), customers);
56+
customerBulkResponseList.add(new CustomerBulkResponse(customers, customerBulkRequest.getBulkActionType(), bulkStatus));
57+
});
58+
59+
return new ResponseEntity<>(customerBulkResponseList, HttpStatus.MULTI_STATUS);
60+
}
61+
62+
private BulkStatus getBulkStatus(List<Customer> customersInRequest, List<Customer> customersProcessed) {
63+
if (!customersProcessed.isEmpty()) {
64+
return customersProcessed.size() == customersInRequest.size() ?
65+
BulkStatus.PROCESSED :
66+
BulkStatus.PARTIALLY_PROCESSED;
67+
}
68+
69+
return BulkStatus.NOT_PROCESSED;
70+
}
71+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.bulkandbatchapi.exception;
2+
3+
public class BatchException extends RuntimeException {
4+
public BatchException(String exceptionMsg) {
5+
super(exceptionMsg);
6+
}
7+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.bulkandbatchapi.exception;
2+
3+
import jakarta.servlet.http.HttpServletResponse;
4+
import jakarta.validation.ConstraintViolationException;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.web.bind.annotation.ControllerAdvice;
7+
import org.springframework.web.bind.annotation.ExceptionHandler;
8+
9+
import java.io.IOException;
10+
11+
@ControllerAdvice
12+
public class CustomErrorHandler {
13+
@ExceptionHandler(ConstraintViolationException.class)
14+
public void handleConstraintViolationException(ConstraintViolationException exception, HttpServletResponse httpServletResponse) throws IOException {
15+
httpServletResponse.sendError(HttpStatus.BAD_REQUEST.value(), exception.getMessage());
16+
}
17+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.baeldung.bulkandbatchapi.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
import java.io.Serializable;
6+
7+
public class Address implements Serializable {
8+
9+
private int id;
10+
11+
@NotBlank
12+
private String street;
13+
14+
@NotBlank
15+
private String city;
16+
17+
public Address() {
18+
}
19+
20+
public Address(int id, String street, String city) {
21+
this.id = id;
22+
this.street = street;
23+
this.city = city;
24+
}
25+
26+
public void setId(int id) {
27+
this.id = id;
28+
}
29+
30+
public String getStreet() {
31+
return street;
32+
}
33+
34+
public void setStreet(String street) {
35+
this.street = street;
36+
}
37+
38+
public String getCity() {
39+
return city;
40+
}
41+
42+
public void setCity(String city) {
43+
this.city = city;
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return "Address{" +
49+
"id=" + id +
50+
", street='" + street + '\'' +
51+
", city='" + city + '\'' +
52+
'}';
53+
}
54+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.bulkandbatchapi.request;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import jakarta.validation.constraints.NotNull;
5+
import org.springframework.http.HttpMethod;
6+
7+
public class BatchRequest {
8+
@NotNull
9+
private HttpMethod method;
10+
@NotNull
11+
private String relativeUrl;
12+
@NotNull
13+
private JsonNode data;
14+
15+
public JsonNode getData() {
16+
return data;
17+
}
18+
19+
public @NotNull String getRelativeUrl() {
20+
return relativeUrl;
21+
}
22+
23+
public void setRelativeUrl(@NotNull String relativeUrl) {
24+
this.relativeUrl = relativeUrl;
25+
}
26+
27+
public void setData(@NotNull JsonNode data) {
28+
this.data = data;
29+
}
30+
31+
public @NotNull HttpMethod getMethod() {
32+
return method;
33+
}
34+
35+
public void setMethod(@NotNull HttpMethod method) {
36+
this.method = method;
37+
}
38+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.baeldung.bulkandbatchapi.request;
2+
3+
public enum BulkActionType {
4+
CREATE, UPDATE, DELETE
5+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.baeldung.bulkandbatchapi.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
5+
import java.io.Serializable;
6+
7+
public class Customer implements Serializable {
8+
9+
private int id;
10+
11+
@NotBlank
12+
private String name;
13+
14+
@NotBlank
15+
private String email;
16+
17+
@NotBlank
18+
private String address;
19+
20+
public Customer() {
21+
}
22+
23+
public Customer(int id, String name, String email) {
24+
this.id = id;
25+
this.name = name;
26+
this.email = email;
27+
}
28+
29+
public int getId() {
30+
return id;
31+
}
32+
33+
public void setId(int id) {
34+
this.id = id;
35+
}
36+
37+
public String getName() {
38+
return name;
39+
}
40+
41+
public void setName(String name) {
42+
this.name = name;
43+
}
44+
45+
public String getEmail() {
46+
return email;
47+
}
48+
49+
public void setEmail(String email) {
50+
this.email = email;
51+
}
52+
53+
public String getAddress() {
54+
return address;
55+
}
56+
57+
public void setAddress(String address) {
58+
this.address = address;
59+
}
60+
61+
@Override
62+
public String toString() {
63+
return "Customer{" +
64+
"id=" + id +
65+
", name='" + name + '\'' +
66+
", email='" + email + '\'' +
67+
", address=" + address +
68+
'}';
69+
}
70+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.baeldung.bulkandbatchapi.request;
2+
3+
import jakarta.validation.constraints.NotNull;
4+
import jakarta.validation.constraints.Size;
5+
6+
import java.util.List;
7+
8+
public class CustomerBulkRequest {
9+
10+
@NotNull
11+
private BulkActionType bulkActionType;
12+
13+
@NotNull
14+
@Size(min = 1, max = 20)
15+
private List<Customer> customers;
16+
17+
public BulkActionType getBulkActionType() {
18+
return bulkActionType;
19+
}
20+
21+
public List<Customer> getCustomers() {
22+
return customers;
23+
}
24+
}

0 commit comments

Comments
 (0)