diff --git a/extensions/data-transfer/portability-data-transfer-google/src/main/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosInterface.java b/extensions/data-transfer/portability-data-transfer-google/src/main/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosInterface.java index 00ff05bf3..7b3a5d456 100644 --- a/extensions/data-transfer/portability-data-transfer-google/src/main/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosInterface.java +++ b/extensions/data-transfer/portability-data-transfer-google/src/main/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosInterface.java @@ -244,6 +244,11 @@ public static PhotosLibraryClient buildPhotosLibraryClient( return PhotosLibraryClient.initialize(settings); } + private static boolean isTokenException(String message) { + return message.contains("invalid_grant") + || message.contains("The upload could not be initialized. Unauthorized"); + } + /** * Uploads `video` via {@link com.google.photos.library.v1.PhotosLibraryClient} APIs. * @@ -278,10 +283,8 @@ public static Pair uploadVideo( String message = cause.getMessage(); if (message.contains("The upload url is either finalized or rejected by the server")) { throw new UploadErrorException("Upload was terminated because of error", cause); - } else if (message.contains("invalid_grant")) { + } else if (isTokenException(message)) { throw new InvalidTokenException("Token has been expired or revoked", cause); - } else if (message.contains("The upload could not be initialized. Unauthorized")) { - throw new InvalidTokenException("uploadVideo could not be initialized. Unauthorized", cause); } } @@ -296,7 +299,7 @@ public static Pair uploadVideo( // temp check as exception is not captured and wrapped into UploadMediaItemResponse Throwable cause = ex.getCause(); String message = cause.getMessage(); - if (message.contains("invalid_grant")) { + if (isTokenException(message)) { throw new InvalidTokenException("Token has been expired or revoked", cause); } throw new IOException("An error was encountered while uploading the video.", cause); diff --git a/extensions/data-transfer/portability-data-transfer-google/src/test/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosImporterTest.java b/extensions/data-transfer/portability-data-transfer-google/src/test/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosImporterTest.java index 2f76a007a..3a7157b4a 100644 --- a/extensions/data-transfer/portability-data-transfer-google/src/test/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosImporterTest.java +++ b/extensions/data-transfer/portability-data-transfer-google/src/test/java/org/datatransferproject/datatransfer/google/videos/GoogleVideosImporterTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -31,6 +32,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.StatusCode; import com.google.common.collect.Lists; import com.google.common.io.Files; import com.google.photos.library.v1.PhotosLibraryClient; @@ -56,6 +59,7 @@ import org.datatransferproject.spi.cloud.storage.TemporaryPerJobDataStore.InputStreamWrapper; import org.datatransferproject.spi.transfer.idempotentexecutor.InMemoryIdempotentImportExecutor; import org.datatransferproject.spi.transfer.provider.ImportResult; +import org.datatransferproject.spi.transfer.types.InvalidTokenException; import org.datatransferproject.types.common.models.videos.VideoAlbum; import org.datatransferproject.types.common.models.videos.VideoModel; import org.datatransferproject.types.common.models.videos.VideosContainerResource; @@ -63,6 +67,8 @@ import org.datatransferproject.types.transfer.errors.ErrorDetail; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentMatchers; @@ -85,6 +91,15 @@ public class GoogleVideosImporterTest { private PhotosLibraryClient client; private UUID jobId; + class TestStatusCode implements StatusCode { + public StatusCode.Code getCode() { + return StatusCode.Code.PERMISSION_DENIED; + } + + public Object getTransportCode() { + return 401; + } + } @BeforeEach public void setUp() throws Exception { @@ -473,4 +488,76 @@ public void importSameVideoInTwoDifferentAlbums() throws Exception { assertEquals(64L, bytes,"Expected the number of bytes to be the two files of 32L."); assertEquals(0, executor.getErrors().size(),"Expected executor to have no errors."); } + + @ParameterizedTest + @ValueSource(strings = {"invalid_grant", "The upload could not be initialized. Unauthorized"}) + public void refreshTokenErrorsThrowInvalidTokenException(String errorMessage) throws Exception { + VideoModel videoModel = + new VideoModel( + VIDEO_TITLE, + VIDEO_URI, + VIDEO_DESCRIPTION, + MP4_MEDIA_TYPE, + "dataId", + "album1", + false, + null); + + when(client.uploadMediaItem(any())) + .thenThrow( + new ApiException( + errorMessage, new Exception(errorMessage), new TestStatusCode(), false)); + + InMemoryIdempotentImportExecutor executor = + new InMemoryIdempotentImportExecutor(mock(Monitor.class)); + assertThrows( + InvalidTokenException.class, + () -> + googleVideosImporter.importItem( + jobId, + executor, + mock(TokensAndUrlAuthData.class), + new VideosContainerResource(List.of(), List.of(videoModel)))); + } + + @ParameterizedTest + @ValueSource(strings = {"invalid_grant", "The upload could not be initialized. Unauthorized"}) + public void refreshTokenErrorsThrowInvalidTokenExceptionWhenErrorReturned(String errorMessage) + throws Exception { + VideoModel videoModel = + new VideoModel( + VIDEO_TITLE, + VIDEO_URI, + VIDEO_DESCRIPTION, + MP4_MEDIA_TYPE, + "dataId", + "album1", + false, + null); + + when(client.uploadMediaItem(any())) + .thenReturn( + UploadMediaItemResponse.newBuilder() + .setError( + UploadMediaItemResponse.Error.newBuilder() + .setCause( + new ApiException( + errorMessage, + new Exception(errorMessage), + new TestStatusCode(), + false)) + .build()) + .build()); + + InMemoryIdempotentImportExecutor executor = + new InMemoryIdempotentImportExecutor(mock(Monitor.class)); + assertThrows( + InvalidTokenException.class, + () -> + googleVideosImporter.importItem( + jobId, + executor, + mock(TokensAndUrlAuthData.class), + new VideosContainerResource(List.of(), List.of(videoModel)))); + } }