Cloud Services Example 6: Retrieve a template from HotDocs Hub
Document assembly is the process of merging user input with a HotDocs template to create a final, completed document. In this example, you will assemble a document using a HotDocs Template Package file retrieved from HotDocs Hub. Full source code for this example is available at the bottom of the page.
Prerequisites
- This example requires a Cloud Services account enabled to use HotDocs Hub.
- You should complete Cloud Services Example 2: Assemble a Document before starting this example.
Example Source Code on GitHub
The CloudServicesAPIExampleRetrieveFromHub example project is available in the HotDocs-Samples GitHub repository.
1. Create a new Console Application Project in Visual Studio
To begin, create a new Visual Studio Solution and Console Application project. Name the project CloudServicesAPIExampleRetrieveFromHub. You will use this solution and project to create the example.
2. Reference the DLLs
In the project, edit Program.cs. Add the following additional using statements at the beginning of the file:
- using System.Net.Http;
- using System.IO;
3. Copy Methods from the Example 2 Project
You should complete Cloud Services Example 2 before starting this example, as you require the methods created in the previous example. You will then change these methods so that the template file required for the assembly is retrieved from HotDocs Hub instead of Cloud Services.
- Open your CloudServicesAPIExample2DocumentAssembly project.
- Copy the following methods from Program.cs:
- Main
- CreateHttpRequestMessage
- CreateAssembleUrl
- SaveAssembledDocuments
- GetAnswers
- CalculateHMAC
- CanonicalizeParameters
- Paste the methods into the CloudServicesAPIExampleRetrieveFromHub project.
- Save your changes.
4. Modify the Assemble Document Request URL
The URL for the Assemble Document request must be changed so that Cloud Services knows to retrieve the requested template from the HotDocs Hub rather than its own template cache. To do this, you must add a new RetrieveFromHub parameter to the URL.
Modify the URL
The CreateAssembleUrl method constructs the assemble request URL. In this method, change the assembleUrl string by adding the RetrieveFfromHub parameter:
var assembleUrl = string.Format("https://cloud.hotdocs.ws/hdcs/assemble/{0}/{1}/{2}?billingRef={3}&format={4}&retrievefromhub=bytemplateid", subscriberId, packageId, templateName, billingRef, format);
In this example, you use the ByTemplateId value for RetrieveFromHub. This instructs Cloud Services to retrieve the current live version of the template from HotDocs Hub. See Cloud Services API, Assemble Document method for other options used by RetrieveFromHub.
5. Get the Template ID from HotDocs Hub
In a typical solution, the IDs for templates uploaded to Hub are retrieved using the Template Hub API. For this example, copy the Package ID directly from the HotDocs Hub user interface:
- Open HotDocs Hub in a web browser
- Login to the Hub.
- Navigate to the Templates section.
- Click a template in the Templates List.
- In the Template Details page, copy the GUID at the end of the page URL, e.g. https://example.hotdocshub.com/HotDocsTemplateHubUi/Templates/Details/a7c236d4-8zr4-4a94-b161-2dj3c3dadkla
The GUID at the end of the URL is the Package ID for the current live version of the template.
6. Modify the Package ID
To access the template selected above, change the PackageId variable in the Main method to the Package ID copied above. For example:
var packageId = "a7c236d4-8zr4-4a94-b161-2dj3c3dadkla";
Test
To test assembling a document using a template retrieved from HotDocs Hub:
- Set the current project as the Startup Project (Right-click the CloudServicesAPIExampleRetrieveFromHub project in Visual Studio and select Startup Project from the drop-down menu)
- Press F5 to run the Project. The console opens. If the test has been successful, the message Assemble: OK message appears.
- Navigate to the location of the assembled document, C:\temp\.
Source Code (C#)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net.Http; using System.Threading.Tasks; namespace CloudServicesAPIExample2DocumentAssembly { class Program { static void Main(string[] args) { // Cloud Services Subscription Details string subscriberId = "example-subscriber-id"; string signingKey = "example-signing-key"; // HMAC calculation data var timestamp = DateTime.UtcNow; var packageId = "example-package-id"; var format = "Native"; var templateName = ""; var sendPackage = false; var billingRef = "ExampleBillingRef"; Dictionary<string, string> settings = new Dictionary<string, string> { {"UnansweredFormat", "[Variable]"} }; // Generate HMAC using Cloud Services signing key var hmac = CalculateHMAC(signingKey, timestamp, subscriberId, packageId, templateName, sendPackage, billingRef, format, settings); // Create assemble request var request = CreateHttpRequestMessage(hmac, subscriberId, packageId, timestamp, format, billingRef, settings); // Send assemble request to Cloud Services var client = new HttpClient(); var response = client.SendAsync(request); Console.WriteLine("Assemble:" + response.Result.StatusCode); // Save Assembled Documents var saveDocumentsTask = Task.Run(async () => { await SaveAssembledDocuments(response.Result); }); saveDocumentsTask.Wait(); Console.ReadKey(); } private static HttpRequestMessage CreateHttpRequestMessage(string hmac, string subscriberId, string packageId, DateTime timestamp, string format, string billingRef, Dictionary<string, string> settings) { var assembleUrl = CreateAssembleUrl(subscriberId, packageId, format, billingRef, settings); var request = new HttpRequestMessage { RequestUri = new Uri(assembleUrl), Method = HttpMethod.Post, Content = GetAnswers() }; // Add request headers request.Headers.Add("x-hd-date", timestamp.ToString("r")); request.Headers.TryAddWithoutValidation("Content-Type", "text/xml"); request.Headers.TryAddWithoutValidation("Authorization", hmac); request.Headers.Add("Keep-Alive", "false"); return request; } private static string CreateAssembleUrl(string subscriberId, string packageId, string format, string billingRef, Dictionary<string, string> settings) { var assembleUrl = string.Format("https://cloud.hotdocs.ws/hdcs/assemble/{0}/{1}?billingRef={2}&format={3}&retrievefromhub=bytemplateid", subscriberId, packageId, billingRef, format); var assembleUrlWithSettings = new StringBuilder(assembleUrl); foreach (var kv in settings) { assembleUrlWithSettings.AppendFormat("&{0}={1}", kv.Key, kv.Value ?? ""); } return assembleUrlWithSettings.ToString(); } static async Task SaveAssembledDocuments(HttpResponseMessage response) { MultipartStreamProvider multipartStream = await response.Content.ReadAsMultipartAsync(); foreach (var attachment in multipartStream.Contents) { Stream writeAttachmentStream = await attachment.ReadAsStreamAsync(); using (FileStream output = new FileStream(@"C:\temp\" + attachment.Headers.ContentDisposition.FileName, FileMode.Create)) { writeAttachmentStream.CopyTo(output); } } } private static StringContent GetAnswers() { return new StringContent(@"<?xml version="" 1.0""="" encoding="" utf-8""="" standalone="" yes""="" ?><answerset version="" 1.1""=""><answer name="" textexample-t""><textvalue>Hello World</textvalue></answer></answerset>"); } public static string CalculateHMAC(string signingKey, params object[] paramList) { byte[] key = Encoding.UTF8.GetBytes(signingKey); string stringToSign = CanonicalizeParameters(paramList); byte[] bytesToSign = Encoding.UTF8.GetBytes(stringToSign); byte[] signature; using (var hmac = new System.Security.Cryptography.HMACSHA1(key)) { signature = hmac.ComputeHash(bytesToSign); } return Convert.ToBase64String(signature); } public static string CanonicalizeParameters(params object[] paramList) { if (paramList == null) { throw new ArgumentNullException(); } var strings = paramList.Select(param => { if (param is string || param is int || param is Enum || param is bool) { return param.ToString(); } if (param is DateTime) { DateTime utcTime = ((DateTime)param).ToUniversalTime(); return utcTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); } if (param is Dictionary<string, string>) { var sorted = ((Dictionary<string, string>)param).OrderBy(kv => kv.Key); var stringified = sorted.Select(kv => kv.Key + "=" + kv.Value).ToArray(); return string.Join("\n", stringified); } return ""; }); return string.Join("\n", strings.ToArray()); } } }