Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 8b2ced6

Browse files
authored
Merge pull request #1 from hypoport/dev
Updates README & adds CF + Alexa Skill Events
2 parents ac95e0b + da5f94e commit 8b2ced6

File tree

8 files changed

+527
-22
lines changed

8 files changed

+527
-22
lines changed

README.rst

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,26 @@
22
LaWip
33
===========
44

5-
LaWip provides data-classes for AWS lambda events::
5+
LaWip provides data-classes for AWS lambda events
66

7-
#!/usr/bin/env python
7+
**Installation**
88

9-
from towelstuff import location
10-
from towelstuff import utils
9+
::
1110

12-
if utils.has_towel():
13-
print "Your towel is located:", location.where_is_my_towel()
11+
# current version
12+
pip install git+https://github.com/bweigel/LaWip.git
1413

14+
# using a tagged/released version
15+
pip install git+https://github.com/bweigel/[email protected]
1516

16-
A Section
17-
=========
17+
**Test using tox**
1818

19-
Lists look like this:
19+
::
2020

21-
* First
21+
tox
2222

23-
* Second. Can be multiple lines
24-
but must be indented properly.
23+
**Test using setup.py**
2524

26-
A Sub-Section
27-
-------------
25+
::
2826

29-
Numbered lists look like you'd expect:
30-
31-
1. hi there
32-
33-
2. must be going
34-
35-
Urls are http://like.this and links can be
36-
written `like this <http://www.example.com/foo/bar>`_.
27+
python setup.py test

lawip/alexa_skill_event.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from collections import namedtuple
2+
from typing import Dict
3+
4+
IntentSlot = namedtuple("IntentSlot", ['name', 'value'])
5+
AlexaSkillUser = namedtuple("AlexaSkillUser", ["userId"])
6+
AlexaSkillApplication = namedtuple("AlexaSkillApplication", ["applicationId"])
7+
8+
9+
def parse_intents(slots: Dict[str, Dict[str, str]]) -> Dict[str, IntentSlot]:
10+
return {slot_name: IntentSlot(slot.get("key"),
11+
slot.get("value")) for slot_name, slot in slots.items()}
12+
13+
14+
class AlexaIntent:
15+
def __init__(self, name: str,
16+
slots: Dict):
17+
self._name = name
18+
self._slots = parse_intents(slots)
19+
20+
@classmethod
21+
def from_json(cls, intent: Dict):
22+
return cls(**intent)
23+
24+
@property
25+
def name(self) -> str:
26+
return self._name
27+
28+
@property
29+
def slots(self) -> Dict[str, IntentSlot]:
30+
return self._slots
31+
32+
33+
class AlexaSkillRequest:
34+
def __init__(self, locale: str, timestamp: str,
35+
type: str, requestId: str,
36+
intent: Dict = None, reason: str = None):
37+
self._locale = locale
38+
self._timestamp = timestamp
39+
self._request_id = requestId
40+
self._type = type
41+
self._intent = AlexaIntent(**intent) if isinstance(intent, dict) else None
42+
self._reason = reason
43+
44+
@classmethod
45+
def from_json(cls, session: Dict):
46+
return cls(**session)
47+
48+
@property
49+
def locale(self) -> str:
50+
return self._locale
51+
52+
@property
53+
def timestamp(self) -> str:
54+
return self._timestamp
55+
56+
@property
57+
def request_id(self) -> str:
58+
return self._request_id
59+
60+
@property
61+
def type(self) -> str:
62+
return self._type
63+
64+
@property
65+
def reason(self) -> str:
66+
return self._reason
67+
68+
@property
69+
def intent(self) -> AlexaIntent:
70+
return self._intent
71+
72+
73+
class AlexaSkillSession:
74+
def __init__(self, new: bool,
75+
sessionId: str,
76+
user: Dict[str, str],
77+
application: Dict[str, str],
78+
attributes: Dict = None):
79+
self._new = new
80+
self._session_id = sessionId
81+
self._attributes = attributes
82+
self._user = AlexaSkillUser(**user)
83+
self._application = AlexaSkillApplication(**application)
84+
85+
@classmethod
86+
def from_json(cls, session):
87+
return cls(**session)
88+
89+
@property
90+
def new(self) -> bool:
91+
return self._new
92+
93+
@property
94+
def session_id(self) -> str:
95+
return self._session_id
96+
97+
@property
98+
def attributes(self) -> Dict:
99+
return self._attributes
100+
101+
@property
102+
def user(self) -> AlexaSkillUser:
103+
return self._user
104+
105+
@property
106+
def application(self) -> AlexaSkillApplication:
107+
return self._application
108+
109+
110+
class AlexaSkillContext:
111+
def __init__(self, AudioPlayer, System, **kwargs):
112+
pass
113+
114+
115+
class AlexaSkillEvent:
116+
def __init__(self, session: Dict,
117+
version: str, request: Dict,
118+
context: Dict):
119+
self._session = AlexaSkillSession(**session)
120+
self._version = version
121+
self._request = AlexaSkillRequest(**request)
122+
self._context = AlexaSkillContext(**context)
123+
124+
@classmethod
125+
def from_event(cls, event):
126+
return cls(**event)
127+
128+
@property
129+
def session(self) -> AlexaSkillSession:
130+
return self._session
131+
132+
@property
133+
def event_version(self) -> str:
134+
return self._version
135+
136+
@property
137+
def request(self) -> AlexaSkillRequest:
138+
return self._request
139+
140+
@property
141+
def context(self) -> AlexaSkillContext:
142+
return self._context

lawip/cf_event.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
from collections import namedtuple
2+
from typing import Dict
3+
4+
HeaderDict = namedtuple("HeaderDict", ['key', 'value'])
5+
6+
7+
def parse_headers(headers):
8+
return {hdr_name: HeaderDict(header[0].get("key", None),
9+
header[0].get("value", None)) for hdr_name, header in headers.items()}
10+
11+
12+
class CfConfig:
13+
def __init__(self, distribution_id: str, request_id: str):
14+
self._distribution_id = distribution_id
15+
self._request_id = request_id
16+
17+
@classmethod
18+
def from_json(cls, config):
19+
return cls(config["distributionId"],
20+
config["requestId"])
21+
22+
@property
23+
def distribution_id(self) -> str:
24+
return self._distribution_id
25+
26+
@property
27+
def request_id(self) -> str:
28+
return self._request_id
29+
30+
31+
class CfRequest:
32+
def __init__(self, client_ip: str,
33+
querystring: str,
34+
uri: str,
35+
method: str,
36+
headers: Dict[str, HeaderDict],
37+
origin):
38+
self._client_ip = client_ip
39+
self._querystring = querystring
40+
self._uri = uri
41+
self._http_method = method
42+
self._headers = headers
43+
self._origin = origin
44+
45+
@classmethod
46+
def from_json(cls, request):
47+
return cls(request["clientIp"],
48+
request["querystring"],
49+
request["uri"],
50+
request["method"],
51+
parse_headers(request["headers"]),
52+
request.get("origin", None))
53+
54+
@property
55+
def client_ip(self) -> str:
56+
return self._client_ip
57+
58+
@property
59+
def querystring(self) -> str:
60+
return self._querystring
61+
62+
@property
63+
def uri(self) -> str:
64+
return self._uri
65+
66+
@property
67+
def http_method(self) -> str:
68+
return self._http_method
69+
70+
@property
71+
def headers(self) -> Dict[str, HeaderDict]:
72+
return self._headers
73+
74+
@property
75+
def origin(self):
76+
return self._origin
77+
78+
79+
class CfResponse:
80+
def __init__(self, status: str, status_description: str, headers: Dict[str, HeaderDict]):
81+
self._status = status
82+
self._status_description = status_description
83+
self._headers = headers
84+
85+
@classmethod
86+
def from_json(cls, response):
87+
if response is not None:
88+
return cls(response["status"],
89+
response["statusDescription"],
90+
parse_headers(response["headers"]))
91+
92+
@property
93+
def status(self) -> str:
94+
return self._status
95+
96+
@property
97+
def status_description(self) -> str:
98+
return self._status_description
99+
100+
@property
101+
def headers(self) -> Dict[str, HeaderDict]:
102+
return self._headers
103+
104+
105+
class CfRecord:
106+
def __init__(self, config: CfConfig,
107+
request: CfRequest,
108+
response: CfResponse):
109+
self._config = config
110+
self._request = request
111+
self._response = response
112+
113+
@classmethod
114+
def from_json(cls, cf):
115+
return cls(CfConfig.from_json(cf["config"]),
116+
CfRequest.from_json(cf["request"]),
117+
CfResponse.from_json(cf.get("response", None)))
118+
119+
@property
120+
def config(self) -> CfConfig:
121+
return self._config
122+
123+
@property
124+
def request(self) -> CfRequest:
125+
return self._request
126+
127+
@property
128+
def response(self) -> CfResponse:
129+
return self._response
130+
131+
132+
class CloudfrontEvent:
133+
def __init__(self, records: [CfRecord]):
134+
self._records = records
135+
136+
@classmethod
137+
def from_event(cls, event):
138+
return cls([CfRecord.from_json(record["cf"]) for record in event["Records"]])
139+
140+
@property
141+
def records(self) -> [CfRecord]:
142+
return self._records
143+
144+
@property
145+
def first_record(self) -> CfRecord:
146+
return self._records[0]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"session": {
3+
"new": false,
4+
"sessionId": "amzn1.echo-api.session.[unique-value-here]",
5+
"attributes": {},
6+
"user": {
7+
"userId": "amzn1.ask.account.[unique-value-here]"
8+
},
9+
"application": {
10+
"applicationId": "amzn1.ask.skill.[unique-value-here]"
11+
}
12+
},
13+
"version": "1.0",
14+
"request": {
15+
"locale": "en-US",
16+
"timestamp": "2016-10-27T21:06:28Z",
17+
"type": "IntentRequest",
18+
"requestId": "amzn1.echo-api.request.[unique-value-here]",
19+
"intent": {
20+
"slots": {
21+
"Item": {
22+
"name": "Item",
23+
"value": "snowball"
24+
}
25+
},
26+
"name": "RecipeIntent"
27+
}
28+
},
29+
"context": {
30+
"AudioPlayer": {
31+
"playerActivity": "IDLE"
32+
},
33+
"System": {
34+
"device": {
35+
"supportedInterfaces": {
36+
"AudioPlayer": {}
37+
}
38+
},
39+
"application": {
40+
"applicationId": "amzn1.ask.skill.[unique-value-here]"
41+
},
42+
"user": {
43+
"userId": "amzn1.ask.account.[unique-value-here]"
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)