The Google Health Data API allows client applications to view and update Health content in the form of Google Data API feeds. Your client application can request a user's profile as a CCR document, retrieve a list of notices sent to their account, or send a notice to a user.
In addition to providing some background on the capabilities of the Health Data API, this document provides examples for interacting with the API using the .NET client library. This document provides .NET specifics for performing the general actions described in the developer's guide overview page.
This document is intended for programmers who want to write client applications using the Google Data API .NET client library that can interact with Google Health.
To use the .NET client library, you'll need the .NET 1.2.x runtime, and you should also be current on all patches. After downloading the client library, you'll find the DLLs you need to get started in the lib/Release subdirectory of the distribution.
There are some ASP samples available for download to demonstrate basic usage of the Health API.
To compile the examples in this document into your own code, you'll need to import the following namespaces:
<%@ Import Namespace="System.Net" %> <%@ Import Namespace="System.Text.RegularExpressions" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Security.Cryptography" %> <%@ Import Namespace="System.Security.Cryptography.X509Certificates" %> <%@ Import Namespace="Google.GData.Client" %> <%@ Import Namespace="Google.GData.Health" %> <%@ Import Namespace="Google.GData.Extensions" %>
Note: Before you can interact with Google Health, you must register your application with Google. Register your domains by reviewing and fill-out the API Terms of Service. Information is also available in the Domain Registration & Signing Requests section of the Getting Started Guide.
AuthSub can be used by web-based applications to establish a link to a user's Google Health profile.
The first step when working with AuthSub is to generate the URL to redirect users to. The following code generates an AuthSub URL for Google Health.
String authSubLink = AuthSubUtil.getRequestUrl("https", "www.google.com",
"/health/authsub", "http://www.example.com/test/Health.aspx",
"https://www.google.com/health/feeds/", true, true);
Here we are using an overloaded version of getRequestUrl that allows us to specify Google Health's AuthSub handler.
After a successful authorization from AuthSub, the user returns to your next URL with a single-use token appended to the end.
For example, http://www.example.com/test/Health.aspx?token=TOKEN_VALUE. Your application should extract the
single-use token from the URL and instantiate a GAuthSubRequestFactory with the appropriate service name.
Either specify the service name for the H9 Sandbox (weaver), or for Google Health (health), as seen here:
AsymmetricAlgorithm getRsaKey()
{
X509Certificate2 cert = new X509Certificate2("/path/to/yourprivatekey.pfx", "pa$$word");
RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;
return privateKey;
}
String singleUseToken = Request.QueryString["token"];
String sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, getRsaKey()).ToString();
GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("health", "Health-Profile-Tester");
authFactory.Token = sessionToken;
authFactory.PrivateKey = getRsaKey();
HealthService service = new HealthService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
Notice that your private key is set before exchanging the single-use token for a session token, and also
saved within authFactory. This will insure subsequent API requests are digitally signed.
AuthSub session tokens do not expire. Your client should store the session token for as long as it is needed by the user. However, If the user wishes to "unlink" their profile, your application should provide a way to revoke access to their data.
This example revokes a session token using the .NET client library, using secure=0 tokens on H9:
AuthSubUtil.revokeToken(sessionToken, null);
Alternatively, if you're using secure tokens on /h9 or /health, use:
AuthSubUtil.revokeToken(sessionToken, authFactory.PrivateKey);
More information on working with AuthSub, see:
Use ClientLogin authentication if your client is a standalone, single-user "installed" client (such as a desktop application).
To interact with /health, set the credentials of your service object as follows:
HealthService service = new HealthService("exampleCo-exampleApp-1");
service.setUserCredentials("email", "password");
In the snippet above, we pass a single argument to the HealthService constructor. The parameter should be set to a
string identifying your application and be of the form companyName-applicationName-versionID.
To interact with /h9, set your credentials as follows:
GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("weaver", "exampleCo-exampleApp-1");
HealthService service = new HealthService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
service.setUserCredentials("email", "password");
More information on working with ClientLogin, see:
Note: Use the same token for all requests in a given session; don't acquire a new token for each Health request.
Note: As described in the ClientLogin documentation, the authentication request may fail and request a CAPTCHA challenge. If you want Google to issue and handle the CAPTCHA challenge, then send the user to https://www.google.com/accounts/DisplayUnlockCaptcha?service=health (rather than to the CAPTCHA-handling URL given in the ClientLogin documentation).
The profile feed allows you to retrieve a CCR document describing a user's health information stored with Google. Each profile is encapsulated inside of an Atom entry. The .NET client library can be used to extract the CCR information and print it to the page:
HealthQuery profileQuery = new HealthQuery("https://www.google.com/health/feeds/profile/default");
profileQuery.Digest = true; // returns a single <atom:entry> containing the user's CCR
try
{
HealthFeed feed = service.Query(profileQuery);
foreach (HealthEntry entry in feed.Entries)
{
XmlNode ccr = (XmlExtension) entry.FindExtension("ContinuityOfCareRecord", "urn:astm-org:CCR");
if (ccr != null)
{
Response.Write("<pre>");
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
xw.Formatting = Formatting.Indented;
ccr.WriteTo(xw);
Response.Write(HttpUtility.HtmlEncode(sw.ToString()));
Response.Write("</pre>");
}
}
}
catch (GDataRequestException e)
{
...
}
While category queries are more efficient, there may be some situations where you may want the user's entire Health profile (see previous example). Use the following to extract specific elements from the CCR:
...
XmlExtension ccrExt = (XmlExtension) entry.FindExtension("ContinuityOfCareRecord", "urn:astm-org:CCR");
XmlDocument ccrXMLDoc = new XmlDocument();
ccrXMLDoc.ImportNode(ccrExt, true);
XmlNamespaceManager ccrNameManager = new XmlNamespaceManager(ccrXMLDoc.NameTable);
ccrNameManager.AddNamespace("ccr", "urn:astm-org:CCR");
// Use xpath to extract specific CCR elements
XmlNodeList meds = ccrExt.Node.SelectNodes(
"//ccr:Body/ccr:Medications/ccr:Medication/ccr:Product/ccr:ProductName/ccr:Text", ccrNameManager);
XmlNodeList conditions = ccrExt.Node.SelectNodes(
"//ccr:Body/ccr:Problems/ccr:Problem/ccr:Description/ccr:Text", ccrNameManager);
XmlNodeList allergies = ccrExt.Node.SelectNodes(
"//ccr:Body/ccr:Alerts/ccr:Alert/ccr:Description/ccr:Text", ccrNameManager);
...
Notices are the primary means by which your application can communicate and interact with the user. They allow you to send informational messages and CCR profile data to a user. Currently, the only thing you can do using AuthSub is post new notices; you can't update or delete notices using AuthSub. ClientLogin allows for reading existing notices, posting new notices, and updating or deleting notices.
Note: This feature is available only with ClientLogin.
You can retrieve notices that your application has access to view using the register feed in conjunction with a profile ID: register/ui/profileID. Note that you can use standard Google Data API query parameters to narrow down the results of this query.
The following code retrieves all of the notices associated with the given profile. It also prints out any attached CCR data appended to a notice.
FeedQuery query = new FeedQuery();
query.Uri = new Uri("https://www.google.com/health/feeds/register/ui/profileID");
AtomFeed healthFeed = service.Query(query);
foreach (AtomEntry entry in healthFeed.Entries)
{
Response.Write("<div>");
Response.Write("From: " + entry.Authors[0].Name + "<br/>");
Response.Write("Date: " + entry.Published + "<br/>");
Response.Write("Subject: " + entry.Title.Text + "<br/>");
Response.Write("Message: " + entry.Content.Content + "<br/>");
foreach (XmlElement element in entry.ExtensionElements)
{
if (element.Name == "ContinuityOfCareRecord")
{
Response.Write("Contains CCR data: <br/>");
Response.Write("<pre>");
StringWriter sw = new System.IO.StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
xw.Formatting = Formatting.Indented;
element.WriteTo(xw);
Response.Write(HttpUtility.HtmlEncode(sw.ToString()));
Response.Write("</pre>");
}
}
Response.Write("</div><br/>");
}
The above also prints out any attached CCR data appended to a notice.
Creating a new notice is accomplished by constructing an Atom entry and sending an HTTP POST to the register feed.
CCR data can be included with your notice. The below code shows adding a CCR fragment from a string variable called ccrXmlString.
AtomEntry newNotice = new AtomEntry();
newNotice.Title.Text = "A test message";
newNotice.Content.Content = "This is a test message.";
// Set the content type if you're using HTML in your message body
//newNotice.Content.Type = "html";
String ccrXmlString =
@"<ContinuityOfCareRecord xmlns='urn:astm-org:CCR'>
<Body>
<Problems>
<Problem>
<DateTime>
<Type>
<Text>Start date</Text>
</Type>
<ExactDateTime>2007-04-04T07:00:00Z</ExactDateTime>
</DateTime>
<DateTime>
<Type>
<Text>Stop date</Text>
</Type>
<ExactDateTime>2008-07-20T07:00:00Z</ExactDateTime>
</DateTime>
<Description>
<Code>
<Value>346.80</Value>
<CodingSystem>ICD9</CodingSystem>
<Version>2004</Version>
</Code>
</Description>
<Status><Text>Active</Text></Status>
<Source>
<Actor>
<ActorID>Harris Smith</ActorID>
<ActorRole>
<Text>Treating clinician</Text>
</ActorRole>
</Actor>
</Source>
</Problem>
</Problems>
</Body>
</ContinuityOfCareRecord>";
XmlDocument ccrDoc = new XmlDocument();
ccrDoc.LoadXml(ccrXmlString);
newNotice.ExtensionElements.Add(new XmlExtension(ccrDoc.DocumentElement));
service.Insert(new Uri("https://www.google.com/health/feeds/register/default"), newNotice);
...