Support public flag for StateEntry
This commit is contained in:
29
api.py
29
api.py
@@ -51,6 +51,7 @@ class SetValue(webapp2.RequestHandler):
|
|||||||
def post(self):
|
def post(self):
|
||||||
entry_key = self.request.get('key')
|
entry_key = self.request.get('key')
|
||||||
entry_value = self.request.get('value')
|
entry_value = self.request.get('value')
|
||||||
|
public = (self.request.get('public') == 'true')
|
||||||
|
|
||||||
entries = (models.StateEntry.all()
|
entries = (models.StateEntry.all()
|
||||||
.ancestor(self.client.parent_key())
|
.ancestor(self.client.parent_key())
|
||||||
@@ -59,11 +60,13 @@ class SetValue(webapp2.RequestHandler):
|
|||||||
if entries:
|
if entries:
|
||||||
entry = entries[0]
|
entry = entries[0]
|
||||||
entry.entry_value = entry_value
|
entry.entry_value = entry_value
|
||||||
|
entry.public = public
|
||||||
else:
|
else:
|
||||||
entry = models.StateEntry(
|
entry = models.StateEntry(
|
||||||
parent=self.client.parent_key(),
|
parent=self.client.parent_key(),
|
||||||
entry_key=entry_key,
|
entry_key=entry_key,
|
||||||
entry_value=entry_value)
|
entry_value=entry_value,
|
||||||
|
public=public)
|
||||||
|
|
||||||
entry.put()
|
entry.put()
|
||||||
msg = entry.ToMessage()
|
msg = entry.ToMessage()
|
||||||
@@ -75,29 +78,6 @@ class SetValue(webapp2.RequestHandler):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class GetValue(webapp2.RequestHandler):
|
|
||||||
@utils.chaos_monkey
|
|
||||||
@utils.returns_json
|
|
||||||
@utils.local_namespace
|
|
||||||
@security.google_user_xsrf_protection
|
|
||||||
@security.weak_security_checks
|
|
||||||
@session.session_required
|
|
||||||
@db.transactional()
|
|
||||||
def post(self):
|
|
||||||
entry_key = self.request.get('key')
|
|
||||||
|
|
||||||
entries = (models.StateEntry.all()
|
|
||||||
.ancestor(self.client.parent_key())
|
|
||||||
.filter('entry_key =', entry_key)
|
|
||||||
.fetch(1))
|
|
||||||
if entries:
|
|
||||||
return {
|
|
||||||
'value': entries[0].entry_value
|
|
||||||
}
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class CreateChannel(webapp2.RequestHandler):
|
class CreateChannel(webapp2.RequestHandler):
|
||||||
@utils.chaos_monkey
|
@utils.chaos_monkey
|
||||||
@utils.returns_json
|
@utils.returns_json
|
||||||
@@ -119,6 +99,5 @@ class CreateChannel(webapp2.RequestHandler):
|
|||||||
app = webapp2.WSGIApplication([
|
app = webapp2.WSGIApplication([
|
||||||
(config.URL_PREFIX + '/api/createChannel', CreateChannel),
|
(config.URL_PREFIX + '/api/createChannel', CreateChannel),
|
||||||
(config.URL_PREFIX + '/api/getUser', GetUser),
|
(config.URL_PREFIX + '/api/getUser', GetUser),
|
||||||
(config.URL_PREFIX + '/api/getValue', GetValue),
|
|
||||||
(config.URL_PREFIX + '/api/setValue', SetValue),
|
(config.URL_PREFIX + '/api/setValue', SetValue),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import logging
|
|||||||
from google.appengine.api import channel
|
from google.appengine.api import channel
|
||||||
from google.appengine.ext import db
|
from google.appengine.ext import db
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
# Profile
|
# Profile
|
||||||
# ↳ Client
|
# ↳ Client
|
||||||
@@ -94,7 +96,7 @@ class Client(db.Model):
|
|||||||
return cls.FromProfile(profile)
|
return cls.FromProfile(profile)
|
||||||
|
|
||||||
def SendMessage(self, msg):
|
def SendMessage(self, msg):
|
||||||
channel.send_message(str(self.key()), json.dumps(msg))
|
channel.send_message(str(self.key()), json.dumps(msg, default=utils.EncodeJSON))
|
||||||
|
|
||||||
|
|
||||||
class StateEntry(db.Model):
|
class StateEntry(db.Model):
|
||||||
@@ -103,12 +105,15 @@ class StateEntry(db.Model):
|
|||||||
last_set = db.DateTimeProperty(required=True, auto_now=True)
|
last_set = db.DateTimeProperty(required=True, auto_now=True)
|
||||||
entry_key = db.StringProperty(required=True)
|
entry_key = db.StringProperty(required=True)
|
||||||
entry_value = db.StringProperty()
|
entry_value = db.StringProperty()
|
||||||
|
public = db.BooleanProperty(required=True, default=False)
|
||||||
|
|
||||||
def ToMessage(self):
|
def ToMessage(self):
|
||||||
return {
|
return {
|
||||||
'message_type': 'state',
|
'message_type': 'state',
|
||||||
'key': self.entry_key,
|
'key': self.entry_key,
|
||||||
'value': self.entry_value,
|
'value': self.entry_value,
|
||||||
|
'last_set': self.last_set,
|
||||||
|
'public': self.public,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
10
lib/utils.py
10
lib/utils.py
@@ -12,9 +12,11 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
from google.appengine.api import namespace_manager
|
from google.appengine.api import namespace_manager
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ def returns_json(handler):
|
|||||||
|
|
||||||
@functools.wraps(handler)
|
@functools.wraps(handler)
|
||||||
def SerializeResult(self):
|
def SerializeResult(self):
|
||||||
json.dump(handler(self), self.response.out)
|
json.dump(handler(self), self.response.out, default=EncodeJSON)
|
||||||
|
|
||||||
return SerializeResult
|
return SerializeResult
|
||||||
|
|
||||||
@@ -54,3 +56,9 @@ def local_namespace(handler):
|
|||||||
return handler(self)
|
return handler(self)
|
||||||
|
|
||||||
return SetNamespace
|
return SetNamespace
|
||||||
|
|
||||||
|
|
||||||
|
def EncodeJSON(o):
|
||||||
|
if isinstance(o, datetime.datetime):
|
||||||
|
return time.mktime(o.timetuple())
|
||||||
|
return json.JSONEncoder.default(o)
|
||||||
|
|||||||
@@ -144,10 +144,11 @@ cosmopolite.Client.prototype.getUser_ = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
cosmopolite.Client.prototype.setValue = function(key, value) {
|
cosmopolite.Client.prototype.setValue = function(key, value, is_public) {
|
||||||
this.sendRPC_('setValue', {
|
this.sendRPC_('setValue', {
|
||||||
'key': key,
|
'key': key,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
'public': is_public,
|
||||||
})
|
})
|
||||||
// Provide immediate feedback without waiting for a round trip.
|
// Provide immediate feedback without waiting for a round trip.
|
||||||
// We'll also get a response from the server, so this should be eventually
|
// We'll also get a response from the server, so this should be eventually
|
||||||
@@ -200,14 +201,20 @@ cosmopolite.Client.prototype.onServerMessage_ = function(msg) {
|
|||||||
switch (msg.message_type) {
|
switch (msg.message_type) {
|
||||||
case 'state':
|
case 'state':
|
||||||
var key = msg['key'];
|
var key = msg['key'];
|
||||||
var value = msg['value'];
|
if (this.stateCache_[key] &&
|
||||||
if (this.stateCache_[key] == value) {
|
this.stateCache_[key]['value'] == msg['value'] &&
|
||||||
|
this.stateCache_[key]['last_set'] == msg['last_set'] &&
|
||||||
|
this.stateCache_[key]['public'] == msg['public']) {
|
||||||
// Duplicate message.
|
// Duplicate message.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.stateCache_[key] = value;
|
this.stateCache_[key] = {
|
||||||
|
'value': msg['value'],
|
||||||
|
'last_set': msg['last_set'],
|
||||||
|
'public': msg['public'],
|
||||||
|
}
|
||||||
if ('onStateChange' in this.callbacks_) {
|
if ('onStateChange' in this.callbacks_) {
|
||||||
this.callbacks_['onStateChange'](key, value);
|
this.callbacks_['onStateChange'](key, this.stateCache_[key]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -10,24 +10,36 @@ a {
|
|||||||
<body>
|
<body>
|
||||||
<div>Google user: <span id="google_user"></span></div>
|
<div>Google user: <span id="google_user"></span></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
<div>
|
<div>
|
||||||
Key:
|
Key:
|
||||||
<select id="keys"></select>
|
<select id="keys"></select>
|
||||||
<input type="button" id="set" value="Set">
|
<input type="button" id="set" value="Set">
|
||||||
<input type="button" id="add" value="Add">
|
<input type="button" id="add" value="Add">
|
||||||
<br />
|
</div>
|
||||||
|
<div>
|
||||||
|
Last set:
|
||||||
|
<span id="last_set"></span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="public"> Public
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
<textarea id="value" rows="10" cols="40"></textarea>
|
<textarea id="value" rows="10" cols="40"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var keys = document.getElementById('keys');
|
var keys = document.getElementById('keys');
|
||||||
var value = document.getElementById('value');
|
var value = document.getElementById('value');
|
||||||
|
var last_set = document.getElementById('last_set');
|
||||||
|
var is_public = document.getElementById('public');
|
||||||
|
|
||||||
var selectKey = function(new_key) {
|
var selectKey = function(new_key) {
|
||||||
keys.value = new_key;
|
keys.value = new_key;
|
||||||
keys.dispatchEvent(new CustomEvent('change'));
|
keys.dispatchEvent(new CustomEvent('change'));
|
||||||
};
|
};
|
||||||
|
|
||||||
var addKey = function(new_key, new_value, index) {
|
var addKey = function(new_key, index) {
|
||||||
var option = document.createElement('option');
|
var option = document.createElement('option');
|
||||||
option.text = new_key;
|
option.text = new_key;
|
||||||
keys.options.add(option, index);
|
keys.options.add(option, index);
|
||||||
@@ -55,19 +67,19 @@ window.addEventListener('load', function() {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (keys.options[i].value > new_key) {
|
if (keys.options[i].value > new_key) {
|
||||||
addKey(new_key, new_value, i);
|
addKey(new_key, i);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// Sorts at the end
|
// Sorts at the end
|
||||||
addKey(new_key, new_value, keys.options.length);
|
addKey(new_key, keys.options.length);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var debug = new cosmopolite.Client(callbacks);
|
var debug = new cosmopolite.Client(callbacks);
|
||||||
|
|
||||||
document.getElementById('set').addEventListener('click', function() {
|
document.getElementById('set').addEventListener('click', function() {
|
||||||
debug.setValue(keys.value, value.value);
|
debug.setValue(keys.value, value.value, is_public.checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('add').addEventListener('click', function() {
|
document.getElementById('add').addEventListener('click', function() {
|
||||||
@@ -80,7 +92,10 @@ window.addEventListener('load', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('keys').addEventListener('change', function() {
|
document.getElementById('keys').addEventListener('change', function() {
|
||||||
value.value = debug.getValue(keys.value);
|
var value_obj = debug.getValue(keys.value);
|
||||||
|
value.value = value_obj.value;
|
||||||
|
last_set.innerHTML = (new Date(value_obj.last_set * 1000)).toString();
|
||||||
|
is_public.checked = value_obj['public'];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user