@Override
  public ExportResult<MediaContainerResource> export(
      UUID jobId, TokensAndUrlAuthData authData, Optional<ExportInformation> exportInformation)
          throws UploadErrorException, FailedToListAlbumsException, InvalidTokenException, PermissionDeniedException, IOException, FailedToListMediaItemsException {
    if (!exportInformation.isPresent()) {
      // Make list of photos contained in albums so they are not exported twice later on
      populateContainedMediaList(jobId, authData);
      return exportAlbums(authData, Optional.empty(), jobId);
    } else if (exportInformation.get().getContainerResource() instanceof PhotosContainerResource) {
      // if ExportInformation is a photos container, this is a request to only export the contents
      // in that container instead of the whole user library
      return exportPhotosContainer(
          (PhotosContainerResource) exportInformation.get().getContainerResource(), authData, jobId);
    } else if (exportInformation.get().getContainerResource() instanceof MediaContainerResource) {
      // if ExportInformation is a media container, this is a request to only export the contents
      // in that container instead of the whole user library (this is to support backwards
      // compatibility with the GooglePhotosExporter)
      return exportMediaContainer(
          (MediaContainerResource) exportInformation.get().getContainerResource(), authData, jobId);
    }

    /*
     * Use the export information to determine whether this export call should export albums or
     * photos.
     *
     * Albums are exported if and only if the export information doesn't hold an album
     * already, and the pagination token begins with the album prefix.  There must be a pagination
     * token for album export since this is isn't the first export operation performed (if it was,
     * there wouldn't be any export information at all).
     *
     * Otherwise, photos are exported. If photos are exported, there may or may not be pagination
     * information, and there may or may not be album information. If there is no container
     * resource, that means that we're exporting albumless photos and a pagination token must be
     * present. The beginning step of exporting albumless photos is indicated by a pagination token
     * containing only MEDIA_TOKEN_PREFIX with no token attached, in order to differentiate this
     * case for the first step of export (no export information at all).
     */
    StringPaginationToken paginationToken =
        (StringPaginationToken) exportInformation.get().getPaginationData();
    IdOnlyContainerResource idOnlyContainerResource =
        (IdOnlyContainerResource) exportInformation.get().getContainerResource();

    boolean containerResourcePresent = idOnlyContainerResource != null;
    boolean paginationDataPresent = paginationToken != null;

    if (!containerResourcePresent
        && paginationDataPresent
        && paginationToken.getToken().startsWith(ALBUM_TOKEN_PREFIX)) {
      // were still listing out all of the albums since we have pagination data
      return exportAlbums(authData, Optional.of(paginationToken), jobId);
    } else {
      return exportMedia(
          authData,
          Optional.ofNullable(idOnlyContainerResource),
          Optional.ofNullable(paginationToken),
          jobId);
    }
  }
@Test
  public void testExportAlbums_retryingSkipsFailures() throws Exception {
    String albumIdToFail1 = "albumid3";
    String albumIdToFail2 = "albumid5";

    ImmutableList<PhotoModel> photos = ImmutableList.of();
    ImmutableList<PhotoAlbum> albums = ImmutableList.of(
        setUpSinglePhotoAlbum("albumid1", "album 1`", ""),
        setUpSinglePhotoAlbum("albumid2", "album 2", ""),
        setUpSinglePhotoAlbum(albumIdToFail1, "album 3", ""),
        setUpSinglePhotoAlbum("albumid4", "album 4", ""),
        setUpSinglePhotoAlbum(albumIdToFail2, "album 5", ""),
        setUpSinglePhotoAlbum("albumid6", "album 6", "")
    );

    PhotosContainerResource container = new PhotosContainerResource(albums, photos);
    ExportInformation exportInfo = new ExportInformation(null, container);

    MediaMetadata photoMediaMetadata = new MediaMetadata();
    photoMediaMetadata.setPhoto(new Photo());

    // For the album_id_to_fail albums, throw an exception.
    when(photosInterface.getAlbum(albumIdToFail1)).thenThrow(IOException.class);
    when(photosInterface.getAlbum(albumIdToFail2)).thenThrow(IOException.class);
    // For all other albums, return a GoogleMediaAlbum.
    for (PhotoAlbum photoAlbum: albums) {
      if (photoAlbum.getId().equals(albumIdToFail1) || photoAlbum.getId().equals(albumIdToFail2)) {
        continue;
      }
      when(photosInterface.getAlbum(photoAlbum.getId())).thenReturn(
          setUpGoogleAlbum(Optional.of(photoAlbum.getId()), Optional.of(photoAlbum.getName()))
      );
    }

    ExportResult<MediaContainerResource> result = retryingGoogleMediaExporter.export(
        uuid, authData, Optional.of(exportInfo)
    );

    assertThat(
        result.getExportedData().getAlbums().stream().map(x -> x.getId()).collect(Collectors.toList())
    ).isEqualTo(
        albums.stream().map(
            x -> x.getId()
        ).filter(
            id -> !(id.equals(albumIdToFail1) || id.equals(albumIdToFail2))
        ).collect(
            Collectors.toList()
        )
    );
    assertThat(result.getExportedData().getAlbums().size()).isEqualTo(albums.size() - 2);
    assertThat(retryingExecutor.getErrors().size()).isEqualTo(2);
    assertThat(retryingExecutor.getErrors().stream().findFirst().toString().contains("IOException")).isTrue();
  }