Si hay algo interesante en utilizar google docs(para mi gusto) es el detalle de tener tus documentos disponibles a cualquier hora y donde sea, no importando si tienes laptop o una memoria usb para andar cargando tus chivas. Pero, ¿Qué tal si trabajamos un poco desde afuera utilizando su API?.
En ésta, como en muchas otras ocasiones, utilizaré Ruby, es práctico, sencillo y muy expresivo. Veamos como obtener la lista de los documentos que tenemos en google docs.
Para comenzar indaguemos un poco acerca del API, lo primero que tenemos que hacer es autenticarnos con google, para ello debemos conectarnos al servidor de google y pedir una autenticación a ClientLogin con SSL y unos datos que pasaremos por POST.
En Ruby necesitamos añadir net/http y net/https para las conexiones, además de rexml/document para parsear lo que escupe el servidor. Podríamos hacer todo a mano, con sockets y todo el rollo, pero en ésta ocasión nos centraremos en el uso del API de google docs y no en las conexiones de red en si. Según la documentación, necesitamos mandar por POST a www.google.com/accounts/ClientLogin los siguientes datos:
- accountType: Es el tipo de cuenta, es decir, si la cuenta es de google o es una cuenta de un dominio específico hospedado en google. Los posibles valores son: GOOGLE, para una cuenta tipo deathwarr17@google.com. HOSTED, para una cuenta como deathwarrior@deathwarrior.net que hospedo en google gracias a google domains. O el último valor puede ser HOSTED_OR_GOOGLE cubriendo ambas posibilidades intentando la autenticación en ese orden.
- Email: Es eso mismo, el correo electrónico completo, como deathwarrior@deathwarrior.net o deathwarr17@gmail.com.
- Passwd: No necesita explicarse.
- service: El servicio de google donde queremos autenticarnos, para nuestros fines google docs, aunque en realidad debemos pasar “writely”.
- source: El cual es una cadena en el que indicamos a google quien es el intermediario entre Juan Pérez y el servidor de google, o sea, nuestro programa. Según la documentación, debe de seguir el siguiente formato: Compañía-Programa-Version. Yo lo he bautizado así: LIDSOL-RubyGoogleDocs-0.1b. Mucha imaginación, ¿apoco no?.
Bien, ahora veamos como queda en Ruby nuestra conexión con google:
require 'net/http'
require 'net/https'
require 'rexml/document'
URLAUTH = 'https://www.google.com/accounts/ClientLogin'
SERVICE = 'writely'
ATYPE = 'GOOGLE'
SOURCE = 'LIDSOL-RubyGoogleDocs-0.1b'
email = 'deathwarr17@gmail.com'
passwd = 'mypass'
postdata = "accountType=#{ATYPE}&Email=#{email}&Passwd=#{passwd}&"
postdata += "service=#{SERVICE}&source=#{SOURCE}"
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
parsed = URI.parse(URL)
conn = NET::HTTP.new(parsed.host, parsed.port)
conn.use_ssl = true
resp, data = conn.post(parsed.path, postdata, headers)
Ahora bien, la conexión y la petición fueron hechas. Tenemos que checar si el servidor nos autenticó(código 200) o si falló(otró código que no me acuerdo). Lo anterior con un simple if:
if resp.code.to_i == 200
puts "Autenticado"
else
puts "Autenticación fallida, revise sus datos"
Kernel::exit(-1)
end
Se supone que si nuestros datos eran correctos, en la respuesta del servidor viene una cadena de autenticación que usaremos cada que hagamos una petición a google. Nos regresa 3 valores separados por un salto de linea y el que necesitamos viene al último. Los otros dos -dice google- no se usan. Entonces, para obtener la cadena haremos lo siguiente:
auth = data.split[2].split('=')[1]
Esa cadena de autenticación debemos pasarla en los headers de ahora en adelante del siguiente modo:
headers = { 'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => "GoogleLogin auth=#{auth}",
'GData-Version' => GDV
}
Y GDV siempre debe ser 2 según el API, por lo que lo agregamos en nuestras constantes del principio. Recuerden, debe ser un 2 “letra” no un 2 “número” o Ruby chillará por ahí.
Ahora bien, una vez autorizados, podemos obtener la lista de documentos guardados en google docs. Para ello necesitamos pedirla a docs.google.com/feed/documents/private/full. Lo haremos del siguiente modo:
URLDOCS = 'https://docs.google.com/feed/documents/private/full'
parsed = URI.parse(URLDOCS)
conn = NET::HTTP.new(parsed.host, parsed.port)
conn.use_ssl = true
resp, data = conn.get(parsed.path, headers)
Y lo que nos regresará el servidor será un documento XML que debemos analizar para obtener los títulos de los documentos. Si vemos en modo plano lo que nos regresa, vemos que los documentos están dentro de los tags feed/entry/title. Por lo que usaremos rexml/document para analizarlo.
doc = REXML::Document.new(data)
titles = []
doc.elements.each('feed/entry/title') do |title|
titles << title.text
puts title.text
end
Y con eso imprimimos la lista de los documentos. Por ahora ha sido una práctica muy sencilla. En artículos posteriores analizaremos opciones más avanzadas para trabajar con los documentos.
El código completo lo dejo a continuación:
require 'net/http'
require 'net/https'
require 'rexml/document'
URLAUTH = 'https://www.google.com/accounts/ClientLogin'
URLDOCS = 'https://docs.google.com/feed/documents/private/full'
SERVICE = 'writely'
ATYPE = 'GOOGLE'
SOURCE = 'LIDSOL-RubyGoogleDocs-0.1b'
email = 'deathwarr17@gmail.com'
passwd = 'mypass'
postdata = "accountType=#{ATYPE}&Email=#{email}&Passwd=#{passwd}&"
postdata += "service=#{SERVICE}&source=#{SOURCE}"
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
parsed = URI.parse(URL)
conn = NET::HTTP.new(parsed.host, parsed.port)
conn.use_ssl = true
resp, data = conn.post(parsed.path, postdata, headers)
if resp.code.to_i == 200
puts "Autenticado"
else
puts "Autenticación fallida, revise sus datos"
Kernel::exit(-1)
end
auth = data.split[2].split('=')[1]
headers = { 'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => "GoogleLogin auth=#{auth}",
'GData-Version' => GDV
}
parsed = URI.parse(URLDOCS)
conn = NET::HTTP.new(parsed.host, parsed.port)
conn.use_ssl = true
resp, data = conn.get(parsed.path, headers)
doc = REXML::Document.new(data)
titles = []
doc.elements.each('feed/entry/title') do |title|
titles << title.text
puts title.text
end
Comentarios, sugerencias y demás, no duden en escribir. Un saludo y un genial inicio de año.