CORD-2977 Fix failure to resolve generic foreign keys;
Fix exception reporting in xossh

Change-Id: Ie57bba891c6d0176e7174f6e1964bb2ff5e7dfbc
diff --git a/xos/xos_client/xosapi/fake_stub.py b/xos/xos_client/xosapi/fake_stub.py
index 8cd9c53..0970b39 100644
--- a/xos/xos_client/xosapi/fake_stub.py
+++ b/xos/xos_client/xosapi/fake_stub.py
@@ -275,6 +275,27 @@
     def __init__(self, items):
         self.items = items
 
+class FakeElement(object):
+    EQUAL="equal"
+
+    def __init__(self):
+        pass
+
+class FakeElements(object):
+    def __init__(self):
+        self.items = []
+
+    def add(self):
+        el=FakeElement()
+        self.items.append(el)
+        return el
+
+class FakeQuery(object):
+    DEFAULT="default"
+
+    def __init__(self):
+        self.elements = FakeElements()
+
 class FakeStub(object):
     def __init__(self):
         self.id_counter = 1
@@ -285,6 +306,7 @@
             setattr(self, "Create%s" % name, functools.partial(self.create, name))
             setattr(self, "Delete%s" % name, functools.partial(self.delete, name))
             setattr(self, "Update%s" % name, functools.partial(self.update, name))
+            setattr(self, "Filter%s" % name, functools.partial(self.filter, name))
 
 
     def make_key(self, name, id):
@@ -302,6 +324,22 @@
                     items.append(v)
         return FakeItemList(items)
 
+    def filter(self, classname, query, metadata=None):
+        items = []
+        for (k,v) in self.objs.items():
+            (this_classname, id) = k.split(":")
+            if this_classname != classname:
+                continue
+            for q in query.elements.items:
+                iValue = getattr(q, "iValue", None)
+                if (iValue is not None) and getattr(v,q.name)!=iValue:
+                    continue
+                sValue = getattr(q, "sValue", None)
+                if (sValue is not None) and getattr(v, q.name) != sValue:
+                    continue
+                items.append(v)
+        return FakeItemList(items)
+
     def create(self, classname, obj, metadata=None):
         obj.id = self.id_counter
         self.id_counter = self.id_counter + 1
@@ -329,6 +367,7 @@
 class FakeCommonProtos(object):
     def __init__(self):
         self.ID = ID
+        self.Query = FakeQuery
 
 class FakeProtos(object):
     def __init__(self):
diff --git a/xos/xos_client/xosapi/orm.py b/xos/xos_client/xosapi/orm.py
index 7845d14..a50b7ee 100644
--- a/xos/xos_client/xosapi/orm.py
+++ b/xos/xos_client/xosapi/orm.py
@@ -35,13 +35,18 @@
 u=c.xos_orm.User.objects.get(id=1)
 """
 
-import functools
 import os
 import sys
 import time
 
 convenience_wrappers = {}
 
+class ORMGenericContentNotFoundException(Exception):
+    pass
+
+class ORMGenericObjectNotFoundException(Exception):
+    pass
+
 class ORMWrapper(object):
     """ Wraps a protobuf object to provide ORM features """
 
@@ -525,8 +530,19 @@
                        self.reverse_content_type_map[model_name] = ct
 
     def genericForeignKeyResolve(self, content_type_id, id):
+        if content_type_id.endswith("_decl"):
+            content_type_id = content_type_id[:-5]
+
+        if content_type_id not in self.content_type_map:
+            raise ORMGenericContentNotFoundException("Content_type %s not found in self.content_type_map" % content_type_id)
+
         model_name = self.content_type_map[content_type_id]
+
         model = getattr(self, model_name)
+        objs = model.objects.filter(id=id)
+        if not objs:
+            raise ORMGenericObjectNotFoundException("Object %s of model %s was not found" % (id,model_name))
+
         return model.objects.get(id=id)
 
     def add_default_metadata(self, metadata):
diff --git a/xos/xos_client/xosapi/test_orm.py b/xos/xos_client/xosapi/test_orm.py
index 136f71a..93f2155 100644
--- a/xos/xos_client/xosapi/test_orm.py
+++ b/xos/xos_client/xosapi/test_orm.py
@@ -328,6 +328,47 @@
         self.assertNotEqual(tag.content_object, None)
         self.assertEqual(tag.content_object.id, site.id)
 
+    def test_generic_foreign_key_get_decl(self):
+        orm = self.make_coreapi()
+        service = orm.Service(name="myservice")
+        service.save()
+        site = orm.Site(name="mysite")
+        site.save()
+        self.assertTrue(site.id > 0)
+        tag = orm.Tag(service=service, name="mytag", value="somevalue", content_type=site.self_content_type_id + "_decl", object_id=site.id)
+        tag.save()
+        self.assertTrue(tag.id > 0)
+        self.assertNotEqual(tag.content_object, None)
+        self.assertEqual(tag.content_object.id, site.id)
+
+    def test_generic_foreign_key_get_bad_contenttype(self):
+        orm = self.make_coreapi()
+        service = orm.Service(name="myservice")
+        service.save()
+        site = orm.Site(name="mysite")
+        site.save()
+        self.assertTrue(site.id > 0)
+        tag = orm.Tag(service=service, name="mytag", value="somevalue", content_type="does_not_exist", object_id=site.id)
+        tag.save()
+        self.assertTrue(tag.id > 0)
+        with self.assertRaises(Exception) as e:
+            obj = tag.content_object
+        self.assertEqual(e.exception.message, "Content_type does_not_exist not found in self.content_type_map")
+
+    def test_generic_foreign_key_get_bad_id(self):
+        orm = self.make_coreapi()
+        service = orm.Service(name="myservice")
+        service.save()
+        site = orm.Site(name="mysite")
+        site.save()
+        self.assertTrue(site.id > 0)
+        tag = orm.Tag(service=service, name="mytag", value="somevalue", content_type=site.self_content_type_id, object_id=4567)
+        tag.save()
+        self.assertTrue(tag.id > 0)
+        with self.assertRaises(Exception) as e:
+            obj = tag.content_object
+        self.assertEqual(e.exception.message, "Object 4567 of model Site was not found")
+
     def test_generic_foreign_key_set(self):
         orm = self.make_coreapi()
         service = orm.Service(name="myservice")
diff --git a/xos/xos_client/xossh b/xos/xos_client/xossh
index 9eda018..02bab8a 100644
--- a/xos/xos_client/xossh
+++ b/xos/xos_client/xossh
@@ -8,7 +8,7 @@
 import os, sys
 import atexit
 import readline
-import rlcompleter
+import traceback
 
 from twisted.internet import reactor
 from xosapi.version import __version__
@@ -245,8 +245,8 @@
                     # Do it
                     code = compile(command, "<stdin>", "single")
                     exec code
-                except Exception, err:
-                    print err
+                except Exception:
+                    traceback.print_exc()
 
             # check to see if login() was used
             if client.session_change: