Skip to content

Commit a5ea9bb

Browse files
Shreyas-vgrnikhilym
authored andcommitted
Creating ask-sdk-model-runtime package (#6)
* Created ask-sdk-model-runtime package which contains the defintions of LWA servies, implementations of ApiClient and its componenets, also default Serializer definitions. This package can be reused across multiple models released by ask sdk team in future, also the current ask-sdk-models (python) can add this as dependency. * Adding Optional in serializer for mypy test success and doc reference fix * Bug fix on api_client, using 'is not' with literal
1 parent 3b02613 commit a5ea9bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4117
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=========
2+
CHANGELOG
3+
=========
4+
5+
1.0
6+
-------
7+
8+
* Initial release of alexa sdk model runtime.

ask-sdk-model-runtime/MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include README.rst
2+
include CHANGELOG.rst
3+
include LICENSE
4+
include requirements.txt
5+
recursive-exclude tests *
6+
recursive-include ask_sdk_model_runtime py.typed

ask-sdk-model-runtime/README.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
====================================================
2+
ASK SDK Model Runtime - Base components of ASK SDK
3+
====================================================
4+
5+
ask-sdk-model-runtime is the common models package by
6+
the Software Development Kit (SDK) team for Python that
7+
provides common implementations like Serializer, ApiClient
8+
BaseServiceClient, Authentication Configuration etc.
9+
that is used by the SDK models.
10+
11+
Quick Start
12+
-----------
13+
14+
Installation
15+
~~~~~~~~~~~~~~~
16+
Assuming that you have Python and ``virtualenv`` installed, you can
17+
install the package and it's dependencies from PyPi
18+
as follows:
19+
20+
.. code-block:: sh
21+
22+
$ virtualenv venv
23+
$ . venv/bin/activate
24+
$ pip install ask-sdk-model-runtime
25+
26+
27+
You can also install the whole standard package locally by following these steps:
28+
29+
.. code-block:: sh
30+
31+
$ git clone https://github.com/alexa/alexa-apis-for-python.git
32+
$ cd ask_sdk_model_runtime
33+
$ virtualenv venv
34+
...
35+
$ . venv/bin/activate
36+
$ python setup.py install
37+
38+
39+
Usage and Getting Started
40+
-------------------------
41+
42+
Getting started guides, SDK Features, API references, samples etc. can
43+
be found at `Read The Docs <https://alexa-skills-kit-python-sdk.readthedocs.io/en/latest/>`_
44+
45+
46+
Got Feedback?
47+
-------------
48+
49+
- We would like to hear about your bugs, feature requests, questions or quick feedback.
50+
Please search for the `existing issues <https://github.com/alexa/alexa-skills-kit-sdk-for-python/issues>`_ before opening a new one. It would also be helpful
51+
if you follow the templates for issue and pull request creation. Please follow the `contributing guidelines <https://github.com/alexa/alexa-skills-kit-sdk-for-python/blob/master/CONTRIBUTING.md>`_!
52+
- Request and vote for `Alexa features <https://alexa.uservoice.com/forums/906892-alexa-skills-developer-voice-and-vote>`_!
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights
4+
# Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License").
7+
# You may not use this file except in compliance with the License.
8+
# A copy of the License is located at
9+
#
10+
# http://aws.amazon.com/apache2.0/
11+
#
12+
# or in the "license" file accompanying this file. This file is
13+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
14+
# OF ANY KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations under the
16+
# License.
17+
#
18+
from __future__ import absolute_import
19+
20+
from .api_client_message import ApiClientMessage
21+
from .api_client_request import ApiClientRequest
22+
from .api_client_response import ApiClientResponse
23+
from .api_client import DefaultApiClient, ApiClient
24+
from .api_configuration import ApiConfiguration
25+
from .api_response import ApiResponse
26+
from .authentication_configuration import AuthenticationConfiguration
27+
from .base_service_client import BaseServiceClient
28+
from .serializer import DefaultSerializer, Serializer
29+
from .exceptions import (
30+
ServiceException, SerializationException, ApiClientException)
31+
from .service_client_response import ServiceClientResponse
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#
2+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights
3+
# Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License").
6+
# You may not use this file except in compliance with the License.
7+
# A copy of the License is located at
8+
#
9+
# http://aws.amazon.com/apache2.0/
10+
#
11+
# or in the "license" file accompanying this file. This file is
12+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
13+
# OF ANY KIND, either express or implied. See the License for the
14+
# specific language governing permissions and limitations under the
15+
# License.
16+
#
17+
18+
__pip_package_name__ = 'ask-sdk-model-runtime'
19+
__description__ = ('The ASK SDK Model Runtime package provides common provides '
20+
'common implementations like Serializer, ApiClient, '
21+
'Authentication Configuration etc that is used by the SDK '
22+
'models.')
23+
__url__ = 'https://github.com/alexa/alexa-apis-for-python'
24+
__version__ = '1.0.0'
25+
__author__ = 'Alexa Skills Kit'
26+
__author_email__ = '[email protected]'
27+
__license__ = 'Apache 2.0'
28+
__keywords__ = ['ASK SDK Model Runtime', 'Alexa Skills Kit', 'Alexa', 'Model', 'Runtime']
29+
__install_requires__ = ["requests", "python_dateutil"]
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights
4+
# Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License").
7+
# You may not use this file except in compliance with the License.
8+
# A copy of the License is located at
9+
#
10+
# http://aws.amazon.com/apache2.0/
11+
#
12+
# or in the "license" file accompanying this file. This file is
13+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
14+
# OF ANY KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations under the
16+
# License.
17+
#
18+
import typing
19+
import requests
20+
import six
21+
import json
22+
23+
from abc import ABCMeta, abstractmethod
24+
from urllib3.util import parse_url
25+
26+
from .api_client_response import ApiClientResponse
27+
28+
from .exceptions import ApiClientException
29+
30+
if typing.TYPE_CHECKING:
31+
from typing import Callable, Dict, List, Tuple
32+
from ask_sdk_model_runtime import ApiClientRequest, ApiClientResponse
33+
34+
35+
class ApiClient(object):
36+
"""Represents a basic contract for API request invocation."""
37+
__metaclass__ = ABCMeta
38+
39+
@abstractmethod
40+
def invoke(self, request):
41+
# type: (ApiClientRequest) -> ApiClientResponse
42+
"""Dispatches a request to an API endpoint described in the request.
43+
The ApiClient is expected to resolve in the case an API returns
44+
a non-200 HTTP status code. The responsibility of translating a
45+
particular response code to an error lies with the caller.
46+
:param request: Request to dispatch to the ApiClient
47+
:type request: ApiClientRequest
48+
:return: Response from the client call
49+
:rtype: ApiClientResponse
50+
"""
51+
pass
52+
53+
54+
class DefaultApiClient(ApiClient):
55+
"""Default ApiClient implementation of
56+
:py:class:`ask_sdk_model_runtime.api_client.ApiClient` using the
57+
`requests` library.
58+
"""
59+
60+
def invoke(self, request):
61+
# type: (ApiClientRequest) -> ApiClientResponse
62+
"""Dispatches a request to an API endpoint described in the
63+
request.
64+
Resolves the method from input request object, converts the
65+
list of header tuples to the required format (dict) for the
66+
`requests` lib call and invokes the method with corresponding
67+
parameters on `requests` library. The response from the call is
68+
wrapped under the `ApiClientResponse` object and the
69+
responsibility of translating a response code and response/
70+
error lies with the caller.
71+
:param request: Request to dispatch to the ApiClient
72+
:type request: ApiClientRequest
73+
:return: Response from the client call
74+
:rtype: ApiClientResponse
75+
:raises: :py:class:`ask_sdk_model_runtime.exceptions.ApiClientException`
76+
"""
77+
try:
78+
http_method = self._resolve_method(request)
79+
http_headers = self._convert_list_tuples_to_dict(
80+
headers_list=request.headers)
81+
82+
parsed_url = parse_url(request.url)
83+
if parsed_url.scheme is None or parsed_url.scheme != "https":
84+
raise ApiClientException(
85+
"Requests against non-HTTPS endpoints are not allowed.")
86+
87+
if request.body:
88+
body_content_type = http_headers.get("Content-type", None)
89+
if (body_content_type is not None and
90+
"json" in body_content_type):
91+
raw_data = json.dumps(request.body)
92+
else:
93+
raw_data = request.body
94+
else:
95+
raw_data = None
96+
97+
http_response = http_method(
98+
url=request.url, headers=http_headers, data=raw_data)
99+
100+
return ApiClientResponse(
101+
headers=self._convert_dict_to_list_tuples(
102+
http_response.headers),
103+
status_code=http_response.status_code,
104+
body=http_response.text)
105+
except Exception as e:
106+
raise ApiClientException(
107+
"Error executing the request: {}".format(str(e)))
108+
109+
def _resolve_method(self, request):
110+
# type: (ApiClientRequest) -> Callable
111+
"""Resolve the method from request object to `requests` http
112+
call.
113+
:param request: Request to dispatch to the ApiClient
114+
:type request: ApiClientRequest
115+
:return: The HTTP method that maps to the request call.
116+
:rtype: Callable
117+
:raises :py:class:`ask_sdk_model_runtime.exceptions.ApiClientException`
118+
if invalid http request method is being called
119+
"""
120+
try:
121+
return getattr(requests, request.method.lower())
122+
except AttributeError:
123+
raise ApiClientException(
124+
"Invalid request method: {}".format(request.method))
125+
126+
def _convert_list_tuples_to_dict(self, headers_list):
127+
# type: (List[Tuple[str, str]]) -> Dict[str, str]
128+
"""Convert list of tuples from headers of request object to
129+
dictionary format.
130+
:param headers_list: List of tuples made up of two element
131+
strings from `ApiClientRequest` headers variable
132+
:type headers_list: List[Tuple[str, str]]
133+
:return: Dictionary of headers in keys as strings and values
134+
as comma separated strings
135+
:rtype: Dict[str, str]
136+
"""
137+
headers_dict = {} # type: Dict
138+
if headers_list is not None:
139+
for header_tuple in headers_list:
140+
key, value = header_tuple[0], header_tuple[1]
141+
if key in headers_dict:
142+
headers_dict[key] = "{}, {}".format(
143+
headers_dict[key], value)
144+
else:
145+
headers_dict[header_tuple[0]] = value
146+
return headers_dict
147+
148+
def _convert_dict_to_list_tuples(self, headers_dict):
149+
# type: (Dict[str, str]) -> List[Tuple[str, str]]
150+
"""Convert headers dict to list of string tuples format for
151+
`ApiClientResponse` headers variable.
152+
:param headers_dict: Dictionary of headers in keys as strings
153+
and values as comma separated strings
154+
:type headers_dict: Dict[str, str]
155+
:return: List of tuples made up of two element strings from
156+
headers of client response
157+
:rtype: List[Tuple[str, str]]
158+
"""
159+
headers_list = []
160+
if headers_dict is not None:
161+
for key, values in six.iteritems(headers_dict):
162+
for value in values.split(","):
163+
value = value.strip()
164+
if value is not None and value != '':
165+
headers_list.append((key, value.strip()))
166+
return headers_list
167+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"). You may
6+
# not use this file except in compliance with the License. A copy of the
7+
# License is located at
8+
#
9+
# http://aws.amazon.com/apache2.0/
10+
#
11+
# or in the "license" file accompanying this file. This file is distributed
12+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13+
# express or implied. See the License for the specific language governing
14+
# permissions and limitations under the License.
15+
#
16+
import typing
17+
18+
if typing.TYPE_CHECKING:
19+
from typing import List, Tuple
20+
21+
22+
class ApiClientMessage(object):
23+
"""Represents the interface between
24+
:py:class:`ask_sdk_model_runtime.api_client.ApiClient` implementation
25+
and a Service Client.
26+
27+
:param headers: List of header tuples
28+
:type headers: list[tuple[str, str]]
29+
:param body: Body of the message
30+
:type body: str
31+
"""
32+
33+
def __init__(self, headers=None, body=None):
34+
# type: (List[Tuple[str, str]], str) -> None
35+
"""Represents the interface between
36+
:py:class:`ask_sdk_model_runtime.api_client.ApiClient`
37+
implementation and a Service Client.
38+
39+
:param headers: List of header tuples
40+
:type headers: list[tuple[str, str]]
41+
:param body: Body of the message
42+
:type body: str
43+
"""
44+
if headers is None:
45+
headers = []
46+
self.headers = headers
47+
self.body = body

0 commit comments

Comments
 (0)