Data::Google::Visualization::DataSource - Google Chart Datasources
version 0.01
Helper class for implementing the Google Chart Tools Datasource Protocol (v0.6)
# Step 1: Create the container based on the HTTP request my $datasource = Data::Google::Visualization::DataSource->new({ tqx => $q->param('tqx'), xda => ($q->header('X-DataSource-Auth') || undef) }); # Step 2: Add data $datasource->datatable( Data::Google::Visualiation::DataSource object ); # Step 3: Show the user... my ( $headers, $body ) = $datasource->serialize; printf("%s: %s\n", @$_) for $headers; print "\n" . $body . "\n";
The Google Visualization API is a nifty bit of kit for generating pretty pictures from your data. By design it has a fair amount of Google-cruft, such as non-standard JSON and stuffing configuration options in to a single CGI query parameter. It's also got somewhat confusing documentation, and some non-obvious rules for generating certain message classes.
Data::Google::Visualization::DataTable takes care of preparing data for the API, but this module implements the Google Chart Tools Datasource Protocol, or Google Visualization API wire protocol, or whatever it is they've decided to call it this week.
This documentation is not laid out like standard Perl documentation, because it needs extra explanation. You should read this whole document sequentially if you wish to make use of it.
There's quite a bit of logic around how to craft a response, how to throw errors, how to throw warnings, etc. After some thought, I have discovered an interface that hopefully won't make you want to throw yourself off a cliff.
At its essence, Google Datasources allow querying clients to specify a lot about what they want the response to look like. This information is specified in the tqx parameter (Request Format) and also somewhat implied by the existence of an X-DataSource-Auth header.
tqx
X-DataSource-Auth
In order to use this module, you will need to create a container for the outgoing data. This is as easy as passing in whatever the caller gave you as their tqx parameter.
You then set any data and any messages you wish to. This is your chance to tell the user they're not logged in, or you can't connect to the database, or - if everything worked out, build and set the Data::Google::Visualization::DataTable object they're ultimately requesting.
Finally, serialize attempts to build the response, checking the messages to see if we should return an error or actual data, and giving you appropriate headers and the body itself.
Our first job is to specify what the response container will look like, and the easiest way to do this is to pass new() the contents of the tqx parameter and the X-DataSource-Auth header.
new()
# Give the user what they requested ->new({ tqx => $q->param('tqx') }); # Be conscientious and pass in contents of X-DataSource-Auth ->new({ tqx => $q->param('tqx'), datasource_auth => $q->header('X-DataSource-Auth') }); # Set it by hand... ->new({ reqId => 3, out => 'json', sig => 'deadbeef' });
new() will set the following object attributes based on this, all based on the Request Format linked above:
reqId
version
0.06
warning
sig
error
out
json
responseHandler
google.visualization.Query.setResponse
outFileName
datasource_auth
Having created our container, we then need to put data in it. There are two types of data - messages, and the DataTable.
Messages are errors or warnings that need to be passed back to the client, but they also have potential to change the rest of the data payload. The following algorithm is used:
1. Have any error messages been added? If so, discard all but the first, set the response status to 'error', and discard the DataTable and all warning messages. We discard all the other messages (error and warning) to prevent malicious data discovery. 2. An integrity check is run on the attributes that have been set. We check the attributes listed above, and generate any needed messages from those. If we generate any error messages, step 1 is rerun. 2. Have any warning messages been added? If so, set the response status to 'warning'. Include all warning messages and the DataTable in the response. 3. If there are no warning or error messages, set the response status to 'ok', and include the DataTable in the response.
When messages are described as discarded, they are not included in the returned body - they're still available to the developer in the returned messages. See the documentation on serialize below.
body
messages
serialize
Messages are added using the add_message method:
add_message
$datasource->add_message({ type => 'error', # Required. Can also be 'warning' reason => 'access_denied', # Required. See Google Docs for allowed options message => 'Unauthorized User', # Optional detailed_message => 'Please login to use the service' # Optional });
The datatable is added via the datatable method:
datatable
$datasource->datatable( $datatable_object );
and must be a Data::Google::Visualization::DataTable object. If you know you've already added an error message, you don't need to set this - it won't be checked.
Up to this point, we've just accumulated data without actually acting on it. If the user has specified some inputs we can't handle, well we haven't checked that yet.
To kick the whole circus off, call serialize.
my ( $headers, $body, $messages ) = $datasource->serialize();
Serialize accepts no arguments, and does not change the state of the underlying object. It returns:
headers
An arrayref or arrayrefs, which in this version of this module will always be:
[[ 'Content-Type', 'text/javascript' ]]
However, don't use that knowledge, as future versions will definitely add new headers, based on other user options - Content-Disposition, for starters. You should return all received headers to the user. As future versions will allow returning of different data types, you must allow control of Content-Type and Content-Disposition to fall to this module in their entirity.
Content-Disposition
Content-Type
A JSON-like string containing the response. Google JSON is not real JSON (see the continually linked documentation), and what's more, this may well be JSONP instead. This string will come back UTF-8 encoded, so make sure whatever you're serving this with doesn't re-encode that.
{ errors => [ { reason => 'not_modified', message => 'Data not modified' } ] warnings => [] }
A hashref of arrayrefs containing all messages raised. You must not show this to the user - it's purely for your own debugging. When we talk about messages being discarded in the "Adding Messages" section, they will turn up here instead. DO NOT MAKE DECISIONS ABOUT WHAT TO RETURN TO THE USER BY POKING THROUGH THIS DATA. The not_modified error is a great example of why not - it is not an error for the user, and the user has to act a certain way on getting it - it's expected in the normal course of use.
not_modified
It'd be nice to support the other data types, but currently Data::Google::Visualization::DataTable serializes its data a little too early which makes this impracticle. I tend to do hassle-related development, so if you are in desparate need of this feature, I recommend emailing me.
If you find a bug, please use this modules page on the CPAN bug tracker to raise it, or I might never see.
Peter Sergeant pete@clueball.com
pete@clueball.com
Data::Google::Visualization::DataTable - for preparing your data
Python library that does the same thing
Google Visualization API.
Github Page for this code
Copyright 2012 Peter Sergeant, some rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Data::Google::Visualization::DataSource, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Data::Google::Visualization::DataSource
CPAN shell
perl -MCPAN -e shell install Data::Google::Visualization::DataSource
For more information on module installation, please visit the detailed CPAN module installation guide.