From a112c2456ac66d98299c17b3b873641fab54a7c3 Mon Sep 17 00:00:00 2001 From: Alex Burt Date: Tue, 24 Sep 2024 11:21:03 +0100 Subject: [PATCH] SDK-2478: Allow the Relying Business to delete tracked device metadata for a given session --- .../yoti/api/client/docs/DocScanClient.java | 14 +++- .../yoti/api/client/docs/DocScanService.java | 23 ++++++ .../devicemetadata/MetadataResponse.java | 7 ++ .../call/factory/UnsignedPathFactory.java | 8 +- .../api/client/docs/DocScanClientTest.java | 11 +++ .../api/client/docs/DocScanServiceTest.java | 76 +++++++++++++++++++ 6 files changed, 136 insertions(+), 3 deletions(-) diff --git a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanClient.java b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanClient.java index d59f1c39..8d0a920e 100644 --- a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanClient.java +++ b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanClient.java @@ -249,7 +249,7 @@ public SessionConfigurationResponse getSessionConfiguration(String sessionId) th * * @param sessionId the session ID * - * @return the session configuration + * @return the list of tracked devices information * @throws DocScanException if an error has occurred */ public List getTrackedDevices(String sessionId) throws DocScanException { @@ -257,6 +257,18 @@ public List getTrackedDevices(String sessionId) throws DocScan return docScanService.getTrackedDevices(sdkId, keyPair, sessionId); } + /** + * Deletes the tracked devices metadata for the given sessionID. + * + * @param sessionId the session ID + * + * @throws DocScanException if an error has occurred + */ + public void deleteTrackedDevices(String sessionId) throws DocScanException { + LOG.debug("Deleting tracked devices for session '{}'", sessionId); + docScanService.deleteTrackedDevices(sdkId, keyPair, sessionId); + } + private KeyPair loadKeyPair(KeyPairSource kpSource) throws InitialisationException { try { LOG.debug("Loading key pair from '{}'", kpSource); diff --git a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanService.java b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanService.java index b6d2ba18..e586b6b8 100644 --- a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanService.java +++ b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/DocScanService.java @@ -556,6 +556,29 @@ public List getTrackedDevices(String sdkId, KeyPair keyPair, S } } + public void deleteTrackedDevices(String sdkId, KeyPair keyPair, String sessionId) throws DocScanException { + notNullOrEmpty(sdkId, "SDK ID"); + notNull(keyPair, "Application key Pair"); + notNullOrEmpty(sessionId, "sessionId"); + + String path = unsignedPathFactory.createDeleteTrackedDevices(sdkId, sessionId); + LOG.info("Deleting tracked devices at '{}'", path); + + try { + signedRequestBuilderFactory.create() + .withKeyPair(keyPair) + .withBaseUrl(apiUrl) + .withEndpoint(path) + .withHttpMethod(HTTP_DELETE) + .build() + .execute(); + } catch (GeneralSecurityException | ResourceException ex) { + throw new DocScanException("Error executing the GET: " + ex.getMessage(), ex); + } catch (IOException | URISyntaxException ex) { + throw new DocScanException("Error building the request: " + ex.getMessage(), ex); + } + } + private String findContentType(SignedRequestResponse response) { List contentTypeValues = null; for (Map.Entry> entry : response.getResponseHeaders().entrySet()) { diff --git a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/session/devicemetadata/MetadataResponse.java b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/session/devicemetadata/MetadataResponse.java index 0311cc1d..73a975d1 100644 --- a/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/session/devicemetadata/MetadataResponse.java +++ b/yoti-sdk-api/src/main/java/com/yoti/api/client/docs/session/devicemetadata/MetadataResponse.java @@ -7,6 +7,9 @@ public class MetadataResponse { @JsonProperty("event") private String event; + @JsonProperty("resource_id") + private String resourceId; + @JsonProperty("created") private String created; @@ -17,6 +20,10 @@ public String getEvent() { return event; } + public String getResourceId() { + return resourceId; + } + public String getCreated() { return created; } diff --git a/yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/factory/UnsignedPathFactory.java b/yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/factory/UnsignedPathFactory.java index 5d8d59e2..6b322cdf 100644 --- a/yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/factory/UnsignedPathFactory.java +++ b/yoti-sdk-api/src/main/java/com/yoti/api/client/spi/remote/call/factory/UnsignedPathFactory.java @@ -32,7 +32,7 @@ public class UnsignedPathFactory { private static final String DOCS_NEW_FACE_CAPTURE_RESOURCE = "/sessions/%s/resources/face-capture?sdkId=%s"; private static final String DOCS_UPLOAD_FACE_CAPTURE_IMAGE = "/sessions/%s/resources/face-capture/%s/image?sdkId=%s"; private static final String DOCS_TRIGGER_IBV_NOTIFICATION = "/sessions/%s/instructions/email?sdkId=%s"; - private static final String DOCS_FETCH_TRACKED_DEVICES = "/sessions/%s/tracked-devices?sdkId=%s"; + private static final String DOCS_TRACKED_DEVICES = "/sessions/%s/tracked-devices?sdkId=%s"; public String createAmlPath(String appId) { return format(AML, appId); @@ -123,7 +123,11 @@ public String createTriggerIbvEmailNotificationPath(String sdkId, String session } public String createFetchTrackedDevices(String sdkId, String sessionId) { - return format(DOCS_FETCH_TRACKED_DEVICES, sessionId, sdkId); + return format(DOCS_TRACKED_DEVICES, sessionId, sdkId); + } + + public String createDeleteTrackedDevices(String sdkId, String sessionId) { + return format(DOCS_TRACKED_DEVICES, sessionId, sdkId); } } diff --git a/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanClientTest.java b/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanClientTest.java index 536835a6..265275b1 100644 --- a/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanClientTest.java +++ b/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanClientTest.java @@ -197,4 +197,15 @@ public void getTrackedDevices_shouldFailWithExceptionFromYotiDocsService() throw assertThat(thrown, is(original)); } + @Test + public void deleteTrackedDevices_shouldFailWithExceptionFromYotiDocsService() throws Exception { + DocScanException original = new DocScanException("Test exception"); + doThrow(original).when(docScanServiceMock).deleteTrackedDevices(eq(APP_ID), any(KeyPair.class), eq(SOME_SESSION_ID)); + DocScanClient testObj = new DocScanClient(APP_ID, validKeyPairSource, docScanServiceMock); + + DocScanException thrown = assertThrows(DocScanException.class, () -> testObj.deleteTrackedDevices(SOME_SESSION_ID)); + + assertThat(thrown, is(original)); + } + } diff --git a/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanServiceTest.java b/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanServiceTest.java index 354e2720..a945140f 100644 --- a/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanServiceTest.java +++ b/yoti-sdk-api/src/test/java/com/yoti/api/client/docs/DocScanServiceTest.java @@ -1538,4 +1538,80 @@ public void getTrackedDevices_shouldReturnTrackedDevices() throws Exception { assertThat(result.get(0), is(metadataResponseMock)); } + @Test + public void deleteTrackedDevices_shouldThrowExceptionWhenSdkIdIsNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> docScanService.deleteTrackedDevices(null, KEY_PAIR, SOME_SESSION_ID)); + assertThat(exception.getMessage(), containsString("SDK ID")); + } + + @Test + public void deleteTrackedDevices_shouldThrowExceptionWhenSdkIdIsEmpty() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> docScanService.deleteTrackedDevices("", KEY_PAIR, SOME_SESSION_ID)); + assertThat(exception.getMessage(), containsString("SDK ID")); + } + + @Test + public void deleteTrackedDevices_shouldThrowExceptionWhenKeyPairIsNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, null, SOME_SESSION_ID)); + assertThat(exception.getMessage(), containsString("Application key Pair")); + } + + @Test + public void deleteTrackedDevices_shouldThrowExceptionWhenSessionIdIsNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, null)); + assertThat(exception.getMessage(), containsString("sessionId")); + } + + @Test + public void deleteTrackedDevices_shouldThrowExceptionWhenSessionIdIsEmpty() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, "")); + assertThat(exception.getMessage(), containsString("sessionId")); + } + + @Test + public void deleteTrackedDevices_shouldWrapGeneralSecurityException() throws Exception { + GeneralSecurityException gse = new GeneralSecurityException("some gse"); + when(signedRequestBuilderMock.build()).thenThrow(gse); + + DocScanException ex = assertThrows(DocScanException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, SOME_SESSION_ID)); + + assertSame(ex.getCause(), gse); + assertThat(ex.getMessage(), containsString("Error executing the GET: some gse")); + } + + @Test + public void deleteTrackedDevices_shouldWrapResourceException() throws Exception { + ResourceException resourceException = new ResourceException(400, "Failed Request", "Some response from API"); + when(signedRequestBuilderMock.build()).thenReturn(signedRequestMock); + when(signedRequestMock.execute()).thenThrow(resourceException); + + DocScanException ex = assertThrows(DocScanException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, SOME_SESSION_ID)); + + assertSame(ex.getCause(), resourceException); + assertThat(ex.getMessage(), containsString("Error executing the GET: Failed Request")); + } + + @Test + public void deleteTrackedDevices_shouldWrapIOException() throws Exception { + IOException ioException = new IOException("Some io exception"); + when(signedRequestBuilderMock.build()).thenReturn(signedRequestMock); + when(signedRequestMock.execute()).thenThrow(ioException); + + DocScanException ex = assertThrows(DocScanException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, SOME_SESSION_ID)); + + assertSame(ex.getCause(), ioException); + assertThat(ex.getMessage(), containsString("Error building the request: Some io exception")); + } + + @Test + public void deleteTrackedDevices_shouldWrapURISyntaxException() throws Exception { + URISyntaxException uriSyntaxException = new URISyntaxException("someUrl", "Failed to build URI"); + when(signedRequestBuilderMock.build()).thenThrow(uriSyntaxException); + + DocScanException ex = assertThrows(DocScanException.class, () -> docScanService.deleteTrackedDevices(SOME_APP_ID, KEY_PAIR, SOME_SESSION_ID)); + + assertSame(ex.getCause(), uriSyntaxException); + assertThat(ex.getMessage(), containsString("Error building the request: Failed to build URI: someUrl")); + } + }