diff --git a/api.py b/api.py index 1bf6057..a6dc690 100644 --- a/api.py +++ b/api.py @@ -28,7 +28,11 @@ import config def CreateChannel(google_user, client, client_address, instance_id, args): - models.Instance.FindOrCreate(instance_id) + instance = models.Instance.FindOrCreate(instance_id) + + if instance.polling: + instance.polling = False + instance.save() token = channel.create_channel( client_id=instance_id, @@ -50,6 +54,32 @@ def CreateChannel(google_user, client, client_address, instance_id, args): } +def Poll(google_user, client, client_address, instance_id, args): + instance = models.Instance.FindOrCreate(instance_id) + + if not instance.polling: + instance.polling = True + instance.save() + + events = [] + if google_user: + events.append({ + 'event_type': 'login', + 'google_user': google_user.email(), + }) + else: + events.append({ + 'event_type': 'logout', + }) + + for subscription in instance.GetSubscriptions(): + events.extend(subscription.GetMessages()) + + return { + 'events': events, + } + + def Pin(google_user, client, client_address, instance_id, args): instance = models.Instance.FromID(instance_id) if not instance or not instance.active: @@ -143,7 +173,7 @@ def Subscribe(google_user, client, client_address, instance_id, args): return { 'result': 'ok', 'events': models.Subscription.FindOrCreate( - subject, client, instance, args['subject'], messages, last_id), + subject, client, instance, args['subject'], messages, last_id, instance.polling), } @@ -181,6 +211,7 @@ class APIWrapper(webapp2.RequestHandler): _COMMANDS = { 'createChannel': CreateChannel, 'pin': Pin, + 'poll': Poll, 'sendMessage': SendMessage, 'subscribe': Subscribe, 'unpin': Unpin, diff --git a/lib/models.py b/lib/models.py index 686a760..2fe382e 100644 --- a/lib/models.py +++ b/lib/models.py @@ -36,6 +36,7 @@ import utils # ↳ Message # ↳ Pin (⤴︎ Instance) # ↳ Subscription (⤴︎ Instance) +# ↳ Event class DuplicateMessage(Exception): @@ -92,6 +93,7 @@ class Client(db.Model): class Instance(db.Model): # key_name=instance_id active = db.BooleanProperty(required=True, default=False) + polling = db.BooleanProperty(required=True, default=False) @classmethod def FromID(cls, instance_id): @@ -99,9 +101,14 @@ class Instance(db.Model): return cls.get_by_key_name(instance_id) @classmethod - def FindOrCreate(cls, instance_id): + def FindOrCreate(cls, instance_id, polling=False): logging.info('Instance: %s', instance_id) - return cls.get_or_insert(instance_id) + return cls.get_or_insert(instance_id, polling=polling) + + def GetSubscriptions(self): + return ( + Subscription.all() + .filter('instance=', self)) class Subject(db.Model): @@ -386,11 +393,12 @@ class Subscription(db.Model): instance = db.ReferenceProperty(reference_class=Instance, required=True) readable_only_by_me = db.BooleanProperty(required=True, default=False) writable_only_by_me = db.BooleanProperty(required=True, default=False) + polling = db.BooleanProperty(required=True, default=False) @classmethod @db.transactional() def FindOrCreate(cls, subject, client, instance, request, - messages=0, last_id=None): + messages=0, last_id=None, polling=False): readable_only_by_me = (request.get('readable_only_by') == 'me') writable_only_by_me = (request.get('writable_only_by') == 'me') subscriptions = ( @@ -422,10 +430,30 @@ class Subscription(db.Model): subscription.delete() def SendMessage(self, msg): - instance_key = Subscription.instance.get_value_for_datastore(self) - channel.send_message( - str(instance_key.name()), - json.dumps(msg, default=utils.EncodeJSON)) + encoded = json.dumps(msg, default=utils.EncodeJSON) + if self.polling: + Event(parent=this, + json=encoded).save() + else: + instance_key = Subscription.instance.get_value_for_datastore(self) + channel.send_message(str(instance_key.name()), encoded) + + def GetMessages(self): + events = ( + Event.all() + .ancestor(self)) + return [e.ToEvent() for e in events] + + +class Event(db.Model): + # parent=Subscription + + json = db.StringProperty(required=True) + + def ToEvent(self): + ret = json.loads(self.json) + ret['event_id'] = str(self.key()) + return ret class Message(db.Model):