Browse Source

add full example

Damien Accorsi 6 years ago
parent
commit
217670f185

+ 39 - 0
example/usermanagement/schema.py View File

@@ -0,0 +1,39 @@
1
+# -*- coding: utf-8 -*-
2
+import marshmallow
3
+
4
+class NoContentSchema(marshmallow.Schema):
5
+    pass
6
+
7
+
8
+class AboutSchema(marshmallow.Schema):
9
+    """ Representation of the /about route """
10
+    version = marshmallow.fields.String(required=True)
11
+    datetime = marshmallow.fields.DateTime(required=True)
12
+
13
+
14
+class UserIdPathSchema(marshmallow.Schema):
15
+    """
16
+    representation of a user id in the uri. This allow to define rules for
17
+    what is expected. For example, you may want to limit id to number between
18
+    1 and 999
19
+    """
20
+    id = marshmallow.fields.Int(
21
+        required=True,
22
+        validate=marshmallow.validate.Range(min=1),
23
+    )
24
+
25
+
26
+class UserSchema(marshmallow.Schema):
27
+    """Complete representation of a user"""
28
+    id = marshmallow.fields.Int(required=True)
29
+    first_name = marshmallow.fields.String(required=True)
30
+    last_name = marshmallow.fields.String(required=True)
31
+    email_address = marshmallow.fields.Email(required=True)
32
+    display_name = marshmallow.fields.String(required=False)
33
+    company = marshmallow.fields.String(required=False)
34
+
35
+
36
+class UserDigestSchema(marshmallow.Schema):
37
+    """User representation for listing"""
38
+    id = marshmallow.fields.Int(required=True)
39
+    display_name = marshmallow.fields.String(required=False, default='')

+ 115 - 0
example/usermanagement/serve_bottle.py View File

@@ -0,0 +1,115 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import bottle
4
+from datetime import datetime
5
+from http import HTTPStatus
6
+import json
7
+import time
8
+
9
+from hapic import Hapic
10
+from hapic.data import HapicData
11
+from hapic.ext.bottle import BottleContext
12
+
13
+from example.usermanagement.schema import AboutSchema
14
+from example.usermanagement.schema import NoContentSchema
15
+from example.usermanagement.schema import UserDigestSchema
16
+from example.usermanagement.schema import UserIdPathSchema
17
+from example.usermanagement.schema import UserSchema
18
+
19
+from example.usermanagement.userlib import User
20
+from example.usermanagement.userlib import UserLib
21
+from example.usermanagement.userlib import UserNotFound
22
+
23
+hapic = Hapic()
24
+
25
+
26
+class BottleController(object):
27
+    @hapic.with_api_doc()
28
+    @hapic.output_body(AboutSchema())
29
+    def about(self, context, request):
30
+        """
31
+        This endpoint allow to check that the API is running. This description
32
+        is generated from the docstring of the method.
33
+        """
34
+        return {
35
+            'version': '1.2.3',
36
+            'datetime': datetime.now(),
37
+        }
38
+
39
+    @hapic.with_api_doc()
40
+    @hapic.output_body(UserDigestSchema(many=True))
41
+    def get_users(self):
42
+        """
43
+        Obtain users list.
44
+        """
45
+        return UserLib().get_users()
46
+
47
+    @hapic.with_api_doc()
48
+    @hapic.handle_exception(UserNotFound, HTTPStatus.NOT_FOUND)
49
+    @hapic.input_path(UserIdPathSchema())
50
+    @hapic.output_body(UserSchema())
51
+    def get_user(self, id, hapic_data: HapicData):
52
+        """
53
+        Return a user taken from the list or return a 404
54
+        """
55
+        return UserLib().get_user(int(hapic_data.path['id']))
56
+
57
+    @hapic.with_api_doc()
58
+    # TODO - G.M - 2017-12-5 - Support input_forms ?
59
+    # TODO - G.M - 2017-12-5 - Support exclude, only ?
60
+    @hapic.input_body(UserSchema(exclude=('id',)))
61
+    @hapic.output_body(UserSchema())
62
+    def add_user(self, hapic_data: HapicData):
63
+        """
64
+        Add a user to the list
65
+        """
66
+        print(hapic_data.body)
67
+        new_user = User(**hapic_data.body)
68
+        return UserLib().add_user(new_user)
69
+
70
+    @hapic.with_api_doc()
71
+    @hapic.handle_exception(UserNotFound, HTTPStatus.NOT_FOUND)
72
+    @hapic.output_body(NoContentSchema(), default_http_code=204)
73
+    @hapic.input_path(UserIdPathSchema())
74
+    def del_user(self, id, hapic_data: HapicData):
75
+        UserLib().del_user(int(hapic_data.path['id']))
76
+        return NoContentSchema()
77
+
78
+    def bind(self, app:bottle.Bottle):
79
+        app.route('/about', callback=self.about)
80
+        app.route('/users', callback=self.get_users)
81
+        app.route('/users/<id>', callback=self.get_user)
82
+        app.route('/users', callback=self.add_user,  method='POST')
83
+        app.route('/users/<id>', callback=self.del_user, method='DELETE')
84
+
85
+
86
+if __name__ == "__main__":
87
+    app = bottle.Bottle()
88
+    controllers = BottleController()
89
+    controllers.bind(app)
90
+    hapic.set_context(BottleContext(app))
91
+
92
+    print('')
93
+    print('')
94
+    print('GENERATING OPENAPI DOCUMENTATION')
95
+    openapi_file_name = 'api-documentation.json'
96
+    with open(openapi_file_name, 'w') as openapi_file_handle:
97
+        openapi_file_handle.write(
98
+            json.dumps(
99
+                hapic.generate_doc(
100
+                    title='Demo API documentation',
101
+                    description='This documentation has been generated from '
102
+                                'code. You can see it using swagger: '
103
+                                'http://editor2.swagger.io/'
104
+                )
105
+            )
106
+        )
107
+
108
+    print('Documentation generated in {}'.format(openapi_file_name))
109
+    time.sleep(1)
110
+
111
+    print('')
112
+    print('')
113
+    print('RUNNING BOTTLE SERVER NOW')
114
+    # Run app
115
+    app.run(host='127.0.0.1', port=8081, debug=True)

+ 66 - 0
example/usermanagement/userlib.py View File

@@ -0,0 +1,66 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""
4
+This module implements a basic User management library
5
+"""
6
+
7
+class User(object):
8
+    def __init__(
9
+        self,
10
+        id=0,
11
+        first_name='first',
12
+        last_name='last',
13
+        email_address='',
14
+        company=''
15
+    ):
16
+        self.id = id
17
+        self.first_name = first_name
18
+        self.last_name = last_name
19
+        self.email_address = email_address
20
+        self.company = company
21
+
22
+    @property
23
+    def display_name(self):
24
+        return '{} {}'.format(self.first_name, self.last_name)
25
+
26
+
27
+class UserNotFound(Exception):
28
+    pass
29
+
30
+
31
+class UserLib(object):
32
+    """
33
+    A dummy library to list, add and delete users
34
+    """
35
+    USERS = []
36
+
37
+    def add_user(self, user: User) -> User:
38
+        user.id = 1 + max(u.id for u in UserLib.USERS)
39
+        UserLib.USERS.append(user)
40
+        return user
41
+
42
+    def del_user(self, user_id: int) -> None:
43
+        try:
44
+            UserLib.USERS.pop(user_id - 1)
45
+        except:
46
+            raise UserNotFound
47
+
48
+    def get_user(self, user_id: int) -> User:
49
+        try:
50
+            return UserLib.USERS[user_id - 1]
51
+        except:
52
+            raise UserNotFound
53
+
54
+    def get_users(self) -> [User]:
55
+        return UserLib.USERS
56
+
57
+
58
+UserLib.USERS.append(
59
+    User(**{
60
+        'id': 1,
61
+        'first_name': 'Damien',
62
+        'last_name': 'Accorsi',
63
+        'email_address': 'damien.accorsi@algoo.fr',
64
+        'company': 'Algoo',
65
+    }),
66
+)