Skip to content

Commit 42a7664

Browse files
authored
Merge pull request #140 from companieshouse/feature/implement_get_checkout_item_endpoint
Implement get checkout item endpoint
2 parents f527d3a + 56eb82e commit 42a7664

File tree

10 files changed

+202
-3
lines changed

10 files changed

+202
-3
lines changed

src/main/java/uk/gov/companieshouse/orders/api/controller/OrderController.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class OrderController {
5353
"${uk.gov.companieshouse.orders.api.orders}/{" + ORDER_ID_PATH_VARIABLE + "}";
5454

5555
public static final String GET_ORDER_ITEM_URI = "/orders/{id}/items/{itemId}";
56+
public static final String GET_CHECKOUT_ITEM_URI = "/checkouts/{id}/items/{itemId}";
5657

5758
/** <code>${uk.gov.companieshouse.orders.api.checkouts}/{id}</code> */
5859
public static final String GET_CHECKOUT_URI =
@@ -101,6 +102,21 @@ public ResponseEntity<Item> getOrderItem(final @PathVariable("id") String orderI
101102
return ResponseEntity.ok().body(item);
102103
}
103104

105+
@GetMapping(GET_CHECKOUT_ITEM_URI)
106+
public ResponseEntity<Item> getCheckoutItem(final @PathVariable("id") String checkoutId,
107+
final @PathVariable("itemId") String itemId,
108+
final @RequestHeader(REQUEST_ID_HEADER_NAME) String requestId) {
109+
Map<String, Object> logMap = createLogMapWithRequestId(requestId);
110+
logIfNotNull(logMap, LoggingUtils.ORDER_ID, checkoutId);
111+
logIfNotNull(logMap, LoggingUtils.ITEM_ID, itemId);
112+
LOGGER.info("Retrieving checkout item", logMap);
113+
final Item item = this.checkoutService.getCheckoutItem(checkoutId, itemId)
114+
.orElseThrow(ResourceNotFoundException::new);
115+
logMap.put(LoggingUtils.STATUS, HttpStatus.OK);
116+
LOGGER.info("Checkout item found and returned", logMap);
117+
return ResponseEntity.ok().body(item);
118+
}
119+
104120
@GetMapping(GET_CHECKOUT_URI)
105121
public ResponseEntity<CheckoutData> getCheckout(final @PathVariable(CHECKOUT_ID_PATH_VARIABLE) String id,
106122
final @RequestHeader(REQUEST_ID_HEADER_NAME) String requestId) {

src/main/java/uk/gov/companieshouse/orders/api/interceptor/RequestUris.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package uk.gov.companieshouse.orders.api.interceptor;
22

33
import static uk.gov.companieshouse.orders.api.controller.BasketController.*;
4+
import static uk.gov.companieshouse.orders.api.controller.OrderController.GET_CHECKOUT_ITEM_URI;
45
import static uk.gov.companieshouse.orders.api.controller.OrderController.GET_CHECKOUT_URI;
56
import static uk.gov.companieshouse.orders.api.controller.OrderController.GET_ORDER_ITEM_URI;
67
import static uk.gov.companieshouse.orders.api.controller.OrderController.GET_ORDER_URI;
@@ -28,6 +29,7 @@ class RequestUris {
2829
static final String GET_ORDER_ITEM = "getOrderItem";
2930
static final String SEARCH = "searchCheckouts";
3031
static final String GET_CHECKOUT = "getCheckout";
32+
static final String GET_CHECKOUT_ITEM = "getCheckoutItem";
3133
static final String POST_REPROCESS_ORDER = "postReprocessOrder";
3234
static final String GET_BASKET_LINKS = "getBasketLinks";
3335
static final String REMOVE_BASKET_ITEM = "putRemoveBasketItem";
@@ -49,6 +51,8 @@ class RequestUris {
4951
private String searchUri;
5052
@Value(GET_CHECKOUT_URI)
5153
private String getCheckoutUri;
54+
@Value(GET_CHECKOUT_ITEM_URI)
55+
private String getCheckoutItemUri;
5256
@Value(PATCH_PAYMENT_DETAILS_URI)
5357
private String patchPaymentDetailsUri;
5458
@Value(POST_REPROCESS_ORDER_URI)
@@ -120,6 +124,12 @@ List<RequestMappingInfo> requestMappingInfoList() {
120124
.mappingName(GET_ORDER_ITEM)
121125
.build());
122126

127+
knownRequests.add(RequestMappingInfo
128+
.paths(getCheckoutItemUri)
129+
.methods(RequestMethod.GET)
130+
.mappingName(GET_CHECKOUT_ITEM)
131+
.build());
132+
123133
knownRequests.add(RequestMappingInfo
124134
.paths(getCheckoutUri)
125135
.methods(RequestMethod.GET)

src/main/java/uk/gov/companieshouse/orders/api/interceptor/UserAuthenticationInterceptor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private boolean checkAuthenticated(HttpServletRequest request, HttpServletRespon
6161
return securityManager.checkIdentity() || hasAuthenticatedClient(request, response);
6262
case GET_CHECKOUT:
6363
case SEARCH:
64+
case GET_CHECKOUT_ITEM:
6465
return securityManager.checkIdentity();
6566
case PATCH_PAYMENT_DETAILS:
6667
case POST_REPROCESS_ORDER:

src/main/java/uk/gov/companieshouse/orders/api/interceptor/UserAuthorisationInterceptor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.CHECKOUT_BASKET;
1111
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_BASKET_LINKS;
1212
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_CHECKOUT;
13+
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_CHECKOUT_ITEM;
1314
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_ORDER;
1415
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_ORDER_ITEM;
1516
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_PAYMENT_DETAILS;
@@ -91,6 +92,7 @@ private boolean checkAuthorised(HttpServletRequest request, HttpServletResponse
9192
case GET_ORDER_ITEM:
9293
return securityManager.checkPermission() || getRequestClientIsAuthorised(request, response, this::getOrderUserIsResourceOwner);
9394
case SEARCH:
95+
case GET_CHECKOUT_ITEM:
9496
return securityManager.checkPermission();
9597
case PATCH_PAYMENT_DETAILS:
9698
case POST_REPROCESS_ORDER:

src/main/java/uk/gov/companieshouse/orders/api/service/CheckoutService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,13 @@ public Checkout saveCheckout(final Checkout updatedCheckout) {
144144
updatedCheckout.getData().setEtag(etagGeneratorService.generateEtag());
145145
return checkoutRepository.save(updatedCheckout);
146146
}
147+
148+
public Optional<Item> getCheckoutItem(String orderId, String itemId) {
149+
Optional<Checkout> order = getCheckoutById(orderId);
150+
return order.flatMap(o -> o.getData()
151+
.getItems()
152+
.stream()
153+
.filter(item -> item.getId().equals(itemId))
154+
.findFirst());
155+
}
147156
}

src/test/java/uk/gov/companieshouse/orders/api/controller/OrderControllerIntegrationTest.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,67 @@ void getOrderItemReturnsNotFoundIfNoMatchingItemID() throws Exception {
562562
.andExpect(status().isNotFound());
563563
}
564564

565+
@DisplayName("Get a checkout item")
566+
@Test
567+
void getCheckoutItem() throws Exception {
568+
final Checkout preexistingCheckout = new Checkout();
569+
preexistingCheckout.setId(CHECKOUT_ID);
570+
preexistingCheckout.setUserId(ERIC_IDENTITY_VALUE);
571+
final CheckoutData checkoutData = new CheckoutData();
572+
preexistingCheckout.setData(checkoutData);
573+
checkoutData.setReference(ORDER_REFERENCE);
574+
checkoutData.setTotalOrderCost("100");
575+
Item expectedItem = StubHelper.getOrderItem("CCD-123456-123456", "item#certified-copy",
576+
"12345678");
577+
checkoutData.setItems(Arrays.asList(
578+
StubHelper.getOrderItem("MID-123456-123456", "item#missing-image-delivery",
579+
"12345678"),
580+
expectedItem,
581+
StubHelper.getOrderItem("CRT-123456-123456", "item#certificate",
582+
"12345678")));
583+
checkoutRepository.save(preexistingCheckout);
584+
585+
mockMvc.perform(get("/checkouts/"+CHECKOUT_ID+"/items/CCD-123456-123456")
586+
.header(REQUEST_ID_HEADER_NAME, TOKEN_REQUEST_ID_VALUE)
587+
.header(ERIC_IDENTITY_TYPE, API_KEY_IDENTITY_TYPE)
588+
.header(ERIC_AUTHORISED_KEY_PRIVILEGES, INTERNAL_USER_ROLE)
589+
.header(ERIC_IDENTITY_HEADER_NAME, ERIC_IDENTITY_VALUE)
590+
.header(ERIC_AUTHORISED_TOKEN_PERMISSIONS, String.format(TOKEN_PERMISSION_VALUE, Permission.Value.READ))
591+
.contentType(MediaType.APPLICATION_JSON))
592+
.andExpect(status().isOk())
593+
.andExpect(content().json(mapper.writeValueAsString(expectedItem)));
594+
}
595+
596+
@DisplayName("Get checkout item returns HTTP 404 Not Found if no matching item ID")
597+
@Test
598+
void getCheckoutItemReturnsNotFoundIfNoMatchingItemID() throws Exception {
599+
final Checkout preexistingOrder = new Checkout();
600+
preexistingOrder.setId(ORDER_ID);
601+
preexistingOrder.setUserId(ERIC_IDENTITY_VALUE);
602+
final CheckoutData orderData = new CheckoutData();
603+
preexistingOrder.setData(orderData);
604+
orderData.setReference(ORDER_REFERENCE);
605+
orderData.setTotalOrderCost("100");
606+
Item expectedItem = StubHelper.getOrderItem("CCD-123456-123456", "item#certified-copy",
607+
"12345678");
608+
orderData.setItems(Arrays.asList(
609+
StubHelper.getOrderItem("MID-123456-123456", "item#missing-image-delivery",
610+
"12345678"),
611+
expectedItem,
612+
StubHelper.getOrderItem("CRT-123456-123456", "item#certificate",
613+
"12345678")));
614+
checkoutRepository.save(preexistingOrder);
615+
616+
mockMvc.perform(get("/checkouts/"+ORDER_ID+"/items/NONEXISTENT")
617+
.header(REQUEST_ID_HEADER_NAME, TOKEN_REQUEST_ID_VALUE)
618+
.header(ERIC_IDENTITY_TYPE, API_KEY_IDENTITY_TYPE)
619+
.header(ERIC_AUTHORISED_KEY_PRIVILEGES, INTERNAL_USER_ROLE)
620+
.header(ERIC_IDENTITY_HEADER_NAME, ERIC_IDENTITY_VALUE)
621+
.header(ERIC_AUTHORISED_TOKEN_PERMISSIONS, String.format(TOKEN_PERMISSION_VALUE, Permission.Value.READ))
622+
.contentType(MediaType.APPLICATION_JSON))
623+
.andExpect(status().isNotFound());
624+
}
625+
565626
private MockHttpServletRequestBuilder reprocessOrderWithRequiredCredentials() {
566627
return post("/orders/" + ORDER_ID + "/reprocess")
567628
.header(REQUEST_ID_HEADER_NAME, TOKEN_REQUEST_ID_VALUE)

src/test/java/uk/gov/companieshouse/orders/api/interceptor/RequestMapperTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.BASKET;
1515
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.CHECKOUT_BASKET;
1616
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_CHECKOUT;
17+
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_CHECKOUT_ITEM;
1718
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_ORDER;
1819
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_ORDER_ITEM;
1920
import static uk.gov.companieshouse.orders.api.interceptor.RequestUris.GET_PAYMENT_DETAILS;
@@ -208,6 +209,19 @@ void appendItemToBasket() {
208209
assertThat(actual, is(APPEND_BASKET_ITEM));
209210
}
210211

212+
@Test
213+
@DisplayName("Return mapping for getCheckoutItem endpoint")
214+
void getCheckoutItem() {
215+
// given
216+
givenRequest(GET, "/checkouts/{checkoutId}/items/{itemId}");
217+
218+
// when
219+
String actual = requestMapperUnderTest.getRequestMapping(request).getName();
220+
221+
// then
222+
assertThat(actual, is(GET_CHECKOUT_ITEM));
223+
}
224+
211225
/**
212226
* Sets up request givens.
213227
* @param method the HTTP request method

src/test/java/uk/gov/companieshouse/orders/api/interceptor/UserAuthenticationInterceptorTests.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,15 +392,17 @@ private static Stream<Arguments> authenticatedRequestFixtures() {
392392
return Stream.of(arguments("preHandle accepts get checkout request that has authenticated API headers", "/checkouts/1234"),
393393
arguments("preHandle accepts get order request that has authenticated API headers", "/orders/1234"),
394394
arguments("preHandle accepts get order item request that has authenticated API headers", "/orders/1234/items/5678"),
395-
arguments("preHandle accepts get payment details request that has authenticated API headers", "/basket/checkouts/1234/payment"));
395+
arguments("preHandle accepts get payment details request that has authenticated "
396+
+ "API headers", "/basket/checkouts/1234/payment"));
396397
}
397398

398399
private static Stream<Arguments> unauthenticatedRequestFixtures() {
399400
return Stream.of(arguments("preHandle rejects get payment details request that lacks required headers", "/basket/checkouts/1234/payment"),
400401
arguments("preHandle rejects get basket request that lacks required headers", "/basket"),
401402
arguments("preHandle rejects get order request that lacks required headers", "/orders/1234"),
402403
arguments("preHandle rejects get order item request that lacks required headers", "/orders/1234/items/5678"),
403-
arguments("preHandle rejects get basket links request that lacks requires headers", "/basket/links"));
404+
arguments("preHandle rejects get basket links request that lacks requires "
405+
+ "headers", "/basket/links"));
404406
}
405407

406408
private static Stream<Arguments> signedInPostRequestFixtures() {
@@ -422,6 +424,11 @@ private static Stream<Arguments> hasAdminAuthenticationFixtures() {
422424
return Stream.of(arguments("Authentication for orders/search endpoint succeeds if caller identity is valid", "/checkouts/search", true),
423425
arguments("Authentication for orders/search endpoint fails if caller identity is invalid", "/checkouts/search", false),
424426
arguments("Authentication for get order item endpoint succeeds if caller identity is valid", "/orders/1234/items/5678", true),
425-
arguments("Authentication for get order item endpoint fails if caller identity is invalid", "/orders/1234/items/5678", false));
427+
arguments("Authentication for get order item endpoint fails if caller identity is"
428+
+ " invalid", "/orders/1234/items/5678", false),
429+
arguments("Authentication for get checkout item endpoint succeeds if caller "
430+
+ "identity is valid", "/checkouts/1234/items/5678", true),
431+
arguments("Authentication for get checkout item endpoint fails if caller identity "
432+
+ "is invalid", "/checkouts/1234/items/5678", false));
426433
}
427434
}

src/test/java/uk/gov/companieshouse/orders/api/interceptor/UserAuthorisationInterceptorTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,29 @@ void appendBasketItem() {
454454
thenRequestIsAccepted();
455455
}
456456

457+
@Test
458+
@DisplayName("Authorisation for get checkout item endpoint succeeds if caller is has correct "
459+
+ "permissions")
460+
void getCheckoutItemValidAuthorisation() {
461+
when(securityManager.checkPermission()).thenReturn(true);
462+
givenRequest(GET, "/checkouts/1234/items/1234");
463+
464+
boolean actual = interceptorUnderTest.preHandle(request, response, handler);
465+
466+
assertThat(actual, is(true));
467+
}
468+
469+
@DisplayName("Authentication for get checkout item endpoint false if caller has incorrect permissions")
470+
@Test
471+
void getCheckoutItemInvalidAuthorisation() {
472+
when(securityManager.checkPermission()).thenReturn(false);
473+
givenRequest(GET, "/checkouts/1234/items/1234");
474+
475+
boolean actual = interceptorUnderTest.preHandle(request, response, handler);
476+
477+
assertThat(actual, is(false));
478+
}
479+
457480
/**
458481
* Sets up request givens.
459482
* @param method the HTTP request method

src/test/java/uk/gov/companieshouse/orders/api/service/CheckoutServiceTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
import static org.mockito.Mockito.times;
1010
import static org.mockito.Mockito.verify;
1111
import static org.mockito.Mockito.when;
12+
import static uk.gov.companieshouse.orders.api.util.TestConstants.CHECKOUT_ID;
1213
import static uk.gov.companieshouse.orders.api.util.TestConstants.ERIC_AUTHORISED_USER_VALUE;
1314
import static uk.gov.companieshouse.orders.api.util.TestConstants.ERIC_IDENTITY_VALUE;
1415

1516
import java.time.LocalDate;
1617
import java.time.LocalDateTime;
1718
import java.util.ArrayList;
19+
import java.util.Arrays;
1820
import java.util.Collections;
1921
import java.util.List;
22+
import java.util.Optional;
23+
import org.junit.jupiter.api.Assertions;
2024
import org.junit.jupiter.api.BeforeEach;
2125
import org.junit.jupiter.api.DisplayName;
2226
import org.junit.jupiter.api.Test;
@@ -114,6 +118,9 @@ public class CheckoutServiceTest {
114118
@Mock
115119
private Page<Checkout> pages;
116120

121+
@Mock
122+
private Item midItem, certifiedCopyItem, certificateItem;
123+
117124
@Captor
118125
ArgumentCaptor<Checkout> checkoutCaptor;
119126

@@ -375,6 +382,55 @@ void searchCheckoutsLimitsSearchResults() {
375382
assertThat(actual.getOrderSummaries().size(), is(1));
376383
}
377384

385+
@Test
386+
@DisplayName("Fetch order item")
387+
void getCheckoutItem() {
388+
// given
389+
when(checkoutResult.getData()).thenReturn(checkoutData);
390+
when(checkoutData.getItems()).thenReturn(
391+
Arrays.asList(midItem, certifiedCopyItem, certificateItem));
392+
when(midItem.getId()).thenReturn("MID-123456-123456");
393+
when(certifiedCopyItem.getId()).thenReturn("CCD-123456-123456");
394+
when(checkoutRepository.findById(CHECKOUT_ID)).thenReturn(Optional.of(checkoutResult));
395+
396+
// when
397+
Optional<Item> actual = serviceUnderTest.getCheckoutItem(CHECKOUT_ID, "CCD-123456-123456");
398+
399+
// then
400+
Assertions.assertEquals(certifiedCopyItem, actual.get());
401+
}
402+
403+
@Test
404+
@DisplayName("Fetch order item returns Optional.empty if no matching item found")
405+
void getCheckoutItemReturnsEmptyOptionalIfNoMatchingItemFound() {
406+
// given
407+
when(checkoutResult.getData()).thenReturn(checkoutData);
408+
when(checkoutData.getItems()).thenReturn(Arrays.asList(midItem, certifiedCopyItem, certificateItem));
409+
when(midItem.getId()).thenReturn("MID-123456-123456");
410+
when(certifiedCopyItem.getId()).thenReturn("CCD-123456-123456");
411+
when(certificateItem.getId()).thenReturn("CRT-123456-123456");
412+
when(checkoutRepository.findById(CHECKOUT_ID)).thenReturn(Optional.of(checkoutResult));
413+
414+
// when
415+
Optional<Item> actual = serviceUnderTest.getCheckoutItem(CHECKOUT_ID, "UNKNOWN");
416+
417+
// then
418+
Assertions.assertEquals(Optional.empty(), actual);
419+
}
420+
421+
@Test
422+
@DisplayName("Fetch order item returns Optional.empty if no matching order found")
423+
void getCheckoutItemReturnsEmptyOptionalIfNoMatchingOrderFound() {
424+
// given
425+
when(checkoutRepository.findById(CHECKOUT_ID)).thenReturn(Optional.empty());
426+
427+
// when
428+
Optional<Item> actual = serviceUnderTest.getCheckoutItem(CHECKOUT_ID, "UNKNOWN");
429+
430+
// then
431+
Assertions.assertEquals(Optional.empty(), actual);
432+
}
433+
378434
/**
379435
* @return the captured {@link Checkout}.
380436
*/

0 commit comments

Comments
 (0)