forked from dtinit/data-transfer-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add automatic media job type compatibility
- Loading branch information
Showing
26 changed files
with
1,049 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
...ain/java/org/datatransferproject/spi/transfer/provider/TransferCompatibilityProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package org.datatransferproject.spi.transfer.provider; | ||
|
||
import java.util.function.Function; | ||
import org.datatransferproject.spi.transfer.extension.TransferExtension; | ||
import org.datatransferproject.spi.transfer.provider.converter.AnyToAnyExporter; | ||
import org.datatransferproject.spi.transfer.provider.converter.AnyToAnyImporter; | ||
import org.datatransferproject.spi.transfer.provider.converter.MediaExporterDecorator; | ||
import org.datatransferproject.spi.transfer.provider.converter.MediaImporterDecorator; | ||
import org.datatransferproject.types.common.models.ContainerResource; | ||
import org.datatransferproject.types.common.models.media.MediaContainerResource; | ||
import org.datatransferproject.types.common.models.photos.PhotosContainerResource; | ||
import org.datatransferproject.types.common.models.videos.VideosContainerResource; | ||
|
||
/** | ||
* Provides a compatibility layer between adapter types. E.g. when asking for a Media adapter and it | ||
* isn't available, it will combine Photo and Video adapters on the fly to seamlessly support the | ||
* request. | ||
*/ | ||
public class TransferCompatibilityProvider { | ||
|
||
private static final String PHOTOS = "PHOTOS"; | ||
private static final String VIDEOS = "VIDEOS"; | ||
private static final String MEDIA = "MEDIA"; | ||
|
||
public Exporter getCompatibleExporter(TransferExtension extension, String jobType) { | ||
Exporter<?, ?> exporter = getExporterOrNull(extension, jobType); | ||
if (exporter != null) { | ||
return exporter; | ||
} | ||
|
||
switch (jobType) { | ||
case MEDIA: | ||
exporter = getMediaExporter(extension); | ||
break; | ||
case PHOTOS: | ||
exporter = getPhotosExporter(extension); | ||
break; | ||
case VIDEOS: | ||
exporter = getVideosExporter(extension); | ||
break; | ||
} | ||
if (exporter == null) { | ||
return extension.getExporter(jobType); // preserve original exception | ||
} | ||
return exporter; | ||
} | ||
|
||
public Importer getCompatibleImporter(TransferExtension extension, String jobType) { | ||
Importer<?, ?> importer = getImporterOrNull(extension, jobType); | ||
if (importer != null) { | ||
return importer; | ||
} | ||
|
||
switch (jobType) { | ||
case MEDIA: | ||
importer = getMediaImporter(extension); | ||
break; | ||
case PHOTOS: | ||
importer = getPhotosImporter(extension); | ||
break; | ||
case VIDEOS: | ||
importer = getVideosImporter(extension); | ||
break; | ||
} | ||
if (importer == null) { | ||
return extension.getImporter(jobType); | ||
} | ||
return importer; | ||
} | ||
|
||
private Importer<?, ?> getVideosImporter(TransferExtension extension) { | ||
Importer media = getImporterOrNull(extension, MEDIA); | ||
if (media == null) { | ||
return null; | ||
} | ||
return new AnyToAnyImporter<>(media, MediaContainerResource::videoToMedia); | ||
} | ||
|
||
private Importer<?, ?> getPhotosImporter(TransferExtension extension) { | ||
Importer media = getImporterOrNull(extension, MEDIA); | ||
if (media == null) { | ||
return null; | ||
} | ||
return new AnyToAnyImporter<>(media, MediaContainerResource::photoToMedia); | ||
} | ||
|
||
private Importer<?, ?> getMediaImporter(TransferExtension extension) { | ||
Importer<?, ?> photo = getImporterOrNull(extension, PHOTOS); | ||
Importer<?, ?> video = getImporterOrNull(extension, VIDEOS); | ||
if (photo == null || video == null) { | ||
return null; | ||
} | ||
return new MediaImporterDecorator(photo, video); | ||
} | ||
|
||
private Exporter<?, ?> getVideosExporter(TransferExtension extension) { | ||
Exporter media = getExporterOrNull(extension, MEDIA); | ||
if (media == null) { | ||
return null; | ||
} | ||
Function<ContainerResource, ContainerResource> converter = (cr) -> | ||
(cr instanceof VideosContainerResource) ? | ||
MediaContainerResource.videoToMedia((VideosContainerResource) cr) : cr; | ||
return new AnyToAnyExporter<>(media, MediaContainerResource::mediaToVideo, converter); | ||
} | ||
|
||
private Exporter<?, ?> getPhotosExporter(TransferExtension extension) { | ||
Exporter media = getExporterOrNull(extension, MEDIA); | ||
if (media == null) { | ||
return null; | ||
} | ||
Function<ContainerResource, ContainerResource> converter = (cr) -> | ||
(cr instanceof PhotosContainerResource) ? | ||
MediaContainerResource.photoToMedia((PhotosContainerResource) cr) : cr; | ||
return new AnyToAnyExporter<>(media, MediaContainerResource::mediaToPhoto, converter); | ||
} | ||
|
||
private Exporter<?, ?> getMediaExporter(TransferExtension extension) { | ||
Exporter<?, ?> photo = getExporterOrNull(extension, PHOTOS); | ||
Exporter<?, ?> video = getExporterOrNull(extension, VIDEOS); | ||
if (photo == null || video == null) { | ||
return null; | ||
} | ||
return new MediaExporterDecorator(photo, video); | ||
} | ||
|
||
private Exporter<?, ?> getExporterOrNull(TransferExtension extension, String jobType) { | ||
// TODO: Don't use exceptions for control flow. Have a way to query supported adapters | ||
try { | ||
return extension.getExporter(jobType); | ||
} catch (Exception e) { | ||
return null; | ||
} | ||
} | ||
|
||
private Importer<?, ?> getImporterOrNull(TransferExtension extension, String jobType) { | ||
try { | ||
return extension.getImporter(jobType); | ||
} catch (Exception e) { | ||
return null; | ||
} | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
...c/main/java/org/datatransferproject/spi/transfer/provider/converter/AnyToAnyExporter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package org.datatransferproject.spi.transfer.provider.converter; | ||
|
||
import java.util.Optional; | ||
import java.util.UUID; | ||
import java.util.function.Function; | ||
import org.datatransferproject.spi.transfer.provider.ExportResult; | ||
import org.datatransferproject.spi.transfer.provider.Exporter; | ||
import org.datatransferproject.types.common.ExportInformation; | ||
import org.datatransferproject.types.common.models.ContainerResource; | ||
import org.datatransferproject.types.transfer.auth.AuthData; | ||
|
||
/** | ||
* Allows flexible bridging between adapters of different types with compatible functionality. | ||
* | ||
* @param <From> The container type supported by the available exporter. | ||
* @param <To> The container type that's desired. | ||
*/ | ||
public class AnyToAnyExporter< | ||
AD extends AuthData, | ||
From extends ContainerResource, | ||
To extends ContainerResource> implements Exporter<AD, To> { | ||
|
||
private final Exporter<AD, From> exporter; | ||
private final Function<From, To> containerResourceConverter; | ||
private final Function<ContainerResource, ContainerResource> exportInformationConverter; | ||
|
||
/** | ||
* @param exporter existing exporter | ||
* @param containerResourceConverter function converting between the existing and desired | ||
* containers. | ||
* @param exportInformationConverter converter that's used to adapt the export information that | ||
* goes into the export call. It's required because exporters | ||
* often support various container resources as part of export | ||
* information. E.g. photo exporters support | ||
* DateRangeContainerResource, while media adapters might only | ||
* support MediaContainerResource. | ||
*/ | ||
public AnyToAnyExporter(Exporter<AD, From> exporter, | ||
Function<From, To> containerResourceConverter, | ||
Function<ContainerResource, ContainerResource> exportInformationConverter) { | ||
this.exporter = exporter; | ||
this.containerResourceConverter = containerResourceConverter; | ||
this.exportInformationConverter = exportInformationConverter; | ||
} | ||
|
||
/** | ||
* @param exporter existing exporter | ||
* @param containerResourceConverter function converting between the existing and desired | ||
* containers. | ||
*/ | ||
public AnyToAnyExporter(Exporter<AD, From> exporter, | ||
Function<From, To> containerResourceConverter) { | ||
this(exporter, containerResourceConverter, Function.identity()); | ||
} | ||
|
||
@Override | ||
public ExportResult<To> export(UUID jobId, AD authData, Optional<ExportInformation> exportInfo) | ||
throws Exception { | ||
Optional<ExportInformation> infoWithConvertedResource = | ||
exportInfo.map( | ||
(ei) -> | ||
ei.copyWithResource(exportInformationConverter.apply(ei.getContainerResource()))); | ||
ExportResult<From> originalResult = exporter.export(jobId, authData, infoWithConvertedResource); | ||
return originalResult.copyWithExportedData( | ||
containerResourceConverter.apply(originalResult.getExportedData())); | ||
} | ||
} |
Oops, something went wrong.