Statistics
| Branch: | Tag: | Revision:

root / docs / developers / extending-clients-api.rst @ 16d7b9ff

History | View | Annotate | Download (8.2 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 at once, using threads. In that
73
case, users might implement their own thread handling mechanism, use an
74
external solution or take advantage of the mechanism featured in kamaki.clients
75

    
76
.. code-block:: python
77

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

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

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

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

    
100
Going agile
101
-----------
102

    
103
The kamaki.clients package contains a set of fine-grained unit-tests for all
104
APIs. 
105

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

    
109
Using the tests
110
^^^^^^^^^^^^^^^
111

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

    
114
.. code-block:: console
115

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

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

    
123
.. code-block:: console
124

    
125
    $ python test.py
126

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

    
130
.. code-block:: console
131

    
132
    $ python test.py Client
133

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

    
137
.. code-block:: console
138

    
139
    $ python test.py Client request
140

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

    
145
.. code-block:: console
146

    
147
    $ cd pithos
148

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

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

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

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

    
161
.. code-block:: console
162

    
163
    $ cd pithos
164
    $ python test.py
165

    
166
Mechanism
167
^^^^^^^^^
168

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

    
173
By convention, testing classes have the same name as the class they test.
174
Methods not grouped in classes are tested by classes named after their
175
respective module.
176

    
177
For example, the *kamaki.clients.pithos.PithosClient* class is tested by the
178
*kamaki.clients.pithos.test.PithosClient* class, while the methods in
179
*kamaki.clients.utils* module are tested by *kamaki.clients.utils.test.Utils*.
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 goal is to
188
preserve 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: to modify *kamaki.clients.pithos.PithosRestClient.object_get*, the
199
programmer has to also adjust the
200
*kamaki.clients.pithos.test.PithosRestClient.test.object_get* method.
201

    
202
Adding a new method
203
"""""""""""""""""""
204

    
205
To implement a new method in an existing class, developers are encouraged to
206
implement the corresponding unit test first. In order to do that, they should
207
find the testing class that is mapped to the class or module they working on.
208

    
209
Example: Adding **list_special** to *kamaki.clients.astakos.AstakosClient*,
210
requires changes to *kamaki.clients.astakos.test.AstakosClient* too, as shown
211
bellow:
212

    
213
.. code-block:: python
214

    
215
    # file: ${kamaki}/kamaki/clients/astakos/__init__.py
216

    
217
    class AstakosClient(TestCase):
218
        ...
219
        def test_list_special(self):
220
            """Test the list_special method"""
221
            ...
222

    
223
Implementing a new class or module
224
""""""""""""""""""""""""""""""""""
225

    
226
Each class or module needs a seperate test sub-module. By convention, each
227
class or module under *kamaki.clients*, should be located in a separate
228
directory.
229

    
230
Example 1: To add a NewService class that implements *kamaki.clients.Client*: 
231

    
232
* create a new_service package and implement the unit tests in
233
    *kamaki.clients.new_service.test*:
234

    
235
.. code-block:: console
236

    
237
    $ mkdir new_service && touch new_service/test.py
238

    
239
* create the file to code the package implementation:
240

    
241
.. code-block:: console
242

    
243
    $ touch new_service/__init__.py
244

    
245
* Create the test class and methods in *kamaki.clients.new_service.test*
246

    
247
.. code-block:: python
248

    
249
    # file: ${kamaki}/kamaki/clients/new_service/test.py
250
    from unittest import TestCase
251

    
252
    class NewService(TestCase):
253

    
254
        def test_method1(self):
255
            ...
256

    
257
* Create the NewService and its actual functionality in kamaki.clients.new_service
258

    
259
.. code-block:: python
260

    
261
    # file: ${kamaki}/kamaki/clients/new_service/__init__.py
262
    from kamaki.clients import Client
263

    
264
    class NewService(Client):
265

    
266
        def method1(self, ...):
267
            ...
268

    
269
* Import the test class to *kamaki.clients.test*:
270

    
271
.. code-block:: python
272

    
273
    # file: ${kamaki}/kamaki/clients/test.py
274
    from kamaki.clients.new_service.test import NewService
275

    
276
.. note:: If the new class or module is part of an existing sub-package, it is
277
    acceptable to append its testing class in the existing test.py file of the
278
    sub-package it belongs to. For example, the
279
    kamaki.clients.pithos.PithosClient and
280
    kamaki.clients.pithos.rest_api.PithosRestClient classes are tested by two
281
    different classes (PithosClient and PithosRestClient respectively) in the
282
    same module (kamaki.clients.pithos.test).
283