Skip to content

Commit 97941d7

Browse files
committed
docs (Tornado): provide more docs, comments
1 parent 2c90e84 commit 97941d7

File tree

3 files changed

+390
-373
lines changed

3 files changed

+390
-373
lines changed

src/DIRAC/Core/Tornado/Server/TornadoREST.py

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,10 @@
33
It directly inherits from :py:class:`tornado.web.RequestHandler`
44
"""
55

6-
from __future__ import absolute_import
7-
from __future__ import division
8-
from __future__ import print_function
9-
10-
__RCSID__ = "$Id$"
11-
126
import inspect
137
from urllib.parse import unquote
148

15-
from DIRAC import gLogger, S_OK
9+
from DIRAC import gLogger
1610
from DIRAC.ConfigurationSystem.Client import PathFinder
1711
from DIRAC.Core.Tornado.Server.private.BaseRequestHandler import BaseRequestHandler
1812

@@ -21,19 +15,19 @@
2115

2216
class TornadoREST(BaseRequestHandler): # pylint: disable=abstract-method
2317
"""Base class for all the endpoints handlers.
24-
It directly inherits from :py:class:`DIRAC.Core.Tornado.Server.BaseRequestHandler.BaseRequestHandler`
25-
26-
Each HTTP request is served by a new instance of this class.
2718
2819
### Example
29-
In order to create a handler for your service, it has to follow a certain skeleton:
20+
In order to create a handler for your service, it has to follow a certain skeleton.
3021
3122
.. code-block:: python
3223
3324
from DIRAC.Core.Tornado.Server.TornadoREST import TornadoREST
3425
class yourEndpointHandler(TornadoREST):
3526
36-
SYSTEM = "Configuration"
27+
# To use only the HTTP POST method
28+
SUPPORTED_METHODS = ["POST"]
29+
30+
# Base URL
3731
LOCATION = "/registry"
3832
3933
@classmethod
@@ -61,41 +55,39 @@ def web_users(self, count: int = 0):
6155
'''
6256
return Registry.getAllUsers()[:count]
6357
58+
This class can read the method annotation to understand what type of argument expects to get the method,
59+
see :py:meth:`_getMethodArgs`.
60+
6461
Note that because we inherit from :py:class:`tornado.web.RequestHandler`
6562
and we are running using executors, the methods you export cannot write
66-
back directly to the client. Please see inline comments for more details.
63+
back directly to the client. Please see inline comments in
64+
:py:class:`BaseRequestHandler <DIRAC.Core.Tornado.Server.private.BaseRequestHandler.BaseRequestHandler>` for more details.
6765
68-
In order to pass information around and keep some states, we use instance attributes.
69-
These are initialized in the :py:meth:`.initialize` method.
70-
71-
The handler define the ``post`` and ``get`` verbs. Please refer to :py:meth:`.post` for the details.
7266
"""
7367

68+
# By default we enable all authorization grants, see DIRAC.Core.Tornado.Server.private.BaseRequestHandler for details
7469
USE_AUTHZ_GRANTS = ["SSL", "JWT", "VISITOR"]
7570
METHOD_PREFIX = "web_"
7671
LOCATION = "/"
7772

7873
@classmethod
79-
def _getServiceName(cls, request):
80-
"""Define endpoint full name
81-
82-
:param object request: tornado Request
74+
def _getComponentInfoDict(cls, fullComponentName: str, fullURL: str) -> dict:
75+
"""Fills the dictionary with information about the current component,
8376
84-
:return: str
77+
:param fullComponentName: full component name, see :py:meth:`_getFullComponentName`
78+
:param fullURL: incoming request path
8579
"""
86-
if not cls.SYSTEM:
87-
raise Exception("System name must be defined.")
88-
return "/".join([cls.SYSTEM, cls.__name__])
80+
return {}
8981

9082
@classmethod
91-
def _getServiceAuthSection(cls, endpointName):
83+
def _getCSAuthorizarionSection(cls, apiName):
9284
"""Search endpoint auth section.
9385
94-
:param str endpointName: endpoint name
86+
:param str apiName: API name, see :py:meth:`_getFullComponentName`
9587
9688
:return: str
9789
"""
98-
return "%s/Authorization" % PathFinder.getAPISection(endpointName)
90+
return "%s/Authorization" % PathFinder.getAPISection(apiName)
9991

10092
def _getMethodName(self):
10193
"""Parse method name. By default we read the first section in the path
@@ -104,16 +96,19 @@ def _getMethodName(self):
10496
10597
:return: str
10698
"""
99+
# Read target method name from request
107100
method = self.request.path.replace(self.LOCATION, "", 1).strip("/").split("/")[0]
101+
102+
# If handler has target method use it
108103
if method and hasattr(self, "".join([self.METHOD_PREFIX, method])):
109104
return method
110-
elif hasattr(self, "%sindex" % self.METHOD_PREFIX):
111-
gLogger.warn("%s method not implemented. Use the index method to handle this." % method)
105+
106+
# Otherwise, the request can be handled by the `<prefix>index` method
107+
if hasattr(self, "%sindex" % self.METHOD_PREFIX):
108+
self.log.warn(f"{method} method not implemented. Use the index method to handle this.")
112109
return "index"
113-
else:
114-
raise NotImplementedError(
115-
"%s method not implemented. You can use the index method to handle this." % method
116-
)
110+
111+
raise NotImplementedError(f"{method} method not implemented. You can use the index method to handle this.")
117112

118113
def _getMethodArgs(self, args):
119114
"""Search method arguments.
@@ -183,22 +178,3 @@ def _getMethodArgs(self, args):
183178
keywordArguments[name] = value
184179

185180
return (positionalArguments, keywordArguments)
186-
187-
auth_echo = ["all"]
188-
189-
@staticmethod
190-
def web_echo(data):
191-
"""
192-
This method used for testing the performance of a service
193-
"""
194-
return S_OK(data)
195-
196-
auth_whoami = ["authenticated"]
197-
198-
def web_whoami(self):
199-
"""A simple whoami, returns all credential dictionary, except certificate chain object."""
200-
credDict = self.srv_getRemoteCredentials()
201-
if "x509Chain" in credDict:
202-
# Not serializable
203-
del credDict["x509Chain"]
204-
return S_OK(credDict)

src/DIRAC/Core/Tornado/Server/TornadoService.py

Lines changed: 51 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -27,64 +27,59 @@
2727
class TornadoService(BaseRequestHandler): # pylint: disable=abstract-method
2828
"""
2929
Base class for all the sevices handlers.
30-
It directly inherits from :py:class:`DIRAC.Core.Tornado.Server.BaseRequestHandler.BaseRequestHandler`
3130
32-
Each HTTP request is served by a new instance of this class.
31+
For compatibility with the existing :py:class:`DIRAC.Core.DISET.TransferClient.TransferClient`,
32+
the handler can define a method ``export_streamToClient``. This is the method that will be called
33+
whenever ``TransferClient.receiveFile`` is called. It is the equivalent of the DISET ``transfer_toClient``.
34+
Note that this is here only for compatibility, and we discourage using it for new purposes, as it is
35+
bound to disappear.
3336
34-
In order to create a handler for your service, it has to
35-
follow a certain skeleton::
37+
In order to create a handler for your service, it has to follow a certain skeleton.
3638
37-
from DIRAC.Core.Tornado.Server.TornadoService import TornadoService
38-
class yourServiceHandler(TornadoService):
39+
.. code-block:: python
3940
40-
# Called only once when the first
41-
# request for this handler arrives
42-
# Useful for initializing DB or so.
43-
# You don't need to use super or to call any parents method, it's managed by the server
44-
@classmethod
45-
def initializeHandler(cls, infosDict):
46-
'''Called only once when the first
47-
request for this handler arrives
48-
Useful for initializing DB or so.
49-
You don't need to use super or to call any parents method, it's managed by the server
50-
'''
51-
pass
5241
42+
from DIRAC.Core.Tornado.Server.TornadoService import TornadoService
43+
class yourServiceHandler(TornadoService):
5344
54-
def initializeRequest(self):
55-
'''
56-
Called at the beginning of each request
57-
'''
58-
pass
45+
@classmethod
46+
def initializeHandler(cls, infosDict):
47+
'''Called only once when the first request for this handler arrives.
48+
Useful for initializing DB or so.
49+
You don't need to use super or to call any parents method, it's managed by the server
50+
'''
51+
pass
5952
60-
# Specify the default permission for the method
61-
# See :py:class:`DIRAC.Core.DISET.AuthManager.AuthManager`
62-
auth_someMethod = ['authenticated']
53+
def initializeRequest(self):
54+
'''Called at the beginning of each request
55+
'''
56+
pass
6357
58+
# Specify the default permission for the method
59+
# See :py:class:`DIRAC.Core.DISET.AuthManager.AuthManager`
60+
auth_someMethod = ['authenticated']
6461
65-
def export_someMethod(self):
66-
'''The method you want to export.
67-
It must start with ``export_``
68-
and it must return an S_OK/S_ERROR structure
69-
'''
70-
return S_ERROR()
62+
def export_someMethod(self):
63+
'''The method you want to export. It must start with ``export_``
64+
and it must return an S_OK/S_ERROR structure
65+
'''
66+
return S_ERROR()
7167
68+
def export_streamToClient(self, myDataToSend, token):
69+
''' Automatically called when ``Transfer.receiveFile`` is called.
70+
Contrary to the other ``export_`` methods, it does not need to return a DIRAC structure.
71+
'''
7272
73-
def export_streamToClient(self, myDataToSend, token):
74-
''' Automatically called when ``Transfer.receiveFile`` is called.
75-
Contrary to the other ``export_`` methods, it does not need
76-
to return a DIRAC structure.
77-
'''
73+
# Do whatever with the token
7874
79-
# Do whatever with the token
80-
81-
with open(myFileToSend, 'r') as fd:
82-
return fd.read()
75+
with open(myFileToSend, 'r') as fd:
76+
return fd.read()
8377
8478
8579
Note that because we inherit from :py:class:`tornado.web.RequestHandler`
8680
and we are running using executors, the methods you export cannot write
87-
back directly to the client. Please see inline comments for more details.
81+
back directly to the client. Please see inline comments in
82+
:py:class:`BaseRequestHandler <DIRAC.Core.Tornado.Server.private.BaseRequestHandler.BaseRequestHandler>` for more details.
8883
8984
In order to pass information around and keep some states, we use instance attributes.
9085
These are initialized in the :py:meth:`.initialize` method.
@@ -132,41 +127,34 @@ def export_streamToClient(self, myDataToSend, token):
132127
133128
"""
134129

135-
# To access DIRAC services, RPC requests are used only through the HTTP POST method
136-
SUPPORTED_METHODS = ["POST"]
137-
138130
@classmethod
139-
def _getServiceName(cls, request):
140-
"""Search service name in request.
141-
142-
:param object request: tornado Request
143-
144-
:return: str
145-
"""
131+
def _initializeHandler(cls):
132+
"""Pre-initialization"""
146133
# Expected path: ``/<System>/<Component>``
147-
return request.path[1:]
134+
cls._serviceName = cls._fullComponentName
148135

149136
@classmethod
150-
def _getServiceInfo(cls, serviceName, request):
137+
def _getComponentInfoDict(cls, serviceName, fullURL):
151138
"""Fill service information.
152139
153-
:param str serviceName: service name
154-
:param object request: tornado Request
155-
140+
:param str serviceName: service name, see :py:meth:`_getFullComponentName`
141+
:param str fullURL: incoming request path
156142
:return: dict
157143
"""
158-
return {
144+
path = PathFinder.getServiceSection(serviceName)
145+
cls._serviceInfoDict = {
159146
"serviceName": serviceName,
160-
"serviceSectionPath": PathFinder.getServiceSection(serviceName),
161-
"csPaths": [PathFinder.getServiceSection(serviceName)],
162-
"URL": request.full_url(),
147+
"serviceSectionPath": path,
148+
"csPaths": [path],
149+
"URL": fullURL,
163150
}
151+
return cls._serviceInfoDict
164152

165153
@classmethod
166-
def _getServiceAuthSection(cls, serviceName):
154+
def _getCSAuthorizarionSection(cls, serviceName):
167155
"""Search service auth section.
168156
169-
:param str serviceName: service name
157+
:param str serviceName: service name, see :py:meth:`_getFullComponentName`
170158
171159
:return: str
172160
"""

0 commit comments

Comments
 (0)