Commit c7ca0bdf authored by Sriram Karra's avatar Sriram Karra

Improve the error handling of google contacts batch operatons

parent 5d31deea
......@@ -22,6 +22,7 @@ import copy, logging, re
from abc import ABCMeta, abstractmethod
from folder import Folder
from contact_gc import GCContact
import xml.etree.ElementTree as ET
import utils
import atom, gdata.contacts.client
......@@ -673,6 +674,23 @@ class BatchState:
def set_operation (self, op):
self.operation = op
def handle_interrupted_feed (self, resp_xml):
resp = ET.fromstring(resp_xml)
ffc = utils.find_first_child
resp_title = ffc(resp, utils.QName_GNS0('title'), ret='node').text
resp_intr = ffc(resp, utils.QName_GNS3('interrupted'), ret='node')
parsed = int(resp_intr.attrib['parsed'])
reason = resp_intr.attrib['reason']
entry = self.f.entry[parsed]
logging.error('The server encountered a %s while processing ' +
'the feed. The reason given is: %s', resp_title,
resp_intr)
logging.error('The problematic entry is likely this one: %s',
utils.pretty_xml(str(entry)))
def process_batch_response (self, resp):
"""resp is the response feed obtained from a batch operation to
google.
......@@ -693,8 +711,7 @@ class BatchState:
bid = entry.batch_id.text if entry.batch_id else None
if not entry.batch_status:
# There is something seriously wrong with this request.
logging.error('Unknown fatal error in response. Full resp: %s',
entry)
self.handle_interrupted_feed(str(resp))
success = False
continue
......
......@@ -18,13 +18,13 @@
## not, see <http://www.gnu.org/licenses/>.
##
import iso8601, logging, os, re
import iso8601, logging, os, re, xml.dom.minidom
time_start = "1980-01-01T00:00:00.00+00:00"
def yyyy_mm_dd_to_pytime (date_str):
## FIXME: Temporary hack to ensure we have a yyyy-mm-dd format. Google
## allows the year to be skipped. Outlook crates a problem. We bridge the
## allows the year to be skipped. Outlook creates a problem. We bridge the
## gap by inserting '1887' (birth year of Srinivasa Ramanujan)
import pywintypes
......@@ -241,3 +241,52 @@ def classify_email_addr (addr, domains):
logging.warning('Invalid email_domains specification.')
return (res['home'], res['work'], res['other'])
##
## Some XML parsing and manipulation routines
##
def pretty_xml (x):
x = xml.dom.minidom.parseString(x).toprettyxml()
lines = x.splitlines()
lines = [s for s in lines if not re.match(s.strip(), '^\s*$')]
return os.linesep.join(lines)
def find_first_child (root, tag, ret='text'):
"""
Look for the first child of root with specified tag and return it. If
ret is 'text' then the value of the node is returned, else, the node
is returned as an element.
"""
for child in root.iter(tag):
if ret == 'text':
return child.text
else:
return child
return None
GNS0_NAMESPACE="http://www.w3.org/2005/Atom"
GNS1_NAMESPACE="http://schemas.google.com/g/2005"
GNS2_NAMESPACE="http://schemas.google.com/contact/2008"
GNS3_NAMESPACE="http://schemas.google.com/gdata/batch"
def unQName (name):
res = re.match('{.*}(.*)', name)
return name if res is None else res.group(1)
def QName (namespace, name):
return '{%s}%s' % (namespace, name)
def QName_GNS0 (name):
return QName(GNS0_NAMESPACE, name)
def QName_GNS1 (name):
return QName(GNS1_NAMESPACE, name)
def QName_GNS2 (name):
return QName(GNS2_NAMESPACE, name)
def QName_GNS3 (name):
return QName(GNS3_NAMESPACE, name)
import getopt, logging, os, sys, traceback
CUR_DIR = os.path.abspath('')
ASYNK_BASE_DIR = os.path.abspath('..')
## Being able to fix the sys.path thusly makes is easy to execute this
## script standalone from IDLE. Hack it is, but what the hell.
DIR_PATH = os.path.abspath('../')
EXTRA_PATHS = [os.path.join(DIR_PATH, 'lib'), os.path.join(DIR_PATH, 'asynk')]
sys.path = EXTRA_PATHS + sys.path
from state import Config
import gdata, gdata.data, gdata.contacts.data, gdata.contacts.client
from pimdb_gc import GCPIMDB
from contact_gc import GCContact
from folder_gc import BatchState
import utils
import xml.etree.ElementTree as ET
def main ():
tests = TestGCContact()
tests.test_print_item('https://www.google.com/m8/feeds/contacts/karra.etc%40gmail.com/full/5e6d5ad30b0e2008')
tests.test_batch_error()
# tests.test_print_item('https://www.google.com/m8/feeds/contacts/karra.etc%40gmail.com/full/5e6d5ad30b0e2008')
# tests.test_find_item('https://www.google.com/m8/feeds/contacts/karra.etc%40gmail.com/full/4b814c4c8f0c1558')
#tests.test_sync_status()
#tests.test_del_item('http://www.google.com/m8/feeds/contacts/karra.etc%40gmail.com/base/1fabc8309273c15')
......@@ -17,14 +29,13 @@ def main ():
class TestGCContact:
def __init__ (self):
from pimdb_gc import GCPIMDB
from state import Config
config = Config('../config.json', './state.test.json')
self.conf = Config(asynk_base_dir=ASYNK_BASE_DIR, user_dir='./')
# The following is the 'Gout' group on karra.etc@gmail.com
# self.gid = 'http://www.google.com/m8/feeds/groups/karra.etc%40gmail.com/base/41baff770f898d85'
self.gid = 'http://www.google.com/m8/feeds/groups/karra.etc%40gmail.com/base/6'
# self.gid =
# 'http://www.google.com/m8/feeds/groups/karra.etc%40gmail.com/base/6'
self.gid = 'http://www.google.com/m8/feeds/groups/karra.etc%40gmail.com/base/204eb63b8e2dad8d'
# Parse command line options
try:
......@@ -51,11 +62,61 @@ class TestGCContact:
print 'Password cannot be blank'
try:
self.pimdb = GCPIMDB(config, user, pw)
self.pimdb = GCPIMDB(self.conf, user, pw)
except gdata.client.BadAuthentication:
print 'Invalid credentials. WTF.'
raise
def test_batch_error (self):
fobj = self.find_group(self.gid)
con0 = GCContact(fobj)
con0.set_firstname('Namo Narayananaya')
gce0 = con0.get_gce()
con = GCContact(fobj)
con.set_firstname('Ayeshwarya')
con.set_birthday('abcd"ef')
# con.set_anniv('1978-05-31 %s est n il y a %d ans')
# con.set_birthday('1980-08-10')
gce = con.get_gce()
feed = self.pimdb.new_feed()
feed.add_insert(entry=gce0, batch_id_string="DeadBeef")
feed.add_insert(entry=gce0, batch_id_string="DeadBeef")
feed.add_insert(entry=gce, batch_id_string="DeadBeef")
b = BatchState(1, feed, op='insert', sync_tag="asynk:testgcex:ex")
print 'Request: ', utils.pretty_xml(str(feed))
rr = self.pimdb.exec_batch(feed)
print 'Response: ', utils.pretty_xml(str(rr))
for entry in rr.entry:
print entry.batch_status
if entry.batch_status:
print 'Code: ',entry.batch_status.code
print 'Reason: ', entry.batch_status.reason
else:
self.handle_interrupted_feed(feed, str(rr))
def handle_interrupted_feed (self, feed, resp_xml):
resp = ET.fromstring(resp_xml)
ffc = utils.find_first_child
resp_title = ffc(resp, utils.QName_GNS0('title'), ret='node').text
resp_intr = ffc(resp, utils.QName_GNS3('interrupted'), ret='node')
parsed = int(resp_intr.attrib['parsed'])
reason = resp_intr.attrib['reason']
entry = feed.entry[parsed]
logging.error('The server encountered a %s while processing ' +
'the feed. The reason given is: %s', resp_title,
resp_intr)
logging.error('The problematic entry is likely this one: %s',
utils.pretty_xml(str(entry)))
def find_group (self, gid):
# sample.print_groups()
self.gout, ftype = self.pimdb.find_folder(gid)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment