Browse Source

WIP Pyramid backend

Guénaël Muller 6 years ago
parent
commit
88394195bc

+ 139 - 0
example_a2.py View File

@@ -0,0 +1,139 @@
1
+# -*- coding: utf-8 -*-
2
+import json
3
+from http import HTTPStatus
4
+
5
+from pyramid.view import view_config
6
+from pyramid.config import Configurator
7
+from wsgiref.simple_server import make_server
8
+import time
9
+import yaml
10
+from beaker.middleware import SessionMiddleware
11
+
12
+import hapic
13
+from example import HelloResponseSchema, HelloPathSchema, HelloJsonSchema, \
14
+    ErrorResponseSchema, HelloQuerySchema
15
+from hapic.data import HapicData
16
+
17
+# hapic.global_exception_handler(UnAuthExc, StandardErrorSchema)
18
+# hapic.global_exception_handler(UnAuthExc2, StandardErrorSchema)
19
+# hapic.global_exception_handler(UnAuthExc3, StandardErrorSchema)
20
+# bottle.default_app.push(app)
21
+
22
+# session_opts = {
23
+#     'session.type': 'file',
24
+#     'session.data_dir': '/tmp',
25
+#     'session.cookie_expires': 3600,
26
+#     'session.auto': True
27
+# }
28
+# session_middleware = SessionMiddleware(
29
+#     app,
30
+#     session_opts,
31
+#     environ_key='beaker.session',
32
+#     key='beaker.session.id',
33
+# )
34
+# app = session_middleware.wrap_app
35
+
36
+
37
+def bob(f):
38
+    def boby(*args, **kwargs):
39
+        return f(*args, **kwargs)
40
+    return boby
41
+
42
+class Controllers(object):
43
+    
44
+    @hapic.with_api_doc()
45
+    # @hapic.ext.bottle.bottle_context()
46
+    @hapic.handle_exception(ZeroDivisionError, http_code=HTTPStatus.BAD_REQUEST)
47
+    @hapic.input_path(HelloPathSchema())
48
+    @hapic.input_query(HelloQuerySchema())
49
+    @hapic.output_body(HelloResponseSchema())
50
+    def hello(self,context,request,hapic_data: HapicData):
51
+        """
52
+        my endpoint hello
53
+        ---
54
+        get:
55
+            description: my description
56
+            parameters:
57
+                - in: "path"
58
+                  description: "hello"
59
+                  name: "name"
60
+                  type: "string"
61
+            responses:
62
+                200:
63
+                    description: A pet to be returned
64
+                    schema: HelloResponseSchema
65
+        """
66
+        name = request.matchdict.get('name', None)
67
+        if name == 'zero':
68
+            raise ZeroDivisionError('Don\'t call him zero !')
69
+
70
+        return {
71
+            'sentence': 'Hello !',
72
+            'name': name,
73
+       }
74
+
75
+    
76
+    # @hapic.with_api_doc()
77
+    # # @hapic.ext.bottle.bottle_context()
78
+    # # @hapic.error_schema(ErrorResponseSchema())
79
+    # #@hapic.input_path(HelloPathSchema())
80
+    # #@hapic.input_body(HelloJsonSchema())
81
+    # #@hapic.output_body(HelloResponseSchema())
82
+    # @bob
83
+    # def hello2(self, name: str, hapic_data: HapicData):
84
+    #     return {
85
+    #         'sentence': 'Hello !',
86
+    #         'name': name,
87
+    #         'color': hapic_data.body.get('color'),
88
+    #     }
89
+
90
+    # kwargs = {'validated_data': {'name': 'bob'}, 'name': 'bob'}
91
+
92
+    
93
+    # @view_config(renderer='json')
94
+    # @hapic.with_api_doc()
95
+    # # @hapic.ext.bottle.bottle_context()
96
+    # # @hapic.error_schema(ErrorResponseSchema())
97
+    # @hapic.input_path(HelloPathSchema())
98
+    # @hapic.output_body(HelloResponseSchema())
99
+    # def hello3(self, name: str):
100
+    #     return {
101
+    #         'sentence': 'Hello !',
102
+    #         'name': name,
103
+    #     }
104
+
105
+    def bind(self, config):
106
+        config.add_route('hello', '/hello/{name}', request_method='GET')
107
+        #config.add_route('hello2', '/hello/{name}', request_method='POST')
108
+        #config.add_route('hello3', '/hello/{name}', request_method='GET')
109
+        config.add_view(self.hello, route_name='hello', renderer='json')
110
+        #config.add_view(self.hello2, route_name='hello2')
111
+        #config.add_view(self.hello3, route_name='hello3')
112
+
113
+
114
+with Configurator() as config:
115
+    controllers = Controllers()
116
+    controllers.bind(config)
117
+    config.include('pyramid_debugtoolbar')
118
+    app = config
119
+
120
+
121
+
122
+
123
+# time.sleep(1)
124
+# s = hapic.generate_doc(app)
125
+# ss = json.loads(json.dumps(s))
126
+# for path in ss['paths']:
127
+#     for method in ss['paths'][path]:
128
+#         for response_code in ss['paths'][path][method]['responses']:
129
+#             ss['paths'][path][method]['responses'][int(response_code)] = ss['paths'][path][method]['responses'][response_code]
130
+#             del ss['paths'][path][method]['responses'][int(response_code)]
131
+# print(yaml.dump(ss, default_flow_style=False))
132
+# time.sleep(1)
133
+
134
+hapic.set_context(hapic.ext.pyramid.PyramidContext())
135
+import pdb; pdb.set_trace()
136
+#print(json.dumps(hapic.generate_doc(app)))
137
+# app.run(host='localhost', port=8080, debug=True)
138
+server = make_server('0.0.0.0', 6543, app.make_wsgi_app())
139
+server.serve_forever()

+ 174 - 0
example_a_pyramid.py View File

@@ -0,0 +1,174 @@
1
+# -*- coding: utf-8 -*-
2
+import json
3
+from http import HTTPStatus
4
+
5
+#import bottle
6
+from pyramid.config import Configurator
7
+from wsgiref.simple_server import make_server
8
+import time
9
+import yaml
10
+import uuid
11
+from beaker.middleware import SessionMiddleware
12
+
13
+import hapic
14
+from example import HelloResponseSchema, HelloPathSchema, HelloJsonSchema, \
15
+    ErrorResponseSchema, HelloQuerySchema
16
+from hapic.data import HapicData
17
+
18
+# hapic.global_exception_handler(UnAuthExc, StandardErrorSchema)
19
+# hapic.global_exception_handler(UnAuthExc2, StandardErrorSchema)
20
+# hapic.global_exception_handler(UnAuthExc3, StandardErrorSchema)
21
+# bottle.default_app.push(app)
22
+
23
+# session_opts = {
24
+#     'session.type': 'file',
25
+#     'session.data_dir': '/tmp',
26
+#     'session.cookie_expires': 3600,
27
+#     'session.auto': True
28
+# }
29
+# session_middleware = SessionMiddleware(
30
+#     app,
31
+#     session_opts,
32
+#     environ_key='beaker.session',
33
+#     key='beaker.session.id',
34
+# )
35
+# app = session_middleware.wrap_app
36
+
37
+
38
+def bob(f):
39
+    def boby(*args, **kwargs):
40
+        return f(*args, **kwargs)
41
+    return boby
42
+
43
+
44
+class Controllers(object):
45
+    @hapic.with_api_doc()
46
+    # @hapic.ext.bottle.bottle_context()
47
+    @hapic.handle_exception(ZeroDivisionError, http_code=HTTPStatus.BAD_REQUEST)
48
+    @hapic.input_path(HelloPathSchema())
49
+    @hapic.input_query(HelloQuerySchema())
50
+    @hapic.output_body(HelloResponseSchema())
51
+    def hello(self, context, request, hapic_data: HapicData):
52
+        """
53
+        my endpoint hello
54
+        ---
55
+        get:
56
+            description: my description
57
+            parameters:
58
+                - in: "path"
59
+                  description: "hello"
60
+                  name: "name"
61
+                  type: "string"
62
+            responses:
63
+                200:
64
+                    description: A pet to be returned
65
+                    schema: HelloResponseSchema
66
+        """
67
+        name = request.matchdict.get('name', None)
68
+        if name == 'zero':
69
+            raise ZeroDivisionError('Don\'t call him zero !')
70
+
71
+        return {
72
+            'sentence': 'Hello !',
73
+            'name': name,
74
+        }
75
+
76
+    @hapic.with_api_doc()
77
+    # @hapic.ext.bottle.bottle_context()
78
+    # @hapic.error_schema(ErrorResponseSchema())
79
+    @hapic.input_path(HelloPathSchema())
80
+    @hapic.input_body(HelloJsonSchema())
81
+    @hapic.output_body(HelloResponseSchema())
82
+    @bob
83
+    def hello2(self, context, request, hapic_data: HapicData):
84
+        name = request.matchdict.get('name', None)
85
+        return {
86
+            'sentence': 'Hello !',
87
+            'name': name,
88
+            'color': hapic_data.body.get('color'),
89
+        }
90
+
91
+    kwargs = {'validated_data': {'name': 'bob'}, 'name': 'bob'}
92
+
93
+    @hapic.with_api_doc()
94
+    # @hapic.ext.bottle.bottle_context()
95
+    # @hapic.error_schema(ErrorResponseSchema())
96
+    @hapic.input_path(HelloPathSchema())
97
+    @hapic.output_body(HelloResponseSchema())
98
+    def hello3(self, context, request, hapic_data: HapicData):
99
+        name = request.matchdict.get('name', None)
100
+        return {
101
+            'sentence': 'Hello !',
102
+            'name': name,
103
+        }
104
+
105
+    def bind(self, app):
106
+        app.route('/hello/{name}', callback=self.hello)
107
+        app.route('/hello/{name}', callback=self.hello2, method='POST')
108
+        app.route('/hello3/{name}', callback=self.hello3)
109
+        app.config.include('pyramid_debugtoolbar')
110
+
111
+
112
+class PyramRoute(object):
113
+
114
+    def __init__(self, app, rule, method, callback, name, **options):
115
+        self.app = app
116
+        self.rule = rule
117
+        self.method = method
118
+        self.callback = callback
119
+        self.name = name
120
+
121
+        if not self.name:
122
+            self.name = str(uuid.uuid4())
123
+
124
+        with self.app.config as config:
125
+            config.add_route(self.name, self.rule, request_method=self.method)
126
+            config.add_view(
127
+                self.callback, route_name=self.name, renderer='json')
128
+        #import pdb; pdb.set_trace()
129
+
130
+
131
+class Pyramidapp(object):
132
+
133
+    def __init__(self):
134
+        self.config = Configurator()
135
+        self.routes = []
136
+
137
+    def route(self,
138
+              rule,
139
+              callback,
140
+              method='GET',
141
+              name=None,
142
+              **options):
143
+        r = PyramRoute(self, rule, method, callback, name, **options)
144
+        self.routes.append(r)
145
+
146
+    def run(self, host, port, debug):
147
+        server = make_server('0.0.0.0', port, self.config.make_wsgi_app())
148
+        server.serve_forever()
149
+
150
+
151
+app = Pyramidapp()
152
+
153
+controllers = Controllers()
154
+controllers.bind(app)
155
+
156
+
157
+# time.sleep(1)
158
+# s = hapic.generate_doc(app)
159
+# ss = json.loads(json.dumps(s))
160
+# for path in ss['paths']:
161
+#     for method in ss['paths'][path]:
162
+#         for response_code in ss['paths'][path][method]['responses']:
163
+#             ss['paths'][path][method]['responses'][int(response_code)] = ss['paths'][path][method]['responses'][response_code]
164
+#             del ss['paths'][path][method]['responses'][int(response_code)]
165
+# print(yaml.dump(ss, default_flow_style=False))
166
+# time.sleep(1)
167
+
168
+# hapic.set_context(hapic.ext.bottle.BottleContext())
169
+hapic.set_context(hapic.ext.pyramid.PyramidContext())
170
+#import pdb; pdb.set_trace()
171
+print(json.dumps(hapic.generate_doc(app)))
172
+app.run('localhost', 8080, True)
173
+#server = make_server('0.0.0.0', 8080, app.config.make_wsgi_app())
174
+# server.serve_forever()

+ 1 - 1
hapic/ext/__init__.py View File

@@ -1,2 +1,2 @@
1 1
 # -*- coding: utf-8 -*-
2
-from hapic.ext import bottle
2
+from hapic.ext import bottle, pyramid

+ 1 - 1
hapic/ext/bottle/context.py View File

@@ -14,7 +14,7 @@ class BottleContext(ContextInterface):
14 14
     def get_request_parameters(self, *args, **kwargs) -> RequestParameters:
15 15
         return RequestParameters(
16 16
             path_parameters=bottle.request.url_args,
17
-            query_parameters=bottle.request.params,
17
+            query_parameters=bottle.request.params, ## query?
18 18
             body_parameters=bottle.request.json,
19 19
             form_parameters=bottle.request.forms,
20 20
             header_parameters=bottle.request.headers,

+ 2 - 0
hapic/ext/pyramid/__init__.py View File

@@ -0,0 +1,2 @@
1
+# -*- coding: utf-8 -*-
2
+from hapic.ext.pyramid.context import PyramidContext

+ 69 - 0
hapic/ext/pyramid/context.py View File

@@ -0,0 +1,69 @@
1
+# -*- coding: utf-8 -*-
2
+import json
3
+import typing
4
+from http import HTTPStatus
5
+
6
+from pyramid.request import Request
7
+from pyramid.response import Response
8
+
9
+
10
+from hapic.context import ContextInterface
11
+from hapic.exception import OutputValidationException
12
+from hapic.processor import RequestParameters, ProcessValidationError
13
+
14
+
15
+class PyramidContext(ContextInterface):
16
+    def get_request_parameters(self, *args, **kwargs) -> RequestParameters:
17
+        req = args[-1]  # TODO : Check
18
+        assert isinstance(req, Request)
19
+        # TODO : move this code to check_json
20
+        # same idea as in : https://bottlepy.org/docs/dev/_modules/bottle.html#BaseRequest.json
21
+        if req.body and req.content_type in ('application/json', 'application/json-rpc'):
22
+            json_body = req.json_body
23
+            # TODO : raise exception if not correct , return 400 if uncorrect instead ?
24
+        else:
25
+            json_body = None
26
+
27
+        return RequestParameters(
28
+            path_parameters=req.matchdict,
29
+            query_parameters=req.GET,
30
+            body_parameters=json_body,
31
+            form_parameters=req.POST,
32
+            header_parameters=req.headers,
33
+        )
34
+
35
+    def get_response(
36
+        self,
37
+        response: dict,
38
+        http_code: int,
39
+    ) -> Response:
40
+        return Response(
41
+            body=json.dumps(response),
42
+            headers=[
43
+                ('Content-Type', 'application/json'),
44
+            ],
45
+            status=http_code,
46
+        )
47
+
48
+    def get_validation_error_response(
49
+        self,
50
+        error: ProcessValidationError,
51
+        http_code: HTTPStatus=HTTPStatus.BAD_REQUEST,
52
+    ) -> typing.Any:
53
+        # TODO BS 20171010: Manage error schemas, see #4
54
+        from hapic.hapic import _default_global_error_schema
55
+        unmarshall = _default_global_error_schema.dump(error)
56
+        if unmarshall.errors:
57
+            raise OutputValidationException(
58
+                'Validation error during dump of error response: {}'.format(
59
+                    str(unmarshall.errors)
60
+                )
61
+            )
62
+
63
+        return Response(
64
+            body=json.dumps(unmarshall.data),
65
+            headers=[
66
+                ('Content-Type', 'application/json'),
67
+            ],
68
+            status=int(http_code),
69
+        )