Fix deprecated terms in documentation
[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
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