|
a/Allura/docs/platform_tour.rst |
|
b/Allura/docs/platform_tour.rst |
|
... |
|
... |
8 |
robust and open platform. Some of the services provided by the platform include:
|
8 |
robust and open platform. Some of the services provided by the platform include:
|
9 |
|
9 |
|
10 |
- Indexing and search
|
10 |
- Indexing and search
|
11 |
- Authentication and Authorization
|
11 |
- Authentication and Authorization
|
12 |
- Email integration (every tool application gets its own email address)
|
12 |
- Email integration (every tool application gets its own email address)
|
13 |
Asynchronous processing via RabbitMQ
|
13 |
- Asynchronous processing with background tasks and events
|
14 |
- `Markdown <http://daringfireball.net/projects/markdown/>`_ markup formatting
|
14 |
- `Markdown <http://daringfireball.net/projects/markdown/>`_ markup formatting
|
15 |
- Simple autolinking between different artifacts in the forge
|
15 |
- Simple autolinking between different artifacts in the forge
|
16 |
- Attachment handling
|
16 |
- Attachment handling
|
17 |
- Tool administration
|
17 |
- Tool administration
|
18 |
|
18 |
|
|
... |
|
... |
146 |
|
146 |
|
147 |
Asynchronous Processing
|
147 |
Asynchronous Processing
|
148 |
-----------------------------------------
|
148 |
-----------------------------------------
|
149 |
|
149 |
|
150 |
Much of the actual functionality of Allura comes from code that runs
|
150 |
Much of the actual functionality of Allura comes from code that runs
|
151 |
*outside* the context of a web request, in the `reactor` server (invoked by
|
151 |
*outside* the context of a web request, in the `taskd` server (invoked by
|
152 |
running `paster reactor development.ini`. Asynchronous processing is performed
|
152 |
running `paster taskd development.ini`. Asynchronous processing is performed
|
153 |
by two types of functions, *auditors* and *reactors*, differentiated as follows:
|
153 |
by two types of functions, *tasks* and *events*, differentiated as follows:
|
154 |
|
154 |
|
155 |
Auditor
|
155 |
Task
|
156 |
Auditors listen to queues on the `audit` exchange.
|
156 |
Tasks are module-level global functions. They are annotated with the `@task`
|
157 |
Messages sent to an auditor queue are interpreted *imperatively* ("do this").
|
157 |
decorator and are invoked with the `.post` method. For instance, to schedule
|
158 |
Auditor-type messages should specify a project ID `project_id`, an
|
158 |
a task `foobar` to execute in the `taskd` context, you would write::
|
159 |
application mount point `mount_point`, and a user ID `user_id`, which will be
|
|
|
160 |
used by the platform to set the context before calling the registered
|
|
|
161 |
callback, and all of which reference the *recipient* of the message. An
|
|
|
162 |
auditor callback function is called *once* for each message received on its queue.
|
|
|
163 |
Reactor
|
|
|
164 |
Reactors listen to queues on the `react` exchange.
|
|
|
165 |
Messages sent to a reactor queue are interpreted in an *advisory* manner
|
|
|
166 |
("this was done"). Reactor-type messages should specify a project ID
|
|
|
167 |
`project_id` and a user ID `user_id`, which will be
|
|
|
168 |
used by the platform to set the context before calling the registered
|
|
|
169 |
callback, and all of which reference the *source* of the message. If the
|
|
|
170 |
reactor callback is an instance method, it will be called once for each
|
|
|
171 |
instance of the tool that exists for the given project for each message
|
|
|
172 |
received on its queue. If it is a class method, it will be called once for
|
|
|
173 |
each message received on its queue. For instance, the Tracker tool may be
|
|
|
174 |
configured to react to SCM commit messages in order to generate links between
|
|
|
175 |
SCM commits and Tracker tickets. *All tracker instances* in a project will
|
|
|
176 |
be notified of SCM commits in such a case.
|
|
|
177 |
|
159 |
|
178 |
In order to create a callback function for an auditor or a reactor, simply add a
|
160 |
@task
|
179 |
method to the tool application class that is decorated either with the `@audit`
|
161 |
def foobar(a,b,c=5): ...
|
180 |
or the `@react` decorator. For instance, the discussion tool defines a reactor on
|
|
|
181 |
the `Forum.new_post` message::
|
|
|
182 |
|
|
|
183 |
@react('Forum.new_post')
|
|
|
184 |
def notify_subscribers(self, routing_key, data):
|
|
|
185 |
....
|
|
|
186 |
|
|
|
187 |
If there are a large number of reactors, you can define them in a separate module
|
|
|
188 |
and use the `mixin_reactors()` method as in the SCM tool::
|
|
|
189 |
|
|
|
190 |
from .reactors import reactors
|
|
|
191 |
...
|
|
|
192 |
class ForgeGitApp(Application):
|
|
|
193 |
...
|
162 |
|
194 |
mixin_reactors(ForgeGitApp, reactors)
|
163 |
foobar.post(9,1,c=15)
|
195 |
|
164 |
|
196 |
.. sidebar:: Updating auditors and reactors
|
165 |
Event
|
|
|
166 |
Events are intended for "fan-out" types of events. Events have a string
|
|
|
167 |
name, and are "listened" for by using the `@event_handler` decorator. The
|
|
|
168 |
`g.post_event()` helper is provided to run the event handlers for a
|
|
|
169 |
particular event in the `taskd` context. Multiple event handlers can be
|
|
|
170 |
registered for each event::
|
197 |
|
171 |
|
198 |
If you add, remove, or change the routing key of any auditor or reactor,
|
172 |
@event_handler('event_name')
|
199 |
chances are that you'll need to re-configure the rabbitmq server to handle the
|
173 |
def handler1(topic, *args, **kwargs): ...
|
200 |
queue changes. To do this, you need simply to run the following command::
|
|
|
201 |
|
174 |
|
202 |
$ paster reactor_setup development.ini
|
175 |
@event_handler('event_name')
|
|
|
176 |
def handler2(topic, *args, **kwargs): ...
|
203 |
|
177 |
|
204 |
This will tear down all the queues and recreate them based on the code that
|
178 |
g.post_event('event_name', 1,2,3, a=5)
|
205 |
currently exists.
|
|
|
206 |
|
179 |
|
207 |
In order to actually *send* a message to either the `audit` or `react` exchange,
|
|
|
208 |
a helper method is provided in the pylons global object `g`:
|
|
|
209 |
|
|
|
210 |
.. method:: allura.lib.app_globals.AppGlobals.publish(xn, key, message=None, **kw)
|
|
|
211 |
:noindex:
|
|
|
212 |
|
|
|
213 |
Used to send messages to the named exchange. This method will automatically
|
|
|
214 |
set the message attributes `project_id`, `mount_point`, and `user_id` based on
|
|
|
215 |
the current context.
|
|
|
216 |
|
|
|
217 |
:param xn: exchange name (either "audit" or "react")
|
|
|
218 |
:param key: routing key (e.g. "Forum.new_post")
|
|
|
219 |
:param message: optional dictionary with message content
|
|
|
220 |
:param kw: optional keyword arguments which are passed through to the `carrot.Publisher`
|
|
|
221 |
|
180 |
|
222 |
Email Integration
|
181 |
Email Integration
|
223 |
-----------------------------------------
|
182 |
-----------------------------------------
|
224 |
|
183 |
|
225 |
The Allura platform provides easy-to-use email integration. Forge email addresses
|
184 |
The Allura platform provides easy-to-use email integration. Forge email addresses
|
|
... |
|
... |
229 |
the sending user is identified (if possible). Based on the parsed address, the
|
188 |
the sending user is identified (if possible). Based on the parsed address, the
|
230 |
pylons context attributes `c.project` and `c.app` are set, and the application is
|
189 |
pylons context attributes `c.project` and `c.app` are set, and the application is
|
231 |
queried to determine whether the identified user has authority to send an email
|
190 |
queried to determine whether the identified user has authority to send an email
|
232 |
to the given app/topic combination by calling `c.app.has_access(user, topic)`.
|
191 |
to the given app/topic combination by calling `c.app.has_access(user, topic)`.
|
233 |
If the user has access, the message is decomposed into its component parts (if a
|
192 |
If the user has access, the message is decomposed into its component parts (if a
|
234 |
multipart MIME-encoded message) and one `audit` message is generated for each
|
193 |
multipart MIME-encoded message) and `c.app.handle_message(topic, message)` is
|
235 |
part with the following fields:
|
194 |
called for each part with the following components to the `msg` dict:
|
236 |
|
195 |
|
237 |
headers
|
196 |
headers
|
238 |
The actual headers parsed from the body of the message
|
197 |
The actual headers parsed from the body of the message
|
239 |
message_id
|
198 |
message_id
|
240 |
The `Message-ID` header (which should be universally
|
199 |
The `Message-ID` header (which should be universally
|
|
... |
|
... |
251 |
Optional, if the part is an attachment with a filename, this will be populated
|
210 |
Optional, if the part is an attachment with a filename, this will be populated
|
252 |
content_type
|
211 |
content_type
|
253 |
The MIME content_type of the message part
|
212 |
The MIME content_type of the message part
|
254 |
payload
|
213 |
payload
|
255 |
The actual content of the message part
|
214 |
The actual content of the message part
|
256 |
user_id
|
|
|
257 |
The ID of the user who sent the message
|
|
|
258 |
|
|
|
259 |
Once the message is generated, it is sent to the `audit` exchange with the
|
|
|
260 |
routing key <Tool Type>.<topic>. For instance, a message to comment on a Wiki
|
|
|
261 |
page might have the routing key `Wiki.MainPage`.
|
|
|
262 |
|
215 |
|
263 |
The Allura platform also provides full support for *sending* email without
|
216 |
The Allura platform also provides full support for *sending* email without
|
264 |
worrying about the specifics of SMTP or sendmail handling. In order to send an
|
217 |
worrying about the specifics of SMTP or sendmail handling. In order to send an
|
265 |
email, a tool needs simply to send an `audit` message with the routing key
|
218 |
email, simply post a task for `allura.tasks.mail_tasks.sendmail` with the
|
266 |
`forgemail.send_email` and the following fields:
|
219 |
following arguments:
|
267 |
|
220 |
|
268 |
from
|
221 |
fromaddr
|
269 |
Return address on the message (usually the topic@tool_name that generated
|
222 |
Return address on the message (usually the topic@tool_name that generated
|
270 |
it)
|
223 |
it)
|
|
|
224 |
destinations
|
|
|
225 |
List of email addresses and/or :class:`bson.ObjectId` s for
|
|
|
226 |
:class:`allura.model.auth.User` objects
|
|
|
227 |
text
|
|
|
228 |
Markdown-formatted body of the message (If the user has requested html or
|
|
|
229 |
combined text+html messages in their preferences, the Markdown will be so
|
|
|
230 |
rendered. Otherwise a plain text message will be sent.)
|
|
|
231 |
reply_to
|
|
|
232 |
Address to which replies should be sent
|
271 |
subject
|
233 |
subject
|
272 |
Subject of the message
|
234 |
Subject of the message
|
273 |
message_id
|
235 |
message_id
|
274 |
Value to put in the `Message-ID` header (the `_id` field of a
|
236 |
Value to put in the `Message-ID` header (the `_id` field of a
|
275 |
:class:`allura.model.artifact.Message` is suitable for this)
|
237 |
:class:`allura.model.artifact.Message` is suitable for this)
|
276 |
in_reply_to (optional)
|
238 |
in_reply_to (optional)
|
277 |
Value to put in the `In-Reply-To` header (the `parent_id` field of a
|
239 |
Value to put in the `In-Reply-To` header (the `parent_id` field of a
|
278 |
:class:`allura.model.artifact.Message` is suitable for this)
|
240 |
:class:`allura.model.artifact.Message` is suitable for this)
|
279 |
destinations
|
|
|
280 |
List of email addresses and/or :class:`bson.ObjectId` s for
|
|
|
281 |
:class:`allura.model.auth.User` objects
|
|
|
282 |
text
|
|
|
283 |
Markdown-formatted body of the message (If the user has requested html or
|
|
|
284 |
combined text+html messages in their preferences, the Markdown will be so
|
|
|
285 |
rendered. Otherwise a plain text message will be sent.)
|
|
|