Statistics
| Branch: | Tag: | Revision:

root / docs / developers / extending-clients-api.rst @ cfb87e18

History | View | Annotate | Download (7.9 kB)

1
Extending kamaki.clients
2
========================
3

    
4
By default, kamaki clients implement REST APIs, therefore they manage HTTP
5
requests and responses to communicate with services.
6

    
7
How to build a client
8
---------------------
9

    
10
All service clients consist of a subclass of the Client class and implement
11
separate client functionalities as member methods. There is also an error class
12
to raise exceptions that can be handled by kamaki interfaces.
13

    
14
.. code-block:: python
15
    
16
    #  ${KAMAKI_PATH}/kamaki/clients/mynewclient.py
17

    
18
    from kamaki.clients import Client, ClientError
19

    
20

    
21
    class MyNewClient(Client):
22
        """MyNewClient Description Here"""
23

    
24
        def my_first_method(self, **args):
25
            """Method description"""
26
            try:
27
                ...
28
                method code
29
                ...
30
            except SomeKnownException as e1:
31
                raise ClientError('MyError: %s' % e1)
32
            except SomeOtherException as e2:
33
                raise ClientError('MyError: %s' % e2)
34

    
35
        def my_second_method(self, **params):
36
            """Method description"""
37
            ...
38

    
39
Custom clients can use a set of convenience methods for easy HTTP requests
40

    
41
.. code-block:: python
42

    
43
    def get(self, path, **kwargs)
44
    def head(self, path, **kwargs)
45
    def post(self, path, **kwargs)
46
    def put(self, path, **kwargs)
47
    def delete(self, path, **kwargs)
48
    def copy(self, path, **kwargs)
49
    def move(self, path, **kwargs)
50

    
51
How to use your client
52
----------------------
53

    
54
External applications must instantiate a MyNewClient object.
55

    
56
.. code-block:: python
57

    
58
    from kamaki.clients import ClientError
59
    from kamaki.clients.mynewclient import MyNewClient
60

    
61
    ...
62
    try:
63
        cl = MyNewClient(args)
64
        cl.my_first_method(other_args)
65
    except ClientError as cle:
66
        print('Client Error: %s' % cle)
67
    ...
68

    
69
Concurrency control
70
-------------------
71

    
72
Kamaki clients may handle multiple requests with threads. The easiest way is
73
using the `async_run` method, fed with a list of argument dictionaries (one for
74
each call of the single method).
75

    
76
.. code-block:: python
77

    
78
    class MyNewClient(Client):
79
        ...
80

    
81
        def _single_threaded_method(self, **args):
82
            ...
83

    
84
        def multithread_method(self):
85
            kwarg_list = [kwarg for each run]
86
            self.async_run(self._single_threaded_method, kwarg_list)
87

    
88
Going agile
89
-----------
90

    
91
The kamaki.clients package contains a set of fine-grained unit-tests for all
92
APIs. 
93

    
94
.. note:: unit tests require the optional python-mock package, version 1.X or
95
    better
96

    
97
Using the tests
98
^^^^^^^^^^^^^^^
99

    
100
First, the source code of kamaki must be accessible. If this is not the case,
101
the source code can be obtained from here:
102

    
103
`https://code.grnet.gr/projects/kamaki/files <https://code.grnet.gr/projects/kamaki/files>`_
104

    
105
In each package under kamaki.clients, there is a test module (`test.py`). To
106
run all tests, run the test.py file from kamaki.clients
107

    
108
.. code-block:: console
109

    
110
    $ cd ${KAMAKI_SOURCE_LOCATION}/kamaki/clients
111
    $ python test.py
112

    
113
To test a specific class, add the class name as an argument. e.g., for the
114
Client class:
115

    
116
.. code-block:: console
117

    
118
    $ python test.py Client
119

    
120
To test a specific method in a class, apply an extra argument, e.g., for the
121
request method in the Client class:
122

    
123
.. code-block:: console
124

    
125
    $ python test.py Client request
126

    
127
Each package contains a test module (test.py) which is also runnable from the
128
command line. e.g., in the pithos package there is a test module which
129
contains, among others, the **download** sub-test:
130

    
131
.. code-block:: console
132

    
133
    $ cd pithos
134

    
135
    # Run all kamaki.clients.pithos tests
136
    $ python test.py
137

    
138
    # Run all kamaki.clients.pithos.PithoClient tests
139
    $ python test.py Pithos
140

    
141
    # Test kamaki.clients.pithos.PithosClient.download
142
    $ python test.py Pithos download
143

    
144
To fully test a specific package, run test.py from the package location. e.g.,
145
to test everything in kamaki.clients.pithos package:
146

    
147
.. code-block:: console
148

    
149
    $ cd pithos
150
    $ python test.py
151

    
152
Mechanism
153
^^^^^^^^^
154

    
155
Each folder / package contains a test.py file, where its test module lived. All
156
test modules contain a set of classes that extent the TestCase class. They also
157
contain a main method to run the tests.
158

    
159
By convention, testing classes have the same name as the class they test.
160
Methods not grouped in classes are tested by classes named after their
161
respective module.
162

    
163
For example, the *kamaki.clients.pithos.PithosClient* class is tested by the
164
*kamaki.clients.pithos.test.PithosClient* class, while the methods in
165
*kamaki.clients.utils* module are tested by *kamaki.clients.utils.test.Utils*.
166

    
167
Adding unit tests
168
^^^^^^^^^^^^^^^^^
169

    
170
After modifying or extending *kamaki.clients* method, classes, modules or
171
packages, it is a good practice to also modify or extend the corresponding
172
unit tests. What's more, it is recommended to modify or implement the testing
173
of new behavior before implementing the behavior itself. The goal is to
174
preserve an 1 to 1 mapping between methods and their tests.
175

    
176
Modifying an existing method
177
""""""""""""""""""""""""""""
178

    
179
In case of an existing method modification, the programmer has to modify the
180
corresponding test as well. By convention, the test method is located in the
181
test module under the same package, in a TestCase subclass that is named with a
182
name similar to the package or class that contains the tested method.
183

    
184
Example: to modify *kamaki.clients.pithos.PithosRestClient.object_get*, the
185
programmer has to also adjust the
186
*kamaki.clients.pithos.test.PithosRestClient.test.object_get* method.
187

    
188
Adding a new method
189
"""""""""""""""""""
190

    
191
To implement a new method in an existing class, developers are encouraged to
192
implement the corresponding unit test first. In order to do that, they should
193
find the testing class that is mapped to the class or module they work on.
194

    
195
Example: Adding **list_special** to *kamaki.clients.astakos.AstakosClient*,
196
requires changes to *kamaki.clients.astakos.test.AstakosClient* too, as shown
197
bellow:
198

    
199
.. code-block:: python
200

    
201
    # file: ${kamaki}/kamaki/clients/astakos/__init__.py
202

    
203
    class AstakosClient(TestCase):
204
        ...
205
        def test_list_special(self):
206
            """Test the list_special method"""
207
            ...
208

    
209
Implementing a new class or module
210
""""""""""""""""""""""""""""""""""
211

    
212
Each class or module needs a seperate test sub-module. By convention, each
213
class or module under *kamaki.clients*, should be located in a separate
214
directory.
215

    
216
Example 1: To add a NewService class that implements *kamaki.clients.Client*: 
217

    
218
* create a new_service package and implement the unit tests in
219
    *kamaki.clients.new_service.test*:
220

    
221
.. code-block:: console
222

    
223
    $ mkdir new_service && touch new_service/test.py
224

    
225
* create the package file for the package implementation:
226

    
227
.. code-block:: console
228

    
229
    $ touch new_service/__init__.py
230

    
231
* Create the test class and methods in *kamaki.clients.new_service.test*
232

    
233
.. code-block:: python
234

    
235
    # file: ${kamaki}/kamaki/clients/new_service/test.py
236
    from unittest import TestCase
237

    
238
    class NewService(TestCase):
239

    
240
        def test_method1(self):
241
            ...
242

    
243
* Create the NewService and its actual functionality in
244
    kamaki.clients.new_service
245

    
246
.. code-block:: python
247

    
248
    # file: ${kamaki}/kamaki/clients/new_service/__init__.py
249
    from kamaki.clients import Client
250

    
251
    class NewService(Client):
252

    
253
        def method1(self, ...):
254
            ...
255

    
256
* Import the test class to *kamaki.clients.test*:
257

    
258
.. code-block:: python
259

    
260
    # file: ${kamaki}/kamaki/clients/test.py
261
    from kamaki.clients.new_service.test import NewService
262

    
263
.. note:: If the new class or module is part of an existing sub-package, it is
264
    acceptable to append its testing class in the existing test.py file of the
265
    sub-package it belongs to. For example, the
266
    kamaki.clients.pithos.PithosClient and
267
    kamaki.clients.pithos.rest_api.PithosRestClient classes are tested by two
268
    different classes (PithosClient and PithosRestClient respectively) in the
269
    same module (kamaki.clients.pithos.test).
270