def updated(uri, prev_checkpoint=None): uri = canonical_uri(uri) resp = internal_request(uri) resp_headers = dict() for k, v in resp.items(): resp_headers[k] = v if len(resp.content) > 0: try: resp_body = json.loads(resp.content) except: resp_body = '' else: resp_body = '' msg = dict() msg['type'] = 'event' msg['uri'] = uri msg['headers'] = resp_headers msg['body'] = resp_body formats = list() formats.append(HttpResponseFormat(code=resp.status_code, headers=resp_headers, body=resp.content)) formats.append(WebSocketMessageFormat(json.dumps(msg))) channel = channel_for_uri(uri, 'value') # TODO: id, prev_id publish(channel, formats)
def publish_message(message): # Get the most recent sent message from the database (empty string by # default). m = LastMessage.get_only() # Generate ETags of the previous and current messages, using the same # algorithm as the GET handler. etag = '"%s"' % hashlib.md5(message).hexdigest() prev_etag = '"%s"' % hashlib.md5(m.text).hexdigest() # Save the current message to the database, overwriting the previous. m.text = message m.save() # Prepare headers for the HTTP request and HTTP response delivery # mechanisms. headers = {'Content-Type': 'text/plain', 'ETag': etag} formats = [] # Prepare the message in four different formats. formats.append(HttpResponseFormat(headers=headers, body=message + '\n')) formats.append(HttpStreamFormat(message + '\n')) formats.append(WebSocketMessageFormat(message)) formats.append(HttpRequestFormat('POST', headers=headers, body=message + '\n')) # Send to the proxy and/or Fanout. publish('messages', formats, id=etag, prev_id=prev_etag)
def publish_event(channel, event_type, data, pub_id, pub_prev_id, skip_user_ids=[]): from django_grip import publish content_filters = [] if pub_id: event_id = '%I' content_filters.append('build-id') else: event_id = None content = sse_encode_event(event_type, data, event_id=event_id, escape=bool(pub_id)) meta = {} if skip_user_ids: meta['skip_users'] = ','.join(skip_user_ids) publish('events-%s' % quote(channel), HttpStreamFormat(content, content_filters=content_filters), id=pub_id, prev_id=pub_prev_id, meta=meta)
def expire_requests(): reqs = db.request_take_expired() headers = dict() headers['Content-Type'] = 'text/html' body = 'Service Unavailable\n' for r in reqs: publish(grip_prefix + 'wait-%s-%s' % (r[0], r[1]), HttpResponseFormat(code=503, headers=headers, body=body)) return len(reqs)
def broadcast(request): ws = request.wscontext print(ws.in_events[0]) # if this is a new connection, accept it and subscribe it to a channel if ws.is_opening(): ws.accept() publish('test', WebSocketMessageFormat('broadcast\n'))
def expire_requests(): reqs = db.request_take_expired() headers = dict() headers['Content-Type'] = 'text/html' body = 'Service Unavailable\n' for r in reqs: publish(grip_prefix + 'wait-%s-%s' % (r[0], r[1]), HttpResponseFormat(code=503, headers=headers, body=body), id='1', prev_id='0') return len(reqs)
def broadcast(request): if request.method == 'POST': # publish data to all clients that are connected to the echo endpoint data = request.POST['data'] publish('test', WebSocketMessageFormat(data)) return HttpResponse('Ok\n') else: return HttpResponseNotAllowed(['POST'])
def item(request, headline_id): h = get_object_or_404(Headline, pk=headline_id) if request.wscontext: ws = request.wscontext if ws.is_opening(): ws.accept() ws.subscribe(headline_id) while ws.can_recv(): message = ws.recv() if message is None: ws.close() break return HttpResponse() elif request.method == 'GET': if request.META.get('HTTP_ACCEPT') == 'text/event-stream': resp = HttpResponse(content_type='text/event-stream') set_hold_stream(request, headline_id) return resp else: wait = request.META.get('HTTP_WAIT') if wait: wait = int(wait) if wait < 1: wait = None if wait > 300: wait = 300 inm = request.META.get('HTTP_IF_NONE_MATCH') etag = '"%s"' % calendar.timegm(h.date.utctimetuple()) if inm == etag: resp = HttpResponseNotModified() if wait: set_hold_longpoll(request, headline_id, timeout=wait) else: resp = _json_response(h.to_data()) resp['ETag'] = etag return resp elif request.method == 'PUT': hdata = json.loads(request.read()) h.type = hdata['type'] h.title = hdata.get('title', '') h.text = hdata.get('text', '') h.save() hdata = h.to_data() hjson = json.dumps(hdata) etag = '"%s"' % calendar.timegm(h.date.utctimetuple()) rheaders = {'Content-Type': 'application/json', 'ETag': etag} hpretty = json.dumps(hdata, indent=4) + '\n' formats = list() formats.append(HttpResponseFormat(body=hpretty, headers=rheaders)) formats.append(HttpStreamFormat('event: update\ndata: %s\n\n' % hjson)) formats.append(WebSocketMessageFormat(hjson)) publish(headline_id, formats) resp = _json_response(hdata) resp['ETag'] = etag return resp else: return HttpResponseNotAllowed(['GET', 'PUT'])
def item(request, headline_id): h = get_object_or_404(Headline, pk=headline_id) if request.wscontext: ws = request.wscontext if ws.is_opening(): ws.accept() ws.subscribe('headline-%s' % headline_id) while ws.can_recv(): message = ws.recv() if message is None: ws.close() break return HttpResponse() elif request.method == 'GET': if request.META.get('HTTP_ACCEPT') == 'text/event-stream': resp = HttpResponse(content_type='text/event-stream') set_hold_stream(resp, 'headline-%s' % headline_id) return resp else: wait = request.META.get('HTTP_WAIT') if wait: wait = int(wait) if wait < 1: wait = None if wait > 300: wait = 300 inm = request.META.get('HTTP_IF_NONE_MATCH') etag = '"%s"' % calendar.timegm(h.date.utctimetuple()) if inm == etag: resp = HttpResponseNotModified() if wait: set_hold_longpoll(resp, 'headline-%s' % headline_id, timeout=wait) else: resp = _json_response(h.to_data()) resp['ETag'] = etag return resp elif request.method == 'PUT': hdata = json.loads(request.read()) h.type = hdata['type'] h.title = hdata.get('title', '') h.text = hdata.get('text', '') h.save() hdata = h.to_data() hjson = json.dumps(hdata) etag = '"%s"' % calendar.timegm(h.date.utctimetuple()) rheaders = {'ETag': etag} hpretty = json.dumps(hdata, indent=4) + '\n' formats = list() formats.append(HttpResponseFormat(body=hpretty, headers=rheaders)) formats.append(HttpStreamFormat('event: update\ndata: %s\n\n' % hjson)) formats.append(WebSocketMessageFormat(hjson)) publish('headline-%s' % headline_id, formats) resp = _json_response(hdata) resp['ETag'] = etag return resp else: return HttpResponseNotAllowed(['GET', 'PUT'])
def _publish_item(list_id, item, cursor): data = item.to_data() _add_totals(list_id, data) body = _json_data([data]) + '\n' stream_data = deepcopy(data) stream_data['change-id'] = str(cursor.cur) stream_content = _json_data(stream_data, False) + '\n' headers = {'Link': _changes_link(list_id, cursor.cur)} formats = [] formats.append(HttpResponseFormat(headers=headers, body=body)) formats.append(HttpStreamFormat(stream_content)) publish('todos-%s' % list_id, formats, id=cursor.cur, prev_id=cursor.prev)
def publish_kick(user_id, channel): from django_grip import publish msg = 'Permission denied to channels: %s' % channel data = {'condition': 'forbidden', 'text': msg, 'channels': [channel]} content = sse_encode_event('stream-error', data, event_id='error') meta = {'require_sub': 'events-%s' % channel} publish('user-%s' % user_id, HttpStreamFormat(content), id='kick-1', meta=meta) publish('user-%s' % user_id, HttpStreamFormat(close=True), id='kick-2', prev_id='kick-1', meta=meta)
def respond(req, inbox_id, item_id): if req.method == 'POST': try: content = json.loads(req.body) except: return HttpResponseBadRequest( 'Bad Request: Body must be valid JSON\n') try: code = content.get('code') if code is not None: code = int(code) else: code = 200 reason = content.get('reason') headers = content.get('headers') if 'body-bin' in content: body = b64decode(content['body-bin']) elif 'body' in content: body = content['body'] else: body = '' except: return HttpResponseBadRequest( 'Bad Request: Bad format of response\n') try: db.request_remove_pending(inbox_id, item_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) publish( grip_prefix + 'wait-%s-%s' % (inbox_id, item_id), HttpResponseFormat(code=code, reason=reason, headers=headers, body=body)) return HttpResponse('Ok\n') else: return HttpResponseNotAllowed(['POST'])
def inbox(req, inbox_id): if req.method == 'GET': host = req.META.get('HTTP_HOST') if not host: return HttpResponseBadRequest('Bad Request: No \'Host\' header\n') try: inbox = db.inbox_get(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) out = dict() out['id'] = inbox_id out['base_url'] = 'http://' + host + '/i/' + inbox_id + '/' out['ttl'] = inbox['ttl'] response_mode = inbox.get('response_mode') if not response_mode: response_mode = 'auto' out['response_mode'] = response_mode return HttpResponse(json.dumps(out) + '\n', content_type='application/json') elif req.method == 'DELETE': try: db.inbox_delete(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) # we'll push a 404 to any long polls because we're that cool publish( grip_prefix + 'inbox-%s' % inbox_id, HttpResponseFormat(code=404, headers={'Content-Type': 'text/html'}, body='Not Found\n')) return HttpResponse('Deleted\n') else: return HttpResponseNotAllowed(['GET', 'DELETE'])
def wrapper(self, request, room_id, *args, **kwargs): """ Automatically push the data returned from view_func. """ response = view_func(self, request, room_id, *args, **kwargs) # If we got a JSON response, we'll want to publish this out if isinstance(response, JsonResponse): if request.method == 'POST': event = "create" elif request.method == 'DELETE': event = "delete" else: event = "update" # Got back a data object that needs to be published update_content = [HttpStreamFormat('event: %s\ndata: %s\n\n' % (event, response.content))] publish(self._stream_name(room_id), update_content) return response
def inbox(req, inbox_id): if req.method == 'GET': host = req.META.get('HTTP_HOST') if not host: return HttpResponseBadRequest('Bad Request: No \'Host\' header\n') try: inbox = db.inbox_get(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) out = dict() out['id'] = inbox_id out['base_url'] = 'http://' + host + '/i/' + inbox_id + '/' out['ttl'] = inbox['ttl'] response_mode = inbox.get('response_mode') if not response_mode: response_mode = 'auto' out['response_mode'] = response_mode return HttpResponse(json.dumps(out) + '\n', content_type='application/json') elif req.method == 'DELETE': try: db.inbox_delete(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) # we'll push a 404 to any long polls because we're that cool publish(grip_prefix + 'inbox-%s' % inbox_id, HttpResponseFormat(code=404, headers={'Content-Type': 'text/html'}, body='Not Found\n')) return HttpResponse('Deleted\n') else: return HttpResponseNotAllowed(['GET', 'DELETE'])
def respond(req, inbox_id, item_id): if req.method == 'POST': try: content = json.loads(req.body) except: return HttpResponseBadRequest('Bad Request: Body must be valid JSON\n') try: code = content.get('code') if code is not None: code = int(code) else: code = 200 reason = content.get('reason') headers = content.get('headers') if 'body-bin' in content: body = b64decode(content['body-bin']) elif 'body' in content: body = content['body'] else: body = '' except: return HttpResponseBadRequest('Bad Request: Bad format of response\n') try: db.request_remove_pending(inbox_id, item_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) publish(grip_prefix + 'wait-%s-%s' % (inbox_id, item_id), HttpResponseFormat(code=code, reason=reason, headers=headers, body=body), id='1', prev_id='0') return HttpResponse('Ok\n') else: return HttpResponseNotAllowed(['POST'])
def handle(self, *args, **options): c = Consumer(settings.KAFKA_CONSUMER_CONFIG) c.subscribe(['^.*']) self.stdout.write('subscribed') try: while True: msg = c.poll(timeout=1) if msg is None: continue if msg.error(): if msg.error().code() == KafkaError._PARTITION_EOF: continue else: raise KafkaException(msg.error()) # skip internal channels if msg.topic().startswith('__'): continue try: data = msg.value().decode('utf-8') except Exception: self.stdout.write('%s: message is not valid utf-8: %s' % (msg.topic(), repr(msg.value()))) continue publish(msg.topic(), HttpStreamFormat(sse_encode(data))) except KeyboardInterrupt: pass c.close()
def publish_board(board): players = Player.get_top_for_board(board, limit=5) publish( str(board.id), HttpStreamFormat('event: update\ndata: %s\n\n' % _board_json(board, players, pretty=False)))
def document_change(request, document_id): if request.method == 'GET': link = False sse = False if request.GET.get('link') == 'true': link = True sse = True else: accept = request.META.get('HTTP_ACCEPT') if accept and accept.find('text/event-stream') != -1: sse = True after = None grip_last = request.META.get('HTTP_GRIP_LAST') if grip_last: at = grip_last.find('last-id=') if at == -1: raise ValueError('invalid Grip-Last header') at += 8 after = int(grip_last[at:]) if after is None and sse: last_id = request.META.get('Last-Event-ID') if last_id: after = int(last_id) if after is None and sse: last_id = request.GET.get('lastEventId') if last_id: after = int(last_id) if after is None: afterstr = request.GET.get('after') if afterstr: after = int(afterstr) try: doc = Document.objects.get(eid=document_id) if after is not None: if after > doc.version: return HttpResponseNotFound('version in the future') changes = DocumentChange.objects.filter( document=doc, version__gt=after).order_by('version')[:50] out = [c.export() for c in changes] if len(out) > 0: last_version = out[-1]['version'] else: last_version = after else: out = [] last_version = doc.version except Document.DoesNotExist: if after is not None and after > 0: return HttpResponseNotFound('version in the future') out = [] last_version = 0 if sse: body = '' if not link: body += 'event: opened\ndata:\n\n' for i in out: event = 'id: %d\nevent: change\ndata: %s\n\n' % ( i['version'], json.dumps(i)) body += event resp = HttpResponse(body, content_type='text/event-stream') parsed = urlparse(reverse('document-changes', args=[document_id])) resp['Grip-Link'] = '<%s?link=true&after=%d>; rel=next' % ( parsed.path, last_version) if len(out) < 50: resp['Grip-Hold'] = 'stream' resp['Grip-Channel'] = 'document-%s; prev-id=%s' % ( document_id, last_version) resp['Grip-Keep-Alive'] = 'event: keep-alive\\ndata:\\n\\n; format=cstring; timeout=20' # print(resp.content) return resp else: return JsonResponse({'changes': out}) elif request.method == 'POST': opdata = json.loads(request.POST['op']) for i in opdata: if not isinstance(i, int) and not isinstance(i, str): return HttpResponseBadRequest('invalid operation'); op = TextOperation(opdata) request_id = request.POST['request-id'] parent_version = int(request.POST['parent-version']) doc = _doc_get_or_create(document_id) saved = False with transaction.atomic(): doc = Document.objects.select_for_update().get(id=doc.id) try: # already submitted? c = DocumentChange.objects.get( document=doc, request_id=request_id, parent_version=parent_version) except DocumentChange.DoesNotExist: changes_since = DocumentChange.objects.filter( document=doc, version__gt=parent_version, version__lte=doc.version).order_by('version') for c in changes_since: op2 = TextOperation(json.loads(c.data)) try: op, _ = TextOperation.transform(op, op2) except: return HttpResponseBadRequest( 'unable to transform against version %d' % c.version) try: doc.content = op(doc.content) except: return HttpResponseBadRequest( 'unable to apply %s to version %d' % ( json.dumps(op.ops), doc.version)) next_version = doc.version + 1 c = DocumentChange( document=doc, version=next_version, request_id=request_id, parent_version=parent_version, data=json.dumps(op.ops)) c.save() doc.version = next_version doc.save() saved = True if saved: event = 'id: %d\nevent: change\ndata: %s\n\n' % ( c.version, json.dumps(c.export())) publish( 'document-%s' % document_id, HttpStreamFormat(event), id=str(c.version), prev_id=str(c.version - 1)) return JsonResponse({'version': c.version}) else: return HttpResponseNotAllowed(['GET', 'POST'])
def hit(req, inbox_id): try: inbox = db.inbox_get(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) response_mode = inbox.get('response_mode') if not response_mode: response_mode = 'auto' # pubsubhubbub verify request? hub_challenge = req.GET.get('hub.challenge') if response_mode == 'wait' or (response_mode == 'wait-verify' and hub_challenge): respond_now = False else: respond_now = True item = _req_to_item(req) if hub_challenge: item['type'] = 'hub-verify' else: item['type'] = 'normal' try: item_id, prev_id, item_created = db.inbox_append_item(inbox_id, item) db.inbox_clear_expired_items(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) item['id'] = item_id item['created'] = item_created item = _convert_item(item, respond_now) hr_headers = dict() hr_headers['Content-Type'] = 'application/json' hr = dict() hr['last_cursor'] = item_id hr['items'] = [item] hr_body = json.dumps(hr) + '\n' hs_body = json.dumps(item) + '\n' formats = list() formats.append(HttpResponseFormat(headers=hr_headers, body=hr_body)) formats.append(HttpStreamFormat(hs_body)) publish(grip_prefix + 'inbox-%s' % inbox_id, formats, id=item_id, prev_id=prev_id) if respond_now: if hub_challenge: return HttpResponse(hub_challenge) else: return HttpResponse('Ok\n') else: # wait for the user to respond db.request_add_pending(inbox_id, item_id) set_hold_longpoll(req, grip_prefix + 'wait-%s-%s' % (inbox_id, item_id)) return HttpResponse('Service Unavailable\n', status=503, content_type='text/html')
def document_changes(request, document_id): url = REPLICA_URLS[CURRENT_PRIMARY]+'/api/documents/{}/changes/'.format(document_id) print('url here goes', url) if request.method == 'GET': gchannel = 'document-{}'.format(document_id) link = False sse = False if request.GET.get('link') == 'true': link = True sse = True else: accept = request.META.get('HTTP_ACCEPT') if accept and accept.find('text/event-stream') != -1: sse = True after = None last_id = request.grip.last.get(gchannel) if last_id: after = int(last_id) if after is None and sse: last_id = request.META.get('Last-Event-ID') if last_id: after = int(last_id) if after is None and sse: last_id = request.GET.get('lastEventId') if last_id: after = int(last_id) if after is None: afterstr = request.GET.get('after') if afterstr: after = int(afterstr) payload = request.GET.dict() payload['from-master'] = True response = requests.get(url, payload) resp_content = json.loads(response.text) if "success" in resp_content: if resp_content["success"]: response = HttpResponse(resp_content['body'], content_type='text/event-stream') parsed = urlparse(reverse('document-changes', args=[document_id])) instruct = request.grip.start_instruct() instruct.set_next_link('{}?link=true&after={}'.format(parsed.path, resp_content['last_version'])) if len(resp_content['out']) < 50: instruct.set_hold_stream() instruct.add_channel(Channel(resp_content['gchannel'], prev_id=str(resp_content['last_version']))) instruct.set_keep_alive('event: keep-alive\ndata:\n\n; format=cstring', 20) else: response = HttpResponseNotFound('version in the future') elif request.method == 'POST': payload = request.POST.dict() payload['from-master'] = True response = requests.post(url, payload) print('\n\n-----',response,response.status_code,'\n\n') if response.status_code == 200: resp_content = json.loads(response.text) print('resp',resp_content) if "success" in resp_content: if resp_content["success"]: publish( resp_content['gchannel'], HttpStreamFormat(json.loads(resp_content['event'])), id=str(resp_content['version']), prev_id=str(resp_content['version']-1)) response = JsonResponse({'version': resp_content['version']}) elif response.status_code == 500: response = HttpResponseBadRequest(response.text) else: response = HttpResponseNotFound('invalid') return response
def hit(req, inbox_id): if len(req.grip.last) > 0: for channel, last_id in req.grip.last.iteritems(): break set_hold_longpoll(req, Channel(channel, last_id)) return HttpResponse('Service Unavailable\n', status=503, content_type='text/html') try: inbox = db.inbox_get(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) response_mode = inbox.get('response_mode') if not response_mode: response_mode = 'auto' # pubsubhubbub verify request? hub_challenge = req.GET.get('hub.challenge') if response_mode == 'wait' or (response_mode == 'wait-verify' and hub_challenge): respond_now = False else: respond_now = True item = _req_to_item(req) if hub_challenge: item['type'] = 'hub-verify' else: item['type'] = 'normal' try: item_id, prev_id, item_created = db.inbox_append_item(inbox_id, item) db.inbox_clear_expired_items(inbox_id) except redis_ops.InvalidId: return HttpResponseBadRequest('Bad Request: Invalid id\n') except redis_ops.ObjectDoesNotExist: return HttpResponseNotFound('Not Found\n') except: return HttpResponse('Service Unavailable\n', status=503) item['id'] = item_id item['created'] = item_created item = _convert_item(item, respond_now) hr_headers = dict() hr_headers['Content-Type'] = 'application/json' hr = dict() hr['last_cursor'] = item_id hr['items'] = [item] hr_body = json.dumps(hr) + '\n' hs_body = json.dumps(item) + '\n' formats = list() formats.append(HttpResponseFormat(headers=hr_headers, body=hr_body)) formats.append(HttpStreamFormat(hs_body)) publish(grip_prefix + 'inbox-%s' % inbox_id, formats, id=item_id, prev_id=prev_id) if respond_now: if hub_challenge: return HttpResponse(hub_challenge) else: return HttpResponse('Ok\n') else: # wait for the user to respond db.request_add_pending(inbox_id, item_id) set_hold_longpoll(req, Channel(grip_prefix + 'wait-%s-%s' % (inbox_id, item_id), '0')) return HttpResponse('Service Unavailable\n', status=503, content_type='text/html')
def document_changes(request, document_id): gchannel = 'document-{}'.format(document_id) if request.method == 'GET': link = False sse = False if request.GET.get('link') == 'true': link = True sse = True else: accept = request.META.get('HTTP_ACCEPT') if accept and accept.find('text/event-stream') != -1: sse = True after = None last_id = request.grip.last.get(gchannel) if last_id: after = int(last_id) if after is None and sse: last_id = request.META.get('Last-Event-ID') if last_id: after = int(last_id) if after is None and sse: last_id = request.GET.get('lastEventId') if last_id: after = int(last_id) if after is None: afterstr = request.GET.get('after') if afterstr: after = int(afterstr) try: doc = Document.objects.get(eid=document_id) if after is not None: if after > doc.version: return HttpResponseNotFound('version in the future') changes = DocumentChange.objects.filter( document=doc, version__gt=after).order_by('version')[:50] out = [c.export() for c in changes] if len(out) > 0: last_version = out[-1]['version'] else: last_version = after else: out = [] last_version = doc.version except Document.DoesNotExist: if after is not None and after > 0: return HttpResponseNotFound('version in the future') out = [] last_version = 0 if sse: body = '' if not link: body += 'event: opened\ndata:\n\n' for i in out: event = 'id: {}\nevent: change\ndata: {}\n\n'.format( i['version'], json.dumps(i)) body += event resp = HttpResponse(body, content_type='text/event-stream') parsed = urlparse.urlparse(reverse('document-changes', args=[document_id])) instruct = request.grip.start_instruct() instruct.set_next_link('{}?link=true&after={}'.format( parsed.path, last_version)) if len(out) < 50: instruct.set_hold_stream() instruct.add_channel(Channel(gchannel, prev_id=str(last_version))) instruct.set_keep_alive('event: keep-alive\ndata:\n\n; format=cstring', 20) return resp else: return JsonResponse({'changes': out}) elif request.method == 'POST': opdata = json.loads(request.POST['op']) for i in opdata: if not isinstance(i, int) and not isinstance(i, basestring): return HttpResponseBadRequest('invalid operation'); op = TextOperation(opdata) request_id = request.POST['request-id'] parent_version = int(request.POST['parent-version']) doc = _doc_get_or_create(document_id) saved = False with transaction.atomic(): doc = Document.objects.select_for_update().get(id=doc.id) try: # already submitted? c = DocumentChange.objects.get( document=doc, request_id=request_id, parent_version=parent_version) except DocumentChange.DoesNotExist: changes_since = DocumentChange.objects.filter( document=doc, version__gt=parent_version, version__lte=doc.version).order_by('version') for c in changes_since: op2 = TextOperation(json.loads(c.data)) try: op, _ = TextOperation.transform(op, op2) except: return HttpResponseBadRequest( 'unable to transform against version {}'.format(c.version)) try: doc.content = op(doc.content) except: return HttpResponseBadRequest( 'unable to apply {} to version {}'.format( json.dumps(op.ops), doc.version)) next_version = doc.version + 1 c = DocumentChange( document=doc, version=next_version, request_id=request_id, parent_version=parent_version, data=json.dumps(op.ops)) c.save() doc.version = next_version doc.save() saved = True if saved: event = 'id: {}\nevent: change\ndata: {}\n\n'.format( c.version, json.dumps(c.export())) publish( gchannel, HttpStreamFormat(event), id=str(c.version), prev_id=str(c.version - 1)) return JsonResponse({'version': c.version}) else: return HttpResponseNotAllowed(['GET', 'POST'])
def publish_board(board): players = Player.get_top_for_board(board, limit=5) publish(str(board.id), HttpStreamFormat('event: update\ndata: %s\n\n' % _board_json(board, players, pretty=False)))