Dane Google na liniach kolejowych

Eric Bidelman, zespół ds. interfejsów API danych Google
Luty 2009 r.

Wprowadzenie

„Gdzie jest Ruby na liście bibliotek klienta?”

Zachęcony gwałtownym apetytem naszych programistów i wieloletnią popularnością Ruby on Rails (RoR) mój kolega, Jeff Fisher, sfałszował bibliotekę Ruby z ogromnej głębokości góry Doom. Nie jest to kompleksowa biblioteka klienta, ale obsługuje takie podstawowe elementy, jak uwierzytelnianie czy podstawowa manipulacja XML. Wymaga też bezpośredniej pracy z kanałem Atom za pomocą modułu REXML i ścieżki XPath.

Odbiorcy

Ten artykuł jest przeznaczony dla deweloperów zainteresowanych korzystaniem z interfejsów API danych Google przy użyciu systemu Ruby, w szczególności Ruby on Rails. Zakładamy, że czytelnik ma wiedzę na temat języka programowania Ruby i platformy programowania stron internetowych The Rails. Postaram się skupić na interfejsie Documents List API dla większości przykładów, ale te same koncepcje można zastosować do dowolnych interfejsów Data API.

Pierwsze kroki

Wymagania

Instalowanie biblioteki narzędzi Google Data Ruby

Aby uzyskać bibliotekę, możesz pobrać źródło biblioteki bezpośrednio z projektu na serwerze lub zainstalować klejnot:

sudo gem install gdata

Wskazówka: aby mieć pewność, że klejnot został prawidłowo zainstalowany, uruchom gem list --local.

Uwierzytelnianie

ClientLogin

ClientLogin pozwala aplikacji na automatyczne logowanie użytkowników na konta Google lub G Suite. Po zweryfikowaniu danych logowania użytkownika Google wysyła token uwierzytelniania, który może zostać użyty w kolejnych żądaniach do interfejsu API. Token pozostaje ważny przez określony czas zdefiniowany w dowolnej usłudze Google, z którą współpracujesz. Ze względów bezpieczeństwa i dla wygody użytkowników używaj tylko ClientLogin podczas tworzenia zainstalowanych aplikacji komputerowych. W przypadku aplikacji internetowych zalecamy korzystanie z AuthSub lub OAuth.

Biblioteka Ruby zawiera klasę klienta dla każdego z interfejsów API. Na przykład możesz użyć tego fragmentu kodu, aby zalogować się do user@gmail.com za pomocą interfejsu Documents List Data API:

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')

Zobacz pełną listę wdrożonych klas usługi. Jeśli usługa nie ma klasy klienta, użyj klasy GData::Client::Base. Na przykład ten kod wymusza na użytkownikach logowanie się za pomocą konta 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)

Uwaga: domyślnie accountType używa biblioteki HOSTED_OR_GOOGLE. Możliwe wartości to HOSTED_OR_GOOGLE, HOSTED lub GOOGLE.

Jedną z wad każdego z nich jest to, że w przypadku nieudanych prób logowania aplikacja może wysyłać testy CAPTCHA. W takim przypadku możesz usunąć błąd, wywołując metodę clientlogin() z dodatkowymi parametrami: client.clientlogin(username, password, captcha_token, captcha_answer). Więcej informacji o korzystaniu z CAPTCHA znajdziesz w pełnej dokumentacji uwierzytelniania dla zainstalowanych aplikacji.

AuthSub

Generowanie adresu 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)

Poprzedni blok kodu tworzy w authsub_link ten adres URL:

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

Możesz też użyć metody authsub_url obiektu klienta. Każda klasa usługi ma ustawiony domyślny atrybut authsub_scope, więc nie trzeba określać własnej.

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)

Poprzedni blok kodu tworzy ten adres URL:

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

Uaktualnienie tokena jednorazowego do tokena sesji

AuthSub przekieruje użytkownika z powrotem do http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN po przyznaniu mu dostępu do danych. Zwróć uwagę, że adres URL to po prostu next_url z tokenem jednorazowym dołączonym jako parametr zapytania.

Następnie wymień token jednorazowy na token długoterminowej sesji:

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]

Zabezpieczenie AuthSub jest bardzo podobne. Jedynym dodatkiem jest ustawienie klucza prywatnego przed uaktualnieniem tokena:

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]

Uwaga: jeśli chcesz używać tokenów bezpiecznych, ustaw żądanie jednorazowego tokena jako secure=true. Przeczytaj sekcję Generowanie adresu URL AuthSubRequest powyżej.

Zarządzanie tokenami

AuthSub udostępnia 2 dodatkowe moduły obsługi: AuthSubTokenInfo i AuthSubRevokeToken do zarządzania tokenami. AuthSubTokenInfo przydaje się do sprawdzania poprawności tokena. AuthSubRevokeToken umożliwia użytkownikom odłączenie dostępu do ich danych. Sprawdzoną metodą jest używanie AuthSubRevokeToken. Obie metody są obsługiwane w bibliotece Ruby.

Aby wysłać zapytanie do metadanych tokena:

client.auth_handler.info

Aby unieważnić token sesji:

client.auth_handler.revoke

Pełne informacje o AuthSub znajdziesz w pełnej dokumentacji uwierzytelniania AuthSub dla aplikacji internetowych.

OAuth

W momencie tworzenia tego artykułu do protokołu GData::Auth nie dodano protokołu OAuth.

Korzystanie z protokołu OAuth w bibliotece narzędzi powinno być stosunkowo proste podczas korzystania z wtyczki oauth-plugin lub ruby oauth gem. W obu przypadkach musisz utworzyć obiekt GData::HTTP::Request i przekazać do niego nagłówek Authorization wygenerowany przez każdą bibliotekę.

Dostęp do kanałów

GET (pobieranie danych)

Po skonfigurowaniu obiektu klienta użyj jego metody get(), aby utworzyć zapytanie dotyczące pliku danych Google. XPath może służyć do pobierania określonych elementów Atom. Oto przykład pobrania dokumentów Google użytkownika:

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 (tworzenie nowych danych)

Użyj metody post() klienta, aby utworzyć nowe dane na serwerze. Ten przykład dodaje użytkownika new_writer@example.com jako współpracownika do dokumentu o identyfikatorze: 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 (aktualizowanie danych)

Aby zaktualizować dane na serwerze, użyj metody put() klienta. Poniższy przykład spowoduje zaktualizowanie tytułu dokumentu. Przy założeniu, że masz plik danych z poprzedniego zapytania.

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)

USUŃ

Aby usunąć tag <atom:entry> lub inne dane z serwera, użyj metody delete(). Poniższy przykład spowoduje usunięcie dokumentu. Kod zakłada, że masz dokument z poprzedniego zapytania.

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)

Tworzenie nowej aplikacji Rails

Pierwszym ćwiczeniem w celu utworzenia nowej aplikacji Rails jest utworzenie generatorów rusztowań w celu utworzenia plików MVC. Po tym czasie aplikacja korzysta z rake db:migrate, aby konfigurować tabele bazy danych. Ponieważ jednak nasza aplikacja będzie wysyłać zapytania do interfejsu Google Documents List API, aby znaleźć dane, nie musimy już używać szkieletów lub baz danych. Zamiast tego utwórz nową aplikację i prosty kontroler:

rails doclist
cd doclist
ruby script/generate controller doclist

i wprowadź te zmiany w pliku config/environment.rb:

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

Pierwszy wiersz odłącza wartość ActiveRecord z aplikacji. Drugi wiersz wczytuje klejnot gdata przy uruchomieniu.

Na koniec zdecydowałem się połączyć trasę domyślną („/”) z działaniem documents w DoclistController. Dodaj ten wiersz do config/routes.rb:

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

Uruchom kontroler

Nie generowaliśmy rusztowania, więc ręcznie dodaj działanie o nazwie „all” do DoclistController w app/controllers/doclist_controller.rb.

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

i utwórz all.html.erb w app/views/doclist/:

<%= @foo %>

Uruchom serwer WWW i zacznij programować

Teraz możesz uruchomić domyślny serwer WWW przez wywoływanie ruby script/server. Jeśli wszystko jest w porządku, ustaw w przeglądarce adres http://localhost:3000/ na „I pity the foo!”.

Wskazówka: nie zapomnij usunąć nazwy public/index.html lub zmienić jej nazwy.

Gdy wszystko będzie gotowe, zobacz moje ostateczne wersje: DoclistController i ApplicationController w projekcie dla menedżera projektu DocList. Możesz też użyć narzędzia ContactsController, które obsługuje wywołania interfejsu Google Contacts API.

Podsumowanie

Najtrudniejszym etapem tworzenia aplikacji Google Data Rails jest konfigurowanie kolejek. Zbliża się jednak czas na wdrożenie aplikacji. Zalecamy korzystanie z serwera mod_rails na serwerze Apache. Konfiguracja, instalacja i uruchomienie są bardzo łatwe. Możesz zacząć działać od razu.

Zasoby

Dodatek

Przykłady

Menedżer DocList to pełna próbka Ruby on Rails, która obejmuje tematy omówione w tym artykule. Pełny kod źródłowy jest dostępny w usłudze hostingowej.