CORD-762 add deleted objects query, add class_names and pk attributes

Change-Id: Ic47fc0e9792474fe5a1b3352f9399f9e195415bc
diff --git a/xos/grpc/apihelper.py b/xos/grpc/apihelper.py
index e5ee9c6..0ddaaa0 100644
--- a/xos/grpc/apihelper.py
+++ b/xos/grpc/apihelper.py
@@ -1,4 +1,5 @@
 import base64
+import inspect
 import time
 from protos import xos_pb2
 from google.protobuf.empty_pb2 import Empty
@@ -85,6 +86,14 @@
                     continue
                 x=getattr(p_obj,related_name+"_ids").append(rel_obj.id)
 
+        # Generate a list of class names for the object. This includes its
+        # ancestors. Anything that is a descendant of PlCoreBase or User
+        # counts.
+
+        bases = inspect.getmro(obj.__class__)
+        bases = [x for x in bases if issubclass(x, PlCoreBase) or issubclass(x, User)]
+        p_obj.class_names = ",".join( [x.__name__ for x in bases] )
+
         return p_obj
 
     def protoToArgs(self, djangoClass, message):
@@ -206,11 +215,16 @@
                     query = query & self.query_element_to_q(element)
                 else:
                     query = self.query_element_to_q(element)
+            queryset = djangoClass.objects.filter(query)
         elif request.kind == request.SYNCHRONIZER_DIRTY_OBJECTS:
             query = (Q(enacted__lt=F('updated')) | Q(enacted=None)) & Q(lazy_blocked=False) &Q(no_sync=False)
+            queryset = djangoClass.objects.filter(query)
+        elif request.kind == request.SYNCHRONIZER_DELETED_OBJECTS:
+            queryset = djangoClass.deleted_objects.all()
         elif request.kind == request.ALL:
-            return self.querysetToProto(djangoClass, djangoClass.objects.all())
-        return self.querysetToProto(djangoClass, djangoClass.objects.filter(query))
+            queryset = djangoClass.objects.all()
+
+        return self.querysetToProto(djangoClass, queryset)
 
     def authenticate(self, context, required=False):
         for (k, v) in context.invocation_metadata():
diff --git a/xos/grpc/protos/common.proto b/xos/grpc/protos/common.proto
index fbe6563..0cf84c2 100644
--- a/xos/grpc/protos/common.proto
+++ b/xos/grpc/protos/common.proto
@@ -28,6 +28,7 @@
         DEFAULT=0;
         ALL=1;
         SYNCHRONIZER_DIRTY_OBJECTS = 2;
+        SYNCHRONIZER_DELETED_OBJECTS = 3;
     }
     QueryKind kind = 1;
     repeated QueryElement elements = 2;
diff --git a/xos/grpc/protos/xosoptions.proto b/xos/grpc/protos/xosoptions.proto
index e8f06d2..95fc681 100644
--- a/xos/grpc/protos/xosoptions.proto
+++ b/xos/grpc/protos/xosoptions.proto
@@ -23,3 +23,4 @@
   ForeignKeyRule foreignKey = 1002;
   ReverseForeignKeyRule reverseForeignKey = 1003;
 }
+
diff --git a/xos/tools/apigen/protobuf.template.txt b/xos/tools/apigen/protobuf.template.txt
index 91f76da..3e5d82a 100644
--- a/xos/tools/apigen/protobuf.template.txt
+++ b/xos/tools/apigen/protobuf.template.txt
@@ -60,6 +60,7 @@
   {%- for ref in object.reverse_refs %}
     repeated int32 {{ ref.related_name }}_ids  = {{ loop.index+100 }} [(reverseForeignKey).modelName = "{{ ref.camel() }}"];
   {%- endfor %}
+  string class_names = 201;
 }
 
 message {{ object.camel() }}s {
diff --git a/xos/xos_client/xosapi/orm.py b/xos/xos_client/xosapi/orm.py
index 1808e08..9572087 100644
--- a/xos/xos_client/xosapi/orm.py
+++ b/xos/xos_client/xosapi/orm.py
@@ -104,6 +104,10 @@
         # note: getattr is only called for attributes that do not exist in
         #       self.__dict__
 
+        # pk is a synonym for id
+        if (name == "pk"):
+            name = "id"
+
         if name in self.poisoned.keys():
             # see explanation in fk_set()
             raise Exception("foreign key was poisoned")
@@ -185,6 +189,7 @@
 
     # constants better agree with common.proto
     SYNCHRONIZER_DIRTY_OBJECTS = 2;
+    SYNCHRONIZER_DELETED_OBJECTS = 3;
 
     def __init__(self, stub, modelName, packageName):
         self._stub = stub