Client library for the Draftable document comparison API
$ dotnet add package Draftable.CompareAPI.ClientA thin .NET client for the Draftable API which wraps all available endpoints and handles authentication and signing.
The documentation and subsequent examples are provided for C#, however, any CLR-based language is supported (e.g. F#, PowerShell, VB.NET).
See the full API documentation for an introduction to the API, usage notes, and other reference material.
using (var comparisons = new Comparisons("<yourAccountId>", "<yourAuthToken>")) {
var comparison = comparisons.Create(
Comparisons.Side.FromURL("https://api.draftable.com/static/test-documents/paper/left.pdf", "pdf"),
Comparisons.Side.FromURL("https://api.draftable.com/static/test-documents/paper/right.pdf", "pdf")
);
Console.WriteLine($"Comparison created: {comparison}");
// Generate a signed viewer URL to access the private comparison. The expiry
// time defaults to 30 minutes if the ValidFor parameter is not provided.
var viewerURL = comparisons.SignedViewerURL(comparison.Identifier);
Console.WriteLine($"Viewer URL (expires in 30 mins): {viewerURL}");
}
Method calls immediately validate parameters. The following exceptions are thrown on validation failure:
ArgumentNullExceptionArgumentOutOfRangeExceptionDisposing the Comparisons client results in subsequent requests throwing ObjectDisposedException. Any in-progress requests will be cancelled and throw OperationCanceledException.
Async.Task, which when awaited, will complete successfully or throw an exception.The API client class, Comparisons, is thread-safe.
The package provides a namespace, Draftable.CompareAPI, with which a Comparisons instance can be created for your API account.
Comparisons provides methods to manage the comparisons for your API account and return individual Comparison objects.
Creating a Comparisons instance differs slightly based on the API endpoint being used:
using Draftable.CompareAPI;
// Draftable API (default endpoint)
var comparisons = new Comparisons(
"<yourAccountId>", // Replace with your API credentials from:
"<yourAuthToken>" // https://api.draftable.com/account/credentials
);
// Draftable API regional endpoint or Self-hosted
var comparisons = new Comparisons(
"<yourAccountId>", // Replace with your API credentials from the regional
"<yourAuthToken>", // Draftable API endpoint or your Self-hosted container
'https://draftable.example.com/api/v1' // Replace with the endpoint URL
);
The Comparisons instance can be disposed by calling Dispose().
For API Self-hosted you may need to suppress TLS certificate validation if the server is using a self-signed certificate (the default).
GetAll()List<Comparison> of all your comparisons, ordered from newest to oldest. This is potentially an expensive operation.Get(string identifier)Comparison or raises a NotFoundException exception if the specified comparison identifier does not exist.Comparison objects have the following properties:
Identifier: stringLeft: Comparison.Side / Right: Comparison.SideFileType: stringSourceURL: string (optional)nullDisplayName: string (optional)nullIsPublic: boolCreationTime: DateTimeExpiryTime: DateTime (optional)nullReady: boolIf a Comparison is ready (i.e. it has been processed) it has the following additional properties:
ReadyTime: DateTimeFailed: boolErrorMessage: string (only present if Failed)string identifier = "<identifier>";
try {
var comparison = comparisons.Get(identifier);
Console.WriteLine(
"Comparison '{0}' ({1}) is {2}.",
identifier,
comparison.IsPublic ? "public" : "private",
comparison.Ready ? "ready" : "not ready"
);
if (comparison.Ready) {
Console.WriteLine(
"The comparison took {0} seconds.",
(comparison.ReadyTime.Value - comparison.CreationTime).TotalSeconds
);
if (comparison.Failed.Value) {
Console.WriteLine(
"The comparison failed with error: {0}",
comparison.ErrorMessage
);
}
}
} catch (Comparisons.NotFoundException) {
Console.WriteLine("Comparison '{0}' does not exist.", identifier);
}
Delete(string identifier)NotFoundException exception if no such comparison exists.var allComparisons = comparisons.GetAll();
var oldestComparisons = allComparisons.OrderBy(comparison => comparison.CreationTime).Take(10).ToList();
Console.WriteLine("Deleting oldest {0} comparisons ...", oldestComparisons.Count);
foreach (var comparison in oldestComparisons) {
comparisons.Delete(comparison.Identifier);
Console.WriteLine("Comparison '{0}' deleted.", comparison.Identifier);
}
Create(Comparisons.Side left, Comparisons.Side right, string identifier = null, bool isPublic = false, TimeSpan expires = null)Comparison representing the newly created comparison.Create accepts the following arguments:
left / rightidentifier (optional)null, the API will automatically generate a unique identifierisPublic (optional)false or unspecified authentication is required to view the comparisontrue the comparison can be accessed by anyone with knowledge of the URLexpires (optional)null, the comparison will never expire (but may be explicitly deleted)The following exceptions may be raised in addition to parameter validation exceptions:
BadRequestExceptionidentifier already in use)The two most common static constructors for creating Comparisons.Side objects are:
Comparisons.Side.FromFile(Stream fileStream, string fileType, string displayName = null)Comparisons.Side for a locally accessible file.Comparisons.Side.FromURL(string sourceURL, string fileType, string displayName = null)Comparisons.Side for a remotely accessible file referenced by URL.These constructors accept the following arguments:
fileStream (FromFile only)sourceURL (FromURL only)fileTypepdfdocx, docm, doc, rtfpptx, pptm, ppttxtdisplayName (optional)var comparison = comparisons.Create(
Comparisons.Side.FromURL("https://domain.com/path/to/left.pdf", "pdf"),
Comparisons.Side.FromFile("path/to/right/file.docx", "docx"),
// Expire this comparison in 2 hours (default is no expiry)
expires: TimeSpan.FromHours(2)
);
Console.WriteLine($"Created comparison: {comparison}");
PublicViewerURL(string identifier, bool wait = false)SignedViewerURL(string identifier, TimeSpan validFor = null, bool wait = false)Both methods use the following common parameters:
identifierwait (optional)false or unspecified, the viewer will show an error if the identifier does not existtrue, the viewer will wait for a comparison with the provided identifier to existidentifier is never createdThe SignedViewerURL method also supports the following parameters:
validFor (optional)See the displaying comparisons section in the API documentation for additional details.
var identifier = '<identifier>';
// Retrieve a signed viewer URL which is valid for 1 hour. The viewer will wait
// for the comparison to exist in the event processing has not yet completed.
string viewerUrl = comparisons.SignedViewerURL(identifier, TimeSpan.FromHours(1), wait: true);
Console.WriteLine($"Viewer URL (expires in 1 hour): {viewerUrl}");
GenerateIdentifier()
Generates a random unique comparison identifierExports are created and accessed using Comparisons.RunExport method and asynchronous variant: Comparisons.RunExportAsync.
Export RunExport(string comparisonIdentifier, ExportKind kind, bool includeCoverPage = true)
Returns an Export instance representing the newly created export. This method needs the following parameters:
comparisonIdentifier - identifier of comparison, for which we run the exportkind - kind of the export we intend to run. Following values are supported here:
left - content of the left comparison side, with deletions highlights appliedright - content of the right comparison side, with insertions highlights appliedcombined - content of left and right document, placed side by sidesingle_page - comparison content in single page mode.includeCoverPage - relevant only for combined comparison, indicates whether it should include a cover pageThere can be multiple exports of the same type, created for the same comparison.
Class Export represents a single export. It has the following properties:
Identifier - Identifier of the export itself (note that it is different from the comparison ID).Comparison - Identifier of the comparison used for running this exportUrl - Download url of the export documentKind - Export kind. Supported values: single_page, combined, left, right.Ready - Indicates if processing of the export request has completed.IncludeCoverPage - Indicates whether cover page should be included for combined exportsFailed - Indicates if export has failedErrorMessage - Error message for failed exports. This is set to null for successful exports.Existing exports are retrieved using Comparisons.GetExport method.
Export GetExport(string exportIdentifier)
Retrieves existing export by its identifier. Note that the export returned here may not be ready yet.The library respects any Network Settings defined in your application's configuration file, as well as any operating system proxy server configuration (e.g. as configured in Internet Settings).
In addition, the Comparisons class provides a constructor which allows for customisation of the Net.Http.HttpClientHandler instance used internally via an Action<HttpClientHandler> callback.
If connecting to an API Self-hosted endpoint which is using a self-signed certificate (the default) you will need to suppress certificate validation. The recommended approach is to import the self-signed certificate into the certificate store of your operating system, which will ensure the .NET runtime trusts the certificate.
Alternatively, you can suppress certificate validation by providing a server certificate validation callback to the ServicePointManager instance. The simplest implementation is to disable all certificate validation for all TLS connections in the process. For example:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Disabling certificate validation in production environments is strongly discouraged as it significantly lowers security. We only recommend using this approach in development environments if configuring a CA signed certificate for API Self-hosted is not possible.