Relativity Transfer SDK

The Relativity Transfer SDK is designed for uploading and downloading data to and from RelativityOne fileshares.

The Relativity Transfer SDK includes the following core features:

  • Upload a file or a directory to RelativityOne fileshares.
  • Download a file or a directory from RelativityOne fileshares.
  • Track data transfer progress:
    • Shows succeeded, failed, and skipped items. Including the exact path and the reason for the error.
    • Shows transfer speed and the total amount of files and bytes to be transferred.
  • Cross-platform compatibility, transfer data from Windows, Linux, and MacOS.
  • TransferSDK works solely on HTTPS to transfer data, only a single TCP 443 port is required to be opened.
  • Enhanced performance and transfer speeds.

Version History

Fundamentals for the Relativity Transfer SDK

Review the following guidelines for working with the Relativity Transfer SDK.

  • Using a network share between Samba servers running Linux and Windows may cause issues. This is in relation to how Samba handles case-sensitivity. Specifically, Windows may end up copying files more than once or not at all.
  • There are no limits on transfer file path lengths in SDK. However, all destination and source paths are limited against a set of rules defined in Windows and Linux systems . See, Windows maximum file path limitations and File System Limitations for more information.
  • Use a stable internet connection, Wifi is not recommended.
  • Due to bandwidth sharing, avoid performing large and highly fragmented transfers in parallel.
  • Transfers from network drives or external disks (such as USB drives) may degrade performance or have issues maintaining connection. It is most effective to copy data to and from local storage.
  • Relativity does not store any information about file paths due to privacy concerns. See Transfer progress for more information.
  • When transferring massive data sets, it is recommended to use a local drive where Transfer SDK is executed as the transfer source and destination to avoid additional transfer steps. Using a mapped network drive is not recommended for transfer as it introduces an extra connection between the Transfer SDK and RelativityOne that may cause slowness and connection issues.

Example of implementing the Relativity Transfer SDK

Use the following as an example on how to implement the Relativity Transfer SDK:

  1. Using any authentication method supported by RelativityOne, provide the Source Path, Destination Path and your Relativity Instance URL address.

  2. During the transfer you can track the job progress, this includes the percentage progress, total number of transferred GB and files, and predicted time to completion. As well as more detailed information about already transferred files paths.

    Note: Skipped and failed files display with corresponding paths.

    Transfer summary includes the transfer job id, sum of total bytes and files transferred, quantity of empty directories, elapsed time and final status of the job.

Code samples

Use the code samples in the following sections to learn about calling the methods available in the Relativity Transfer SDK.

Note: To use the Transfer SKD sample code and documentation in the Relativity GitHub repository, click here.

These code samples illustrate the following best practices for working with the SDK:

Create instance of SDK client

Use the following code sample to list the necessary namespaces:

Copy
using System;
using System.Threading.Tasks;
using Relativity.Transfer.SDK.Core.ProgressReporting;
using Relativity.Transfer.SDK.Interfaces;
using Relativity.Transfer.SDK.Interfaces.Paths;
using Relativity.Transfer.SDK.Interfaces.ProgressReporting;

Implement IRelativityAuthenticationProvider to provide the RelativityOne access token. If the access token expires or is invalid, the implementation of this interface will be invoked to obtain a new token for the RelativityOne instance.

Once defined, use fluent builder to create the instance of Transfer SDK client

Copy
var clientName = "<YourCompanyName>"; // Needed for troubleshooting scenarios, should you have any. No PII is collected.
var relativityAddress = "<YourTenantAddress>"; // Regular Relativity address, like https://<YourCompaneName>.relativity.one/Relativity
var authenticationProvider = CreateAuthenticationProvider(); // Provide us with authentication method to Relativity (whether it's Basic, OAuth, etc.)

var client = TransferClientBuilder
        .FullPathWorkflow
        .WithAuthentication(authenticationProvider)
        .WithClientName(clientName)
        .Build();
    Notes:
  • Create a unique client name that can be used for easy recognition.
  • It is suggested that the client name contains the name of the company. For example, Relativity-Transfer-Client.

Transfer progress

Use the following code sample to subscribe to all, none, or some Transfer progress updates. Subscribing is optional, but is the only way to obtain information on which individual items failed or were skipped.

Note: Relativity does not store any information about file paths due to privacy concerns.

Copy
ITransferProgressHandler progressHandler = TransferProgressHandlerBuilder
        .Instance
        .OnStatistics(StatisticHook.OnStatisticsReceived) // Updates about overall status (% progress, transfer rates, partial statistics)
        .OnSucceededItem(StatisticHook.OnSucceededItemReceived) // Updates on each transferred item
        .OnFailedItem(StatisticHook.OnFailedItemReceived) // Updates on each failed item (and reason for it)
        .OnSkippedItem(StatisticHook.OnSkippedItemReceived) // Updates on each skipped item (and reason for it)
        .OnProgressSteps(StatisticHook.OnProgressStepsReceived) // Updates on each job's progress steps (percentage progress and state)
        .Create();

Transfer options

Use the UploadDirectoryOptions and UploadFileOptions to upload a directory or file to a specified location with custom options.

Skip Top Level Directory

Use SkipTopLevelDirectory to upload the entire contents of a source directory but not create a directory:

  • Values- True or False
  • Default value- False
  • Can be set on a directory transfer

Transfer Retry Policy Definition

Use TransferRetryPolicyDefinition to define transfer retry behavior:

  • Values- No retry, Linear, or Exponential
  • Default value- No retry
  • Can be set on each type of transfer
Copy
// UploadDirectoryOptions with Linear policy example.
     var transferJobOptions = new UploadDirectoryOptions
    {            TransferRetryPolicyDefinition = TransferRetryPolicyDefinition.LinearPolicy(deltaBackoff: TimeSpan.FromSeconds(1), maxAttempts: 10)
     };

Exclusion Policy

Use ExclusionPolicy to determine if a file is or is not excluded from a transfer:

  • Values- define your own exclusion policy by providing your own implementation of interface.
  • Default value- by default the exclusion policy is empty, no files are being excluded.
  • Can be set on a directory transfer

Note: If all files in a source directory are excluded by the exclusion policy, the directory will not be created.

Copy
// Implement custom exclusion policy.
public class CustomExclusionPolicy: IFileExclusionPolicy
{
    public async Task<bool> ShouldExcludeAsync(IFileReference fileReference)
    {
        // Exclude files containing "TestCase"
        if (fileReference.AbsolutePath.Contains("TestCase"))
        {
            return await Task.FromResult(true);
        }
        
        // Exclude pdf files
        if (fileReference.SystemProperties["Extension"].ToString().EndsWith("pdf"))
        {
            return await Task.FromResult(true);
        }
        return await Task.FromResult(false);
    }
}
// Set ExclusionPolicy on options.
// UploadDirectoryOptions 
var transferJobOptions = new UploadDirectoryOptions
{           
    ExclusionPolicy = new TestCaseFilesExclusionPolicy()
};

Upload a directory

Use the UploadDirectoryAsync() method to upload a directory to a specified location.

Note: Using the method to transfer a whole disk data set may contain hidden files and folders such as, $RECYCLE.BIN. This hidden content may change the quantity of transferred items in the statistics.

Copy
var transferJobId = Guid.NewGuid();
var source = new DirectoryPath(@"C:\your_source_directory"); 
var destination = new DirectoryPath(@\\files.<YourCompanyName>.relativity.one\T001\your_destination_directory); // Destination requires path in Relativity Fileshare UNC notation
var progressHandler = CreateProgressHandler(); // Your implementation of ITransferProgressHandler. Optional, however it's strongly recommended you track and log progress of the transfer, as Relativity does not log PII data, which includes file paths

TransferJobResult result = await transferClient.UploadDirectoryAsync(transferJobId, source, destination, progressHandler, default);

Upload a directory with custom options

Use the UploadDirectoryOptions to upload a directory to a specified location with custom options.

Skip Top Level Directory

Use the SkipTopLevelDirectory option to upload the entire contents of a source directory but does not create a directory.

Copy
private static async Task UploadDirectoryAsync(ITransferFullPathClient transferClient)
                   {
                             DirectoryPath sourceDirectory = GetSource();
                             DirectoryPath destinationDirectory = GetDestination();

                             ITransferProgressHandler progressHandler = GetProgressHandler();

                             var transferJobOptions = new UploadDirectoryOptions
                             {
                                      SkipTopLevelDirectory = true
                             };

                             TransferJobResult result = await transferClient
                                      .UploadDirectoryAsync(_TRANSFER_JOB_ID, sourceDirectory, destinationDirectory,transferJobOptions, progressHandler, default)
                                      .ConfigureAwait(false);
                   }

Upload a file

Use the UploadFileAsync() method to upload a file to a specified location.

Copy
var transferJobId = Guid.NewGuid();
var source = new FilePath(@"C:\your_source_directory\file.txt"); 
var destination = new DirectoryPath(@\\files.<YourCompanyName>.relativity.one\T001\your_destination_directory); // Destination requires path in Relativity Fileshare UNC notation
var progressHandler = CreateProgressHandler(); // Your implementation of ITransferProgressHandler. Optional, however it's strongly recommended you track and log progress of the transfer, as Relativity does not log PII data, which includes file paths

TransferJobResult result = await transferClient.UploadFileAsync(transferJobId, source, destination, progressHandler, default);

Download a file

Use the .DownloadFileAsync() method to download a file to a specified location.

Copy
// Every transfer job in Relativity requires a unique id
Guid _TRANSFER_JOB_ID = Guid.NewGuid();

DownloadDirectoryOptions options = new DownloadDirectoryOptions
{
    TransferRetryPolicyDefinition = TransferRetryPolicyDefinition.NoRetryPolicy()
};

FilePath sourceFile = new FilePathPath(@"\\files2.t001.contoso.relativity.one\T001\your_source_directory\your_file.txt");
DirectoryPath destinationDirectory = new DirectoryPath(@"C:\your_destination_directory");

TransferJobResult result = await transferClient
                .DownloadFileAsync(_TRANSFER_JOB_ID, sourceFile, destinationDirectory, options, progressHandler, default);
                .ConfigureAwait(false);
    Notes:
  • Downloading from legacy fileshare requires a synchronization step with an intermediate storage. During this step, file names will be checked against additional requirements.
  • If file names contain invalid characters, Transfer.SDK will report them as skipped and indicate that the name is invalid. To proceed, rename the files and create a new transfer.
  • The list of invalid characters is specified in Microsoft's documentation.

Download a directory

Use the .DownloadDirectoryAsync() method to download a directory to a specified location.

Copy
// Every transfer job in Relativity requires a unique id
Guid _TRANSFER_JOB_ID = Guid.NewGuid();

DownloadDirectoryOptions options = new DownloadDirectoryOptions
{
    SkipTopLevelDirectory = true,
    TransferRetryPolicyDefinition = TransferRetryPolicyDefinition.NoRetryPolicy()
};

DirectoryPath sourceDirectory = new DirectoryPath(@"\\files2.t001.contoso.relativity.one\T001\your_source_directory");
DirectoryPath destinationDirectory = new DirectoryPath(@"C:\your_destination_directory");

TransferJobResult result = await transferClient
                .DownloadDirectoryAsync(_TRANSFER_JOB_ID, sourceDirectory, destinationDirectory, options, progressHandler, default);
                .ConfigureAwait(false);

Job based workflow

Job base is a workflow that allows users of Relativity.Transfer.SDK to perform a transfer from an existing job. This is needed when you do not know the destination/source path on Relativity Fileshare.

Note: Job based workflow only supports transferring directories.

Complete the following steps to use the job based workflow:

  1. Declare the workflow when creating the Relativity.Transfer.SDK client
  2. Copy
    var clientName = <Your_company_name>

    var authenticationProvider = CreateAuthenticationProvider(); // Provide us with authentication method to Relativity (whether it's Basic, OAuth, etc.)

    // build Transfer SDK client supporting job based workflow  
    var transferClient = TransferClientBuilder  
        .JobBasedWorkflow    
        .WithAuthentication(authenticationProvider)  
        .WithClientName(clientName)  
        .Build();
  3. You can use an existing job ID (e.g. from previous transfers), or you can register a new job using TransferJobBuild, and providing the job identifier and destination path.
  4. Copy
    var jobBuilder = new TransferJobBuilder(authenticationProvider);
    var transferJobId = Guid.NewGuid();
    var destinationPath = <Path_To_Relativity_Fileshare> // e.g.@\\files.<YourCompanyName>.relativity.one\T001\your_destination_directory

    //registers a new job with the specified destination path  
    await jobBuilder.NewJob().CreateUploadDirectoryJobAsync(transferJobId, destinationPath)
            .ConfigureAwait(false);
  5. To create a new job based on an existing one, provide the previous job ID and the destination path will be taken from there.
  6. Copy
    var existingJobId = <Job_Id_From_Previous_Transfer>
    var newJobId = Guid.NewGuid();

    //registers a new job from existing job with the specified destination path  
    await jobBuilder.FromExistingJob(existingJobId).CreateUploadJobAsync(newJobId)  
        .ConfigureAwait(false);
  7. Use the UploadDirectoryAsync() or DownloadDirectoryAsync() method and provide the local path.
Copy
var transferJobId = <Your_registered_transfer_job_id> 
var sourcePath = <Your_local_path> // e.g. C:\\User\Documents\DirectoryToTransfer
var cancellationToken = <Cancellation_Token>

//do the upload
var result = await transferClient.UploadDirectoryAsync(transferJobId, sourcePath, ConsoleStatisticHook.GetProgressHandler(), cancellationToken).ConfigureAwait(false);