Document the Upgrade notes for v0.9
[kamaki] / docs / developers / extending-clients-api.rst
1 Extending kamaki.clients
2 ========================
3
4 By default, kamaki clients are REST clients (they manage HTTP requests and responses to communicate with services).
5
6 How to build a client
7 ---------------------
8
9 All service clients consist of a subclass of the Client class and implement separate client functionalities as member methods. There is also an error class to raise exceptions that can be handled by kamaki interfaces.
10
11 .. code-block:: python
12     
13     #  ${KAMAKI_PATH}/kamaki/clients/mynewclient.py
14
15     from kamaki.clients import Client, ClientError
16
17     class MyNewClient(Client):
18         """MyNewClient Description Here"""
19
20         def my_first_method(self, **args):
21             """Method description"""
22             try:
23                 ...
24                 method code
25                 ...
26             except SomeKnownException as e1:
27                 raise ClientError('MyError: %s' % e1)
28             except SomeOtherException as e2:
29                 raise ClientError('MyError: %s' % e2)
30
31         def my_second_method(self, **params):
32             """Method description"""
33             ...
34
35 Custom clients can use a set of convenience methods for easy HTTP requests
36
37 .. code-block:: python
38
39     def get(self, path, **kwargs)
40     def head(self, path, **kwargs)
41     def post(self, path, **kwargs)
42     def put(self, path, **kwargs)
43     def delete(self, path, **kwargs)
44     def copy(self, path, **kwargs)
45     def move(self, path, **kwargs)
46
47 How to use your client
48 ----------------------
49
50 External applications must instantiate a MyNewClient object.
51
52 .. code-block:: python
53
54     from kamaki.clients import ClientError
55     from kamaki.clients.mynewclient import MyNewClient
56
57     ...
58     try:
59         cl = MyNewClient(args)
60         cl.my_first_method(other_args)
61     except ClientError as cle:
62         print('Client Error: %s' % cle)
63     ...
64
65 Concurrency control
66 -------------------
67
68 Kamaki clients may handle multiple requests at once, using threads. In that case, users might implement their own thread handling mechanism, use an external solution or take advantage of the mechanism featured in kamaki.clients
69
70 .. code-block:: python
71
72     from threading import enumerate
73     from kamaki.clients import SilentEvent
74     ...
75
76     class MyNewClient(Client):
77         ...
78
79         def _single_threaded_method(self, **args):
80             ...
81             request code
82             ...
83
84         def multithread_method(self):
85             thread_list = []
86             self._init_thread_limit()
87             while some_condition or thread_list:
88                 ...
89                 event = SilentEvent(self._single_threaded_method, **args)
90                 event.start()
91                 thread_list.append(event)
92                 thread_list = self._watch_thread_limit(thread_list)
93
94 Going agile
95 -----------
96
97 The kamaki.clients package contains a set of fine-grained unit-tests for all its packages. 
98
99 .. note:: unit tests require the optional python-mock package, version 1.X or better
100
101 Using the tests
102 ^^^^^^^^^^^^^^^
103
104 To run the tests, the kamaki source code has to be downloaded.
105
106 .. code-block:: console
107
108     $ git clone https://code.grnet.gr/git/kamaki
109     $ cd kamaki/kamaki/clients
110
111 In each package under kamaki.clients, there is a test module (test.py) where the tests are implemented. To run all tests, run the test.py file from kamaki.clients
112
113 .. code-block:: console
114
115     $ python test.py
116
117 To test a specific class, add the class name as an argument. E.g. for the Client class:
118
119 .. code-block:: console
120
121     $ python test.py Client
122
123 To test a specific method in a class, apply an extra argument, e.g. for the request method in the Client class:
124
125 .. code-block:: console
126
127     $ python test.py Client request
128
129 Each package contains a test module (test.py) which is also runnable from the command line. E.g. in the pithos package there is a test module which 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. to test everything in kamaki.clients.pithos package:
145
146 .. code-block:: console
147
148     $ cd pithos
149     $ python test.py
150
151 Mechanism
152 ^^^^^^^^^
153
154 Each folder / package contains a test.py file, that represents the test module of this package. All test modules contain a set of classes that extent the TestCase class. They also contain a main method to run the tests.
155
156 By convention, testing classes are named as <Tested Class> where <Test Class> is the name of the tested class or module. Methods not grouped in classes are tested by classes named after their respective module.
157
158 For example, the kamaki.clients.pithos.PithosClient class is tested by the kamaki.clients.pithos.test.PithosClient class, while the methods in kamaki.clients.utils module are tested by the kamaki.clients.utils.test.Utils testing class.
159
160 Adding unit tests
161 ^^^^^^^^^^^^^^^^^
162 After modifying or extending kamaki.clients method, classes, modules or packages, it is a good practice to also modify or extend the corresponding unit tests. What's more, it is recommended to modify or implement the testing of new behavior before implementing the behavior itself. The aim for kamaki.clients package is an 1 to 1 mapping between methods and their tests.
163
164 Modifying an existing method
165 """"""""""""""""""""""""""""
166
167 In case of an existing method modification, the programmer has to modify the corresponding test as well. By convention, the test method is located in the test module under the same package, in a TestCase subclass that is named with a name similar to the package or class that contains the tested method.
168
169 Example 1: to modify the kamaki.clients.utils.filter_in method, the programmer has to also adjust the kamaki.clients.utils.test.Utils.test_filter_in method.
170
171 Example 2: to modify the kamaki.clients.pithos.PithosRestClient.object_get, the programmer has to also adjust the kamaki.clients.pithos.test.PithosRestClient.test_object_get method.
172
173 Adding a new method
174 """""""""""""""""""
175
176 Programmers who want to implement a new method in an existing class, are encouraged to implement the corresponding unit test first. In order to do that, they should find the testing class that is mapped to the class or module they need to extend.
177
178 Example 1: To add a **list_special** method to kamaki.clients.astakos.AstakosClient, extend the kamaki.clients.astakos.test.AstakosClient class, as shown bellow:
179
180 .. code-block:: python
181
182     # file: ${kamaki}/kamaki/clients/astakos/__init__.py
183
184     class AstakosClient(TestCase):
185         ...
186         def test_list_special(self):
187             """Test the list_special method"""
188             ...
189
190 Example 2: To add a **get_random_int** method in kamaki.clients.utils module, extend the kamaki.clients.utils.test.Utils test class, as shown bellow:
191
192 .. code-block:: python
193
194     # file: ${kamaki}/kamaki/clients/utils/__init__.py
195
196     class Utils(TestCase):
197         ...
198         def test_get_random_int(self):
199             """Test the get_random_int method"""
200             ...
201
202 Implementing a new class or module
203 """"""""""""""""""""""""""""""""""
204
205 Each class or module needs a seperate test sub-module. By convention, each class or module under the kamaki.clients should be located in a separate directory.
206
207 Example 1: To add a NewService class that implements the kamaki.clients.Client interface: 
208
209 * create a new_service package and implement the unit tests in the kamaki.clients.new_service.test module:
210
211 .. code-block:: console
212
213     $ mkdir new_service && touch new_service/test.py
214
215 * create the file that will hold the package code and implement the module there:
216
217 .. code-block:: console
218
219     $ touch new_service/__init__.py
220
221 * Create the test class and methods in kamaki.clients.new_service.test
222
223 .. code-block:: python
224
225     # file: ${kamaki}/kamaki/clients/new_service/test.py
226     from unittest import TestCase
227
228     class NewService(TestCase):
229
230         def test_method1(self):
231             ...
232
233 * Create the NewService and its actual functionality in kamaki.clients.new_service
234
235 .. code-block:: python
236
237     # file: ${kamaki}/kamaki/clients/new_service/__init__.py
238     from kamaki.clients import Client
239
240     class NewService(Client):
241
242         def method1(self, ...):
243             ...
244
245 * Expose the new tests to top test module, by importing the test class to kamaki.clients.test
246
247 ..code-block:: python
248
249     # file: ${kamaki}/kamaki/clients/test.py
250
251     from kamaki.clients.new_service.test import NewService
252
253 .. note:: If the new class or module is part of an existing sub-package, it is acceptable to append its testing class in the existing test.py file of the sub-package it belongs to. For example, the kamaki.clients.pithos.PithosClient and kamaki.clients.pithos.rest_api.PithosRestClient classes are tested by two different classes (PithosClient and PithosRestClient respectively) in the same module (kamaki.clients.pithos.test).
254