Données Google sur Rails

Eric Bidelman, équipe Google Data APIs
Février 2009

Introduction

"Où se trouve Ruby dans la liste des bibliothèques clientes ?"

Motivé par l'enthousiasme féroce de nos développeurs et par la popularité durable de Ruby on Rails (RoR), mon collègue Jeff Fisher a créé une bibliothèque d'utilitaires Ruby des profondeurs infimes du mont Doom. Il ne s'agit pas d'une bibliothèque cliente complète, mais elle gère les principes de base tels que l'authentification et la manipulation XML de base. Vous devez également travailler directement avec le flux Atom à l'aide du module REXML et de XPath.

Audience

Cet article est destiné aux développeurs souhaitant accéder aux API Google Data à l'aide de Ruby, en particulier Ruby on Rails. Il suppose que le lecteur maîtrise le langage de programmation Ruby et le framework de développement Web Rails. Je me concentre sur l'API Documents List pour la plupart des exemples, mais les mêmes concepts peuvent être appliqués à n'importe quelle API Data.

Premiers pas

Conditions requises

Installer la bibliothèque d'utilitaires Google Data Ruby

Pour obtenir la bibliothèque, vous pouvez la télécharger directement à partir du projet qui héberge le projet ou installer le gem:

sudo gem install gdata

Conseil: Pour une bonne mesure, exécutez gem list --local pour vérifier que le gem a bien été installé.

Authentification

ClientLogin

ClientLogin permet à votre application de connecter de manière automatisée les utilisateurs à leur compte Google ou G Suite. Lors de la validation des identifiants de l'utilisateur, Google émet un jeton d'authentification à référencer dans les requêtes API ultérieures. Le jeton reste valide pendant une période définie, définie par le service Google avec lequel vous travaillez. Pour des raisons de sécurité et pour offrir une expérience optimale à vos utilisateurs, vous ne devez utiliser ClientLogin que lors du développement d'applications de bureau installées. Pour les applications Web, il est préférable d'utiliser AuthSub ou OAuth.

La bibliothèque Ruby possède une classe cliente pour chacune des API. Par exemple, utilisez l'extrait de code suivant pour vous connecter à user@gmail.com à l'API Documents List Data:

client = GData::Client::DocList.new
client.clientlogin('user@gmail.com', 'pa$$word')

The YouTube Data API would be:

client = GData::Client::YouTube.new
client.clientlogin('user@gmail.com', 'pa$$word')

Consultez la liste complète des classes de service mises en œuvre. Si un service ne possède pas de classe client, utilisez la classe GData::Client::Base. Par exemple, le code suivant oblige les utilisateurs à se connecter avec un compte G Suite.

client_login_handler = GData::Auth::ClientLogin.new('writely', :account_type => 'HOSTED')
token = client_login_handler.get_token('user@example.com', 'pa$$word', 'google-RailsArticleSample-v1')
client = GData::Client::Base.new(:auth_handler => client_login_handler)

Remarque: Par défaut, la bibliothèque utilise HOSTED_OR_GOOGLE pour accountType. Les valeurs possibles sont HOSTED_OR_GOOGLE, HOSTED ou GOOGLE.

L'un des inconvénients de l'utilisation de ClientLogin est que votre application peut recevoir des tests CAPTCHA en cas d'échec de tentative de connexion. Dans ce cas, vous pouvez gérer l'erreur en appelant la méthode clientlogin() avec ses paramètres supplémentaires : client.clientlogin(username, password, captcha_token, captcha_answer). Consultez la documentation complète sur l'authentification pour les applications installées pour en savoir plus sur le traitement des captchas.

AuthSub

Générer l'URL AuthSubRequest

scope = 'http://www.google.com/calendar/feeds/'
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)

Le bloc de code précédent crée l'URL suivante dans authsub_link :

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F&session=1&secure=0

Vous pouvez également utiliser la méthode authsub_url de l'objet client. Chaque classe de service a défini un attribut authsub_scope par défaut. Il n'est donc pas nécessaire d'en spécifier un.

client = GData::Client::DocList.new
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
domain = 'example.com'  # force users to login to a G Suite hosted domain
authsub_link = client.authsub_url(next_url, secure, sess, domain)

Le bloc de code précédent crée l'URL suivante:

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fdocs.google.com%2Ffeeds%2F&session=1&secure=0&hd=example.com

Convertir un jeton à usage unique en jeton de session

AuthSub redirigera l'utilisateur vers http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN une fois qu'il aura autorisé l'accès à ses données. Notez que l'URL est simplement notre next_url, avec le jeton à usage unique ajouté en tant que paramètre de requête.

Remplacez ensuite le jeton à usage unique par un jeton de session de longue durée:

client.authsub_token = params[:token] # extract the single-use token from the URL query params
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Secure AuthSub est très similaire. Le seul ajout consiste à définir votre clé privée avant de mettre à niveau le jeton:

PRIVATE_KEY = '/path/to/private_key.pem'

client.authsub_token = params[:token]
client.authsub_private_key = PRIVATE_KEY
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Remarque: Pour utiliser des jetons sécurisés, veillez à définir secure=true lorsque vous demandez un jeton à usage unique. Consultez la section Générer l'URL de requête AuthSub ci-dessus.

Gestion des jetons

AuthSub fournit deux gestionnaires supplémentaires, AuthSubTokenInfo et AuthSubRevokeToken pour la gestion des jetons. AuthSubTokenInfo est utile pour vérifier la validité d'un jeton. AuthSubRevokeToken permet aux utilisateurs d'interrompre l'accès à leurs données. Votre application doit utiliser AuthSubRevokeToken comme bonne pratique. Les deux méthodes sont compatibles avec la bibliothèque Ruby.

Pour interroger les métadonnées d'un jeton:

client.auth_handler.info

Pour révoquer un jeton de session:

client.auth_handler.revoke

Consultez la documentation complète sur l'authentification AuthSub pour les applications Web pour en savoir plus sur AuthSub.

OAuth

Au moment de la rédaction de cet article, OAuth n'a pas été ajouté au module GData::Auth.

L'utilisation d'OAuth dans la bibliothèque d'utilitaires doit être relativement simple lorsque vous utilisez le module oauth-plugin ou le gem OAuth de Rails. Dans les deux cas, vous devez créer un objet GData::HTTP::Request et lui transmettre l'en-tête Authorization généré par chaque bibliothèque.

Accéder aux flux

GET (extraction des données)

Une fois que vous avez configuré un objet client, utilisez sa méthode get() pour interroger un flux de données Google. XPath peut être utilisé pour récupérer des éléments Atom spécifiques. Voici un exemple de récupération des documents Google d'un utilisateur:

feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml

feed.elements.each('entry') do |entry|
  puts 'title: ' + entry.elements['title'].text
  puts 'type: ' + entry.elements['category'].attribute('label').value
  puts 'updated: ' + entry.elements['updated'].text
  puts 'id: ' + entry.elements['id'].text
  
  # Extract the href value from each <atom:link>
  links = {}
  entry.elements.each('link') do |link|
    links[link.attribute('rel').value] = link.attribute('href').value
  end
  puts links.to_s
end

POST (création de données)

Utilisez la méthode post() d'un client pour créer des données sur le serveur. L'exemple suivant permet d'ajouter new_writer@example.com en tant que collaborateur du document portant l'ID doc_id.

# Return documents the authenticated user owns
feed = client.get('http://docs.google.com/feeds/documents/private/full/-/mine').to_xml
entry = feed.elements['entry']  # first <atom:entry>

acl_entry = <<-EOF
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl='http://schemas.google.com/acl/2007'>
  <category scheme='http://schemas.google.com/g/2005#kind'
    term='http://schemas.google.com/acl/2007#accessRule'/>
  <gAcl:role value='writer'/>
  <gAcl:scope type='user' value='new_writer@example.com'/>
</entry>
EOF

# Regex the document id out from the full <atom:id>.
# http://docs.google.com/feeds/documents/private/full/document%3Adfrk14g25fdsdwf -> document%3Adfrk14g25fdsdwf
doc_id = entry.elements['id'].text[/full\/(.*%3[aA].*)$/, 1]
response = client.post("http://docs.google.com/feeds/acl/private/full/#{doc_id}", acl_entry)

PUT (mise à jour des données)

Pour mettre à jour les données sur le serveur, utilisez la méthode put() d'un client. L'exemple suivant met à jour le titre d'un document. Nous partons du principe que vous disposez d'un flux issu d'une requête précédente.

entry = feed.elements['entry'] # first <atom:entry>

# Update the document's title
entry.elements['title'].text = 'Updated title'
entry.add_namespace('http://www.w3.org/2005/Atom')
entry.add_namespace('gd','http://schemas.google.com/g/2005')

edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
response = client.put(edit_uri, entry.to_s)

SUPPRIMER

Pour supprimer une <atom:entry> ou d'autres données du serveur, utilisez la méthode delete(). L'exemple suivant permet de supprimer un document. Le code suppose que vous disposez d'une entrée de document d'une requête précédente.

entry = feed.elements['entry'] # first <atom:entry>
edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
client.headers['If-Match'] = entry.attribute('etag').value  # make sure we don't nuke another client's updates
client.delete(edit_uri)

Créer une application Rails

En général, le premier exercice de création d'une application Rails implique l'exécution des générateurs d'échafaudage pour créer vos fichiers MVC. Ensuite, il exécute rake db:migrate pour configurer vos tables de base de données. Toutefois, comme notre application interroge l'API Google Documents List pour obtenir des données, nous n'avons pas vraiment besoin d'échafaudages génériques ni de bases de données. À la place, créez une application et un contrôleur simple:

rails doclist
cd doclist
ruby script/generate controller doclist

et apportez les modifications suivantes à config/environment.rb :

config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
config.gem 'gdata', :lib => 'gdata'

La première ligne dissocie ActiveRecord de l'application. La deuxième ligne charge le gem gdata au démarrage.

Enfin, j'ai choisi d'associer l'itinéraire par défaut (/) à l'action documents dans DoclistController. Ajoutez cette ligne à config/routes.rb :

map.root :controller => 'doclist', :action => 'all'

Démarrer une manette

Comme nous n'avons pas généré d'échafaudage, ajoutez manuellement une action appelée "all" à DoclistController dans app/controllers/doclist_controller.rb.

class DoclistController < ApplicationController
  def all
    @foo = 'I pity the foo!'
  end
end

et créez all.html.erb sous app/views/doclist/ :

<%= @foo %>

Déclencher le serveur Web et commencer le développement

Vous devriez maintenant pouvoir démarrer le serveur Web par défaut en appelant ruby script/server. Si tout va bien, pointez http://localhost:3000/ dans votre navigateur pour afficher « I pity the foo! ».

Conseil: N'oubliez pas de supprimer ou de renommer public/index.html.

Une fois que les choses fonctionnent, regardez ma version finale de DoclistController et ApplicationController pour le viaur du projet DocList Manager. Vous pouvez également examiner ContactsController, qui gère les appels à l'API Google Contacts.

Conclusion

Le plus difficile dans la création d'une application Google Data Rails est de configurer Rails. Toutefois, le déploiement de votre application prend une seconde seconde. Pour cela, nous vous recommandons vivement d'utiliser mod_rails pour Apache. Il est très facile à configurer, à installer et à exécuter. Lancez-vous en un rien de temps !

Ressources

Annexe

Exemples

Le gestionnaire de listes de documents est un exemple complet de Ruby on Rails illustrant les sujets abordés dans cet article. Le code source complet est disponible sur le site d'hébergement de projet.