Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.9 kB)

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

    
4
By default, kamaki clients are REST clients (they manage HTTP requests and
5
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
    class MyNewClient(Client):
21
        """MyNewClient Description Here"""
22

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

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

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

    
40
.. code-block:: python
41

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

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

    
53
External applications must instantiate a MyNewClient object.
54

    
55
.. code-block:: python
56

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

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

    
68
Concurrency control
69
-------------------
70

    
71
Kamaki clients may handle multiple requests at once, using threads. In that
72
case, users might implement their own thread handling mechanism, use an
73
external solution or take advantage of the mechanism featured in kamaki.clients
74

    
75
.. code-block:: python
76

    
77
    from threading import enumerate
78
    from kamaki.clients import SilentEvent
79
    ...
80

    
81
    class MyNewClient(Client):
82
        ...
83

    
84
        def _single_threaded_method(self, **args):
85
            ...
86
            request code
87
            ...
88

    
89
        def multithread_method(self):
90
            thread_list = []
91
            self._init_thread_limit()
92
            while some_condition or thread_list:
93
                ...
94
                event = SilentEvent(self._single_threaded_method, **args)
95
                event.start()
96
                thread_list.append(event)
97
                thread_list = self._watch_thread_limit(thread_list)
98

    
99
Going agile
100
-----------
101

    
102
The kamaki.clients package contains a set of fine-grained unit-tests for all
103
its packages. 
104

    
105
.. note:: unit tests require the optional python-mock package, version 1.X or
106
    better
107

    
108
Using the tests
109
^^^^^^^^^^^^^^^
110

    
111
To run the tests, the kamaki source code has to be downloaded.
112

    
113
.. code-block:: console
114

    
115
    $ git clone https://code.grnet.gr/git/kamaki
116
    $ cd kamaki/kamaki/clients
117

    
118
In each package under kamaki.clients, there is a test module (test.py) where
119
the tests are implemented. To run all tests, run the test.py file from
120
kamaki.clients
121

    
122
.. code-block:: console
123

    
124
    $ python test.py
125

    
126
To test a specific class, add the class name as an argument. E.g. for the
127
Client class:
128

    
129
.. code-block:: console
130

    
131
    $ python test.py Client
132

    
133
To test a specific method in a class, apply an extra argument, e.g. for the
134
request method in the Client class:
135

    
136
.. code-block:: console
137

    
138
    $ python test.py Client request
139

    
140
Each package contains a test module (test.py) which is also runnable from the
141
command line. E.g. in the pithos package there is a test module which
142
contains, among others, the **download** sub-test:
143

    
144
.. code-block:: console
145

    
146
    $ cd pithos
147

    
148
    # Run all kamaki.clients.pithos tests
149
    $ python test.py
150

    
151
    # Run all kamaki.clients.pithos.PithoClient tests
152
    $ python test.py Pithos
153

    
154
    # Test kamaki.clients.pithos.PithosClient.download
155
    $ python test.py Pithos download
156

    
157
To fully test a specific package, run test.py from the package location. E.g.
158
to test everything in kamaki.clients.pithos package:
159

    
160
.. code-block:: console
161

    
162
    $ cd pithos
163
    $ python test.py
164

    
165
Mechanism
166
^^^^^^^^^
167

    
168
Each folder / package contains a test.py file, that represents the test module
169
of this package. All test modules contain a set of classes that extent the
170
TestCase class. They also contain a main method to run the tests.
171

    
172
By convention, testing classes are named as <Tested Class> where <Test Class>
173
is the name of the tested class or module. Methods not grouped in classes are
174
tested by classes named after their respective module.
175

    
176
For example, the kamaki.clients.pithos.PithosClient class is tested by the
177
kamaki.clients.pithos.test.PithosClient class, while the methods in
178
kamaki.clients.utils module are tested by the kamaki.clients.utils.test.Utils
179
testing class.
180

    
181
Adding unit tests
182
^^^^^^^^^^^^^^^^^
183

    
184
After modifying or extending kamaki.clients method, classes, modules or
185
packages, it is a good practice to also modify or extend the corresponding
186
unit tests. What's more, it is recommended to modify or implement the testing
187
of new behavior before implementing the behavior itself. The aim for
188
kamaki.clients package is an 1 to 1 mapping between methods and their tests.
189

    
190
Modifying an existing method
191
""""""""""""""""""""""""""""
192

    
193
In case of an existing method modification, the programmer has to modify the
194
corresponding test as well. By convention, the test method is located in the
195
test module under the same package, in a TestCase subclass that is named with a
196
name similar to the package or class that contains the tested method.
197

    
198
Example 1: to modify the kamaki.clients.utils.filter_in method, the programmer
199
has to also adjust the kamaki.clients.utils.test.Utils.test_filter_in method.
200

    
201
Example 2: to modify the kamaki.clients.pithos.PithosRestClient.object_get, the
202
programmer has to also adjust the
203
kamaki.clients.pithos.test.PithosRestClient.test_object_get method.
204

    
205
Adding a new method
206
"""""""""""""""""""
207

    
208
Programmers who want to implement a new method in an existing class, are
209
encouraged to implement the corresponding unit test first. In order to do that,
210
they should find the testing class that is mapped to the class or module they
211
need to extend.
212

    
213
Example 1: To add a **list_special** method to
214
kamaki.clients.astakos.AstakosClient, extend the
215
kamaki.clients.astakos.test.AstakosClient class, as shown bellow:
216

    
217
.. code-block:: python
218

    
219
    # file: ${kamaki}/kamaki/clients/astakos/__init__.py
220

    
221
    class AstakosClient(TestCase):
222
        ...
223
        def test_list_special(self):
224
            """Test the list_special method"""
225
            ...
226

    
227
Example 2: To add a **get_random_int** method in kamaki.clients.utils module,
228
extend the kamaki.clients.utils.test.Utils test class, as shown bellow:
229

    
230
.. code-block:: python
231

    
232
    # file: ${kamaki}/kamaki/clients/utils/__init__.py
233

    
234
    class Utils(TestCase):
235
        ...
236
        def test_get_random_int(self):
237
            """Test the get_random_int method"""
238
            ...
239

    
240
Implementing a new class or module
241
""""""""""""""""""""""""""""""""""
242

    
243
Each class or module needs a seperate test sub-module. By convention, each
244
class or module under the kamaki.clients should be located in a separate
245
directory.
246

    
247
Example 1: To add a NewService class that implements the kamaki.clients.Client
248
interface: 
249

    
250
* create a new_service package and implement the unit tests in the kamaki.clients.new_service.test module:
251

    
252
.. code-block:: console
253

    
254
    $ mkdir new_service && touch new_service/test.py
255

    
256
* create the file that will hold the package code and implement the module there:
257

    
258
.. code-block:: console
259

    
260
    $ touch new_service/__init__.py
261

    
262
* Create the test class and methods in kamaki.clients.new_service.test
263

    
264
.. code-block:: python
265

    
266
    # file: ${kamaki}/kamaki/clients/new_service/test.py
267
    from unittest import TestCase
268

    
269
    class NewService(TestCase):
270

    
271
        def test_method1(self):
272
            ...
273

    
274
* Create the NewService and its actual functionality in kamaki.clients.new_service
275

    
276
.. code-block:: python
277

    
278
    # file: ${kamaki}/kamaki/clients/new_service/__init__.py
279
    from kamaki.clients import Client
280

    
281
    class NewService(Client):
282

    
283
        def method1(self, ...):
284
            ...
285

    
286
* Expose the new tests to top test module, by importing the test class to kamaki.clients.test
287

    
288
..code-block:: python
289

    
290
    # file: ${kamaki}/kamaki/clients/test.py
291

    
292
    from kamaki.clients.new_service.test import NewService
293

    
294
.. note:: If the new class or module is part of an existing sub-package, it is
295
    acceptable to append its testing class in the existing test.py file of the
296
    sub-package it belongs to. For example, the
297
    kamaki.clients.pithos.PithosClient and
298
    kamaki.clients.pithos.rest_api.PithosRestClient classes are tested by two
299
    different classes (PithosClient and PithosRestClient respectively) in the
300
    same module (kamaki.clients.pithos.test).
301