Skip to content

Commit

Permalink
Add Lambda support for Skygear Data Type #195
Browse files Browse the repository at this point in the history
  • Loading branch information
carmenlau committed May 11, 2018
2 parents 3142022 + c2d8572 commit 91ca4ba
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 12 deletions.
59 changes: 59 additions & 0 deletions examples/lambda-types/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import skygear
from skygear.models import Location, Record, RecordID


@skygear.op('hello')
def hello():
"""
This lamda returns some skydb types to the caller. This demonstrates
the lambda can return skydb types.
```
curl -X "POST" "http://localhost:3000" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"api_key": "secret",
"action": "hello"
}'
```
"""
return [
1,
2,
True,
"hello",
Location(1, 2),
Record(
id=RecordID("note", "99D92DBA-74D5-477F-B35E-F735E21B2DD5"),
owner_id="OWNER_ID",
acl=None,
data={
"content": "Hello World!",
}
),
{
'location': Location(1, 2)
}
]


@skygear.op('echo')
def echo(value):
"""
This lamda copies the lambda value and return it to the caller.
```
curl -X "POST" "http://localhost:3000" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"api_key": "secret",
"action": "echo",
"args": [
{
"location": {"$lng":1,"$type":"geo","$lat":2}
}
]
}'
```
"""
return value
30 changes: 26 additions & 4 deletions skygear/encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,23 @@ def deserialize_or_none(obj):
return None


def deserialize_value(value):
return _RecordDecoder().decode_value(value)


def serialize_record(record):
return _RecordEncoder().encode(record)


def serialize_value(value):
return _RecordEncoder().encode_value(value)


class _RecordDecoder:
def decode(self, d):
id = self.decode_id(d['_id'])
owner_id = d['_ownerID']
acl = self.decode_acl(d['_access'])
owner_id = d.get('_ownerID', None)
acl = self.decode_acl(d.get('_access', None))

created_at = None
if d.get('_created_at', None):
Expand Down Expand Up @@ -99,7 +107,7 @@ def decode_ace(self, d):
user_id = d.get('user_id', None)
public = d.get('public', None)
if user_id is not None:
return DirectAccessControlEntry(d['user_id'], level)
return DirectAccessControlEntry(user_id, level)
elif relation is not None:
return RelationalAccessControlEntry(relation, level)
elif role is not None:
Expand Down Expand Up @@ -128,6 +136,8 @@ def decode_value(self, v):
return self.decode_ref(v)
elif type_ == 'unknown':
return self.decode_unknown_value(v)
elif type_ == 'record':
return self.decode_record(v)
else:
return self.decode_dict(v)
elif isinstance(v, list):
Expand All @@ -154,12 +164,16 @@ def decode_ref(self, d):
def decode_unknown_value(self, d):
return UnknownValue(d['$underlying_type'])

def decode_record(self, d):
return self.decode(d['$record'])


class _RecordEncoder:
def encode(self, record):
d = self.encode_dict(record.data)
d['_id'] = self.encode_id(record.id)
d['_ownerID'] = record.owner_id
if record.owner_id:
d['_ownerID'] = record.owner_id
d['_access'] = self.encode_acl(record.acl)
if record.created_at is not None:
# New record don't have following value yet
Expand Down Expand Up @@ -224,6 +238,8 @@ def encode_value(self, v):
return self.encode_ref(v)
elif isinstance(v, UnknownValue):
return self.encode_unknown_value(v)
elif isinstance(v, Record):
return self.encode_record(v)
else:
return v

Expand Down Expand Up @@ -264,3 +280,9 @@ def encode_unknown_value(self, unknown_value):
if unknown_value.underlyingType:
data['$underlying_type'] = unknown_value.underlyingType
return data

def encode_record(self, record):
return {
'$type': 'record',
'$record': self.encode(record)
}
25 changes: 25 additions & 0 deletions skygear/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ def type(self):
def key(self):
return self._key

def __eq__(self, other):
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False


ACCESS_CONTROL_ENTRY_LEVEL_WRITE = 'write'
ACCESS_CONTROL_ENTRY_LEVEL_READ = 'read'
Expand Down Expand Up @@ -158,12 +163,22 @@ def name(self, name):
raise ValueError('Asset.name cannot be None or empty')
self._name = name

def __eq__(self, other):
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False


class Location:
def __init__(self, lng, lat):
self.lng = lng
self.lat = lat

def __eq__(self, other):
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False


class Reference:
def __init__(self, recordID):
Expand All @@ -179,6 +194,11 @@ def recordID(self, recordID):
raise ValueError('Reference.recordID cannot be None')
self._recordID = recordID

def __eq__(self, other):
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False


class UnknownValue:
def __init__(self, underlyingType):
Expand All @@ -191,3 +211,8 @@ def underlyingType(self):
@underlyingType.setter
def underlyingType(self, underlyingType):
self._underlyingType = underlyingType

def __eq__(self, other):
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False
9 changes: 5 additions & 4 deletions skygear/transmitter/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from werkzeug.test import EnvironBuilder
from werkzeug.wrappers import BaseResponse, Request

from ..encoding import _serialize_exc, deserialize_or_none, serialize_record
from ..encoding import (_serialize_exc, deserialize_or_none, deserialize_value,
serialize_record, serialize_value)
from ..error import SkygearException
from ..registry import get_registry
from ..utils import db
Expand Down Expand Up @@ -163,15 +164,15 @@ def handler(self, func, param):

def op(self, func, param):
if isinstance(param, list):
args = param
args = deserialize_value(param)
kwargs = {}
elif isinstance(param, dict):
args = []
kwargs = param
kwargs = deserialize_value(param)
else:
msg = "Unsupported args type '{0}'".format(type(param))
raise ValueError(msg)
return func(*args, **kwargs)
return serialize_value(func(*args, **kwargs))

def hook(self, func, param):
original_record = deserialize_or_none(param.get('original', None))
Expand Down
Loading

0 comments on commit 91ca4ba

Please sign in to comment.