Switch to unified view

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.)