Cloud Services Example 4: Read Variables and Dialogs from the Component File
In some use cases you will need to find out if a HotDocs template contains particular variables or dialogs. In this example, you will:
- Examine how to interact with a HotDocs Component file
- Extract information about the Component File
- Extract Information about the components contained in the Component File.
Full source code for this example is available at the bottom of the page.
Example Source Code on GitHub
The CloudServicesAPIExample4ComponentInfo example project is available on GitHub, in the HotDocs-Samples repository.
Requirements
Assembling a document in Cloud Services requires that a HotDocs Template Package File is uploaded to the service. For information on uploading a Template Package File, see Example 1: Uploading a Template.
1. Create a new Console Project in Visual Studio
Before you start, create a new Console Application project for this example named CloudServicesAPIExample4ComponentInfo. You will gradually add code to this project until you have a working example.
2. Reference the DLLs
In the project, edit Program.cs. Add the following using statements at the beginning of the file:
- using System.Net.Http;
- using System.Threading.Tasks;
3. Add Subscriber Information
In the Main method of the class, add variables with your Cloud Services Subscriber information:
var subscriberId = "example-subscriber-id";
var signingKey = "example-signing-key";
These items are:
- Subscriber ID – your ID for signing in to Cloud Services. This is typically the name of your organisation.
- Signing Key – the unique key associated with your user account. This is used for signing requests made to Cloud Services.
The Subscriber ID and Signing Key are provided when you sign up for Cloud Services. If you do not have this information, please contact your HotDocs Sales Representative.
You will use these items later when calculating the HMAC and creating the HTTP request to access Cloud Services.
4. Add Data Required for retrieving Component Information
In the Main method, add the following seven variables and values:
var timestamp = DateTime.UtcNow;
var packageId = "ed40775b-5e7d-4a51-b4d1-32bf9d6e9e29";
var templateName = "";
var sendPackage = false;
var billingRef = "ExampleBillingRef";
var includeDialogs = true;
These items are:
- Timestamp (DateTime) – the time at which the assembly was started. This is used by Cloud Services to check that an assembly request has not expired.
- Package ID (string) – the unique ID for a template previously uploaded to Cloud Services.
- Template Name (string) – may be empty when assembling a document, as the template name is the same as the package file name.
- Send Package (boolean) – Must be false when assembling a document. Indicates that a package is being sent with a request.
- Billing Reference (string) – a custom logging message. This is typically used to identify the user making the request, for billing purposes.
- includeDialogs (boolean) – include dialog information in the response. If the setting is false, only variable information is returned.
You will use these items later when calculating the HMAC and creating the HTTP request to access Cloud Services.
5. Calculate the HMAC
Every request made to Cloud Services must use an HMAC to verify the request. To calculate the HMAC, you must add two new methods to Program.cs:
- CalculateHMAC – performs the HMAC calculation and returns the HMAC string.
- CanonicalizeParameters – takes the parameters for the request and converts them into the standardised format required to calculate the HMAC.
5.1 Create the CalculateHMAC method
To calculate the HMAC, add a new method, CalculateHMAC:
public string CalculateHMAC(string signingKey, params object[] parametersList) { var key = Encoding.UTF8.GetBytes(signingKey); var parameterStringToSign = Canonicalize(parametersList); var parameterBytesToSign = Encoding.UTF8.GetBytes(parameterStringToSign); byte[] signature; using (var hmac = new System.Security.Cryptography.HMACSHA1(key)) { signature = hmac.ComputeHash(parameterBytesToSign); } return Convert.ToBase64String(signature); }
This method calculates the HMAC by:
- Converting the signing key to a byte array
- Canonicalizing the parameters
- Converting the canonicalized parameters to a byte array
- Creating a new HMAC using the converted signing key and converted canonicalized parameters.
- Returning the new HMAC signature as a string.
For this method to work, you must create a second method to canonicalize the parameters used to create the HMAC, before the HMAC is calculated.
5.2 Create the Canonicalize method
The canonicalize method takes all of the parameters required to calculate the HMAC, converts them to appropriately-formatted strings, and joins the strings together. The complete method looks as follows:
public string Canonicalize(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()); }
5.3 Retrieve the CalculateHMAC method
Add the following line to the Main method, to retrieve the calculated HMAC:
var hmac = CalculateHMAC(signingKey, timestamp, subscriberId,
packageId, templateName, sendPackage, billingRef, includeDialogs);
To calculate the HMAC for retrieving component file information,
you must pass in the following parameters:
- signingKey (string) – the unique key associated with your user account, used for signing requests to Cloud Services.
- timestamp (DateTime) – the time at which the assembly was started. This is used by Cloud Services to check that a request has not expired.
- subscriberId (string) – your ID for signing in to Cloud Services.
- packageId (string) – the unique ID the Template requested.
- templateName (string) – may be empty when assembling a document, as the template name is the same as the package file name.
- sendPackage (boolean) – Must be false when retrieving component information using the REST service.
- billingRef (string) – a custom logging message. This is typically used to identify the user making the request, for billing purposes.
- includeDialogs (boolean) – include dialog information in the results
Once the HMAC has been calculated, you can use it when assembling a document using Cloud Services.
6. Create the Request
6.1 Create the CreateHttpRequestMessage method
Create a new method, CreateHttpRequestMessage:
private static HttpRequestMessage CreateHttpRequestMessage(string hmac, string subscriberId, string packageId, DateTime timestamp, string billingRef, bool includeDialogs)
{
}
This method takes the following parameters:
- hmac – the HMAC calculated in the step above.
- subscriberId – your ID for signing in to Cloud Services.
- packageId – the unique ID of the Template Package used for the request.
- timestamp – the time at which the assembly was started.
- billingRef – a custom logging message, typically used when identifying users for billing purposes.
- includeDialogs – include dialog information with the Component Information response from Web Services.
6.2 Create the Upload URL
To connect to the Cloud Services REST service, you use the following address:
- https://cloud.hotdocs.ws/hdcs
The URL used when retrieving Component File information from Cloud Services must also contain specific parameters:
- SubscriberId
- packageId
- hmac
- includeDialogs
- billingRef (optional)
To the Cloud Services REST service address, the parameters above are added. The complete Component Information URL is formatted as follows:
- https://cloud.hotdocs.ws/hdcs/componentinfo/subscriberId/packageId?billingRef=billingRefincludeDialogs=includeDialogs
In your project, add the following line to create the upload URL:
var uploadUrl = string.Format("https://cloud.hotdocs.ws/hdcs/componentinfo/{0}/{1}?billingRef={2}&includeDialogs={3}",
subscriberId, packageId, billingRef, includeDialogs);
6.3 Create the Request
Next, you create the request, using a HttpRequestMessage:
var request = new HttpRequestMessage { }
The request must have the following properties:
- RequestUri – the URI to which the request is sent. This is the Component Information URL, created above.
- Method – the method used to send the request. Retrieving Component File information requires the GET method.
Add the properties to the request, like this:
RequestUri = new Uri(uploadUrl),
Method = HttpMethod.Get
Finally, add the following content headers Content headers to the request:
request.Content.Headers.TryAddWithoutValidation("x-hd-date", timestamp.ToString("yyyy-MM-ddTHH:mm:ssZ"));
request.Content.Headers.TryAddWithoutValidation("Authorization", hmac);
You can now return the request message. The full method looks as follows:
private static HttpRequestMessage CreateHttpRequestMessage(string hmac, string subscriberId, string packageId, DateTime timestamp, string billingRef, bool includeDialogs) { var componentInfoUrl = string.Format("https://cloud.hotdocs.ws/hdcs/componentinfo/{0}/{1}?billingRef={2}&includeDialogs={3}", subscriberId, packageId, billingRef, includeDialogs); var request = new HttpRequestMessage { RequestUri = new Uri(componentInfoUrl), Method = HttpMethod.Get }; // Add request headers request.Headers.TryAddWithoutValidation("x-hd-date", timestamp.ToString("yyyy-MM-ddTHH:mm:ssZ")); request.Headers.TryAddWithoutValidation("Authorization", hmac); return request; }
8. Read Component File Information from the Response
To read the Component File information from the response, create a new method, GetComponentInfoXmlFromResponse:
private static string GetComponentInfoXmlFromResponse(Task<HttpResponseMessage> response)
{
}
Get the XML content from the response:
var responseStream = response.Result.Content;
Finally, return the XML response content as a string:
return responseStream.ReadAsStringAsync().Result;
The Component File information XML can now be used elsewhere in the application.
9. Complete the Main method
To complete the main method and retrieve the Component File information, add the following lines to the method.
First, calculate the HMAC using the CalculateHMAC method:
var hmac = CalculateHMAC(signingKey, timestamp, subscriberId, packageId, templateName, sendPackage, billingRef, includeDialogs);
Next, create the request message using the CreateHttpRequestMessage method:
var request = CreateHttpRequestMessage(hmac, subscriberId, packageId, timestamp, billingRef, includeDialogs);
Once the request message is created, use HttpClient to send the request and retrieve the response:
var client = new HttpClient();
var response = client.SendAsync(request);
When the response is received, get the Component File information from the response using the GetComponentInfoXmlFromResponse method and write it to the console:
var componentInfoXml = GetComponentInfoXmlFromResponse(response);
Console.WriteLine(componentInfoXml);
The application can now be tested.
Testing
To test retrieving Component Information using Cloud Services:
- Set the current project as the Startup Project (Right-click the CloudServicesAPIExample4ComponentInfo 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 Component Information XML appears.
Source Code (C#)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Http; using System.Threading.Tasks; namespace CloudServicesAPIExample4ComponentInfo { 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 = "ed40775b-5e7d-4a51-b4d1-32bf9d6e9e29"; string templateName = ""; var sendPackage = false; var billingRef = "ExampleBillingRef"; var includeDialogs = false; // Generate HMAC using Cloud Services signing key var hmac = CalculateHMAC(signingKey, timestamp, subscriberId, packageId, templateName, sendPackage, billingRef, includeDialogs); // Create component information request var request = CreateHttpRequestMessage(hmac, subscriberId, packageId, timestamp, billingRef, includeDialogs); // Send component information request to Cloud Service var client = new HttpClient(); var response = client.SendAsync(request); Console.WriteLine("Component File Info:" + response.Result.StatusCode); //Read Component Information from response and write to console var componentInfoXml = GetComponentInfoXmlFromResponse(response); Console.WriteLine(componentInfoXml); Console.ReadKey(); } private static string GetComponentInfoXmlFromResponse(Task<httpresponsemessage> response) { var responseStream = response.Result.Content; return responseStream.ReadAsStringAsync().Result; } private static HttpRequestMessage CreateHttpRequestMessage(string hmac, string subscriberId, string packageId, DateTime timestamp, string billingRef, bool includeDialogs) { var componentInfoUrl = string.Format("https://cloud.hotdocs.ws/hdcs/componentinfo/{0}/{1}?billingRef={2}&includeDialogs={3}", subscriberId, packageId, billingRef, includeDialogs); var request = new HttpRequestMessage { RequestUri = new Uri(componentInfoUrl), Method = HttpMethod.Get }; // Add request headers request.Headers.TryAddWithoutValidation("x-hd-date", timestamp.ToString("yyyy-MM-ddTHH:mm:ssZ")); request.Headers.TryAddWithoutValidation("Authorization", hmac); return request; } public static string CalculateHMAC(string signingKey, params object[] paramList) { byte[] key = Encoding.UTF8.GetBytes(signingKey); string stringToSign = Canonicalize(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 Canonicalize(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()); } } }