From ddf67fce583154ab183949dfc29f3d09f3c72e4a Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 3 Jul 2012 16:46:27 -0700 Subject: [PATCH] Add an example event listener, improve documentation, and add a commit discovery event Summary: Improve documentation around Phabricator events. Test Plan: Generated and read documentation. Ran test script. Reviewers: btrahan, jungejason Reviewed By: jungejason CC: aran Maniphest Tasks: T1092 Differential Revision: https://secure.phabricator.com/D2917 --- scripts/util/emit_test_event.php | 57 ++++++ src/__phutil_library_map__.php | 4 + .../PhabricatorRepositoryPullLocalDaemon.php | 9 + src/docs/userguide/events.diviner | 182 +++++++++++++++++- .../events/PhabricatorEvent.php | 4 + .../events/PhabricatorEventEngine.php | 3 + .../PhabricatorExampleEventListener.php | 52 +++++ .../events/constant/PhabricatorEventType.php | 21 +- 8 files changed, 322 insertions(+), 10 deletions(-) create mode 100755 scripts/util/emit_test_event.php create mode 100644 src/infrastructure/events/PhabricatorExampleEventListener.php diff --git a/scripts/util/emit_test_event.php b/scripts/util/emit_test_event.php new file mode 100755 index 0000000000..35108e0d74 --- /dev/null +++ b/scripts/util/emit_test_event.php @@ -0,0 +1,57 @@ +#!/usr/bin/env php +setTagline('emit a test event'); +$args->setSynopsis(<<parseStandardArguments(); +$args->parse( + array( + array( + 'name' => 'listen', + 'param' => 'listener', + 'repeat' => true, + ), + )); + +$console = PhutilConsole::getConsole(); +foreach ($args->getArg('listen') as $listener) { + $console->writeOut("Installing '%s'...\n", $listener); + newv($listener, array())->register(); +} + + +$console->writeOut("Emitting event...\n"); + +PhutilEventEngine::dispatchEvent( + new PhabricatorEvent( + PhabricatorEventType::TYPE_TEST_DIDRUNTEST, + array( + 'time' => time(), + ))); + +$console->writeOut("Done.\n"); +exit(0); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ae8b69c4f6..cd786089ed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -632,6 +632,7 @@ phutil_register_library_map(array( 'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php', 'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php', 'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php', + 'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php', 'PhabricatorFeedBuilder' => 'applications/feed/builder/PhabricatorFeedBuilder.php', 'PhabricatorFeedConstants' => 'applications/feed/constants/PhabricatorFeedConstants.php', 'PhabricatorFeedController' => 'applications/feed/controller/PhabricatorFeedController.php', @@ -1647,9 +1648,12 @@ phutil_register_library_map(array( 'PhabricatorErrorExample' => 'PhabricatorUIExample', 'PhabricatorEvent' => 'PhutilEvent', 'PhabricatorEventType' => 'PhutilEventType', + 'PhabricatorExampleEventListener' => 'PhutilEventListener', 'PhabricatorFeedController' => 'PhabricatorController', 'PhabricatorFeedDAO' => 'PhabricatorLiskDAO', 'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController', + 'PhabricatorFeedQuery' => 'PhabricatorIDPagedPolicyQuery', + 'PhabricatorFeedStory' => 'PhabricatorPolicyInterface', 'PhabricatorFeedStoryAggregate' => 'PhabricatorFeedStory', 'PhabricatorFeedStoryAudit' => 'PhabricatorFeedStory', 'PhabricatorFeedStoryCommit' => 'PhabricatorFeedStory', diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php index 9f022cd54a..a25372ce93 100644 --- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -374,6 +374,15 @@ final class PhabricatorRepositoryPullLocalDaemon } $this->setCache($repository, $commit_identifier); + + PhutilEventEngine::dispatchEvent( + new PhabricatorEvent( + PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT, + array( + 'repository' => $repository, + 'commit' => $commit, + ))); + } catch (AphrontQueryDuplicateKeyException $ex) { $commit->killTransaction(); // Ignore. This can happen because we discover the same new commit diff --git a/src/docs/userguide/events.diviner b/src/docs/userguide/events.diviner index 8ac62a906a..64c11e51fc 100644 --- a/src/docs/userguide/events.diviner +++ b/src/docs/userguide/events.diviner @@ -9,12 +9,68 @@ Phabricator allows you to install custom runtime event listeners which can react to certain things happening (like a Maniphest Task being edited) and run custom code to perform logging, synchronize with other systems, or modify workflows. -NOTE: This feature is new and experimental, so few events are available and -things might not be completely stable. +These listeners are PHP classes which you install beside Phabricator, and which +Phabricator loads at runtime and runs in-process. They are the most direct and +powerful way to respond to events. + += Installing Event Listeners = + +To install event listeners, follow these steps: + + - Write a listener class which extends @{class:PhutilEventListener}. + - Add it to a libphutil library, or create a new library (for instructions, + see @{article:libphutil Libraries User Guide}. + - Configure Phabricator to load the library by adding it to `load-libraries` + in the Phabricator config. + - Configure Phabricator to install the event listener by adding the class + name to `events.listeners` in the Phabricator config. + +You can verify your listener is registered in the "Events" tab of DarkConsole. +It should appear at the top under "Registered Event Listeners". You can also +see any events the page emitted there. For details on DarkConsole, see +@{article:Using DarkConsole}. + += Example Listener = + +Phabricator includes an example event listener, +@{class:PhabricatorExampleEventListener}, which may be useful as a starting +point in developing your own listeners. This listener listens for a test +event that is emitted by the script `scripts/util/emit_test_event.php`. + +If you run this script normally, it should output something like this: + + $ ./scripts/util/emit_test_event.php + Emitting event... + Done. + +This is because there are no listeners for the event, so nothing reacts to it +when it is emitted. You can add the example listener by either adding it to +your `events.listeners` configuration or with the `--listen` command-line flag: + + $ ./scripts/util/emit_test_event.php --listen PhabricatorExampleEventListener + Installing 'PhabricatorExampleEventListener'... + Emitting event... + PhabricatorExampleEventListener got test event at 1341344566 + Done. + +This time, the listener was installed and had its callback invoked when the +test event was emitted. = Available Events = -== PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK == +You can find a list of all Phabricator events in @{class:PhabricatorEventType}. + +== All Events == + +The special constant `PhutilEventType::TYPE_ALL` will let you listen for all +events. Normally, you want to listen only to specific events, but if you're +writing a generic handler you can listen to all events with this constant +rather than by enumerating each event. + +== Maniphest: Will Edit Task == + +The constant for this event is +`PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK`. This event is dispatched before a task is edited, and allows you to respond to or alter the edit. Data available on this event: @@ -26,14 +82,41 @@ or alter the edit. Data available on this event: - ##mail## If this edit originates from email, the @{class:PhabricatorMetaMTAReceivedMail} object. -== PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL == +This is similar to the next event (did edit task) but occurs before the edit +begins. + +== Maniphest: Did Edit Task == + +The constant for this event is +`PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK`. + +This event is dispatched after a task is edited, and allows you to react to the +edit. Data available on this event: + + - ##task## The @{class:ManiphestTask} that was edited. + - ##transactions## The list of edits (objects of class + @{class:ManiphestTransaction}) that were applied. + - ##new## A boolean indicating if this task was newly created. + - ##mail## If this edit originates from email, the + @{class:PhabricatorMetaMTAReceivedMail} object. + +This is similar to the previous event (will edit task) but occurs after the +edit completes. + +== Differential: Will Send Mail == + +The constant for this event is +`PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL` This event is dispatched before Differential sends an email, and allows you to edit the message that will be sent. Data available on this event: - ##mail## The @{class:PhabricatorMetaMTAMail} being edited. -== PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED == +== Differential: Will Mark Generated == + +The constant for this event is +`PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED`. This event is dispatched before Differential decides if a file is generated (and doesn't need to be reviewed) or not. Data available on this event: @@ -41,3 +124,92 @@ doesn't need to be reviewed) or not. Data available on this event: - ##corpus## Body of the file. - ##is_generated## Boolean indicating if this file should be treated as generated. + +== Diffusion: Did Discover Commit == + +The constant for this event is +`PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT`. + +This event is dispatched when the daemons discover a commit for the first time. +This event happens very early in the pipeline, and not all commit information +will be available yet. Data available on this event: + + - `commit` The @{class:PhabricatorRepositoryCommit} that was discovered. + - `repository` The @{class:PhabricatorRepository} the commit was discovered + in. + +== Edge: Will Edit Edges == + +NOTE: Edge events are low-level events deep in the core. It is more difficult to +correct implement listeners for these events than for higher-level events. + +The constant for this event is +`PhabricatorEventType::TYPE_EDGE_WILLEDITEDGES`. + +This event is dispatched before @{class:PhabricatorEdgeEditor} makes an edge +edit. + + - `id` An identifier for this edit operation. + - `add` A list of dictionaries, each representing a new edge. + - `rem` A list of dictionaries, each representing a removed edge. + +This is similar to the next event (did edit edges) but occurs before the +edit begins. + +== Edge: Did Edit Edges == + +The constant for this event is +`PhabricatorEventType::TYPE_EDGE_DIDEDITEDGES`. + +This event is dispatched after @{class:PhabricatorEdgeEditor} makes an edge +edit, but before it commits the transactions. Data available on this event: + + - `id` An identifier for this edit operation. This is the same ID as + the one included in the corresponding "will edit edges" event. + - `add` A list of dictionaries, each representing a new edge. + - `rem` A list of dictionaries, each representing a removed edge. + +This is similar to the previous event (will edit edges) but occurs after the +edit completes. + +== Test: Did Run Test == + +The constant for this event is +`PhabricatorEventType::TYPE_TEST_DIDRUNTEST`. + +This is a test event for testing event listeners. See above for details. + += Debugging Listeners = + +If you're having problems with your listener, try these steps: + + - If you're getting an error about Phabricator being unable to find the + listener class, make sure you've added it to a libphutil library and + configured Phabricator to load the library with `load-libraries`. + - Make sure the listener is registered. It should appear in the "Events" tab + of DarkConsole. If it's not there, you may have forgotten to add it to + `events.listeners`. + - Make sure it calls `listen()` on the right events in its `register()` + method. If you don't listen for the events you're interested in, you + won't get a callback. + - Make sure the events you're listening for are actually happening. If they + occur on a normal page they should appear in the "Events" tab of + DarkConsole. If they occur on a POST, you could add a `phlog()` + to the source code near the event and check your error log to make sure the + code ran. + - You can check if your callback is getting invoked by adding `phlog()` with + a message and checking the error log. + - You can try listening to `PhutilEventType::TYPE_ALL` instead of a specific + event type to get all events, to narrow down whether problems are caused + by the types of events you're listening to. + - You can edit the `emit_test_event.php` script to emit other types of + events instead, to test that your listener reacts to them properly. You + might have to use fake data, but this gives you an easy way to test the + at least the basics. + += Next Steps = + +Continue by: + + - taking a look at @{class:PhabricatorExampleEventListener}; or + - building a library with @{article:@{article:libphutil Libraries User Guide}. \ No newline at end of file diff --git a/src/infrastructure/events/PhabricatorEvent.php b/src/infrastructure/events/PhabricatorEvent.php index 58b60936a8..bdb37faa20 100644 --- a/src/infrastructure/events/PhabricatorEvent.php +++ b/src/infrastructure/events/PhabricatorEvent.php @@ -16,6 +16,10 @@ * limitations under the License. */ + +/** + * @group events + */ final class PhabricatorEvent extends PhutilEvent { private $user; diff --git a/src/infrastructure/events/PhabricatorEventEngine.php b/src/infrastructure/events/PhabricatorEventEngine.php index 3d63cdd9be..c4d7729ed7 100644 --- a/src/infrastructure/events/PhabricatorEventEngine.php +++ b/src/infrastructure/events/PhabricatorEventEngine.php @@ -16,6 +16,9 @@ * limitations under the License. */ +/** + * @group events + */ final class PhabricatorEventEngine { public static function initialize() { diff --git a/src/infrastructure/events/PhabricatorExampleEventListener.php b/src/infrastructure/events/PhabricatorExampleEventListener.php new file mode 100644 index 0000000000..4bc1e0a53d --- /dev/null +++ b/src/infrastructure/events/PhabricatorExampleEventListener.php @@ -0,0 +1,52 @@ +listen(PhabricatorEventType::TYPE_TEST_DIDRUNTEST); + } + + public function handleEvent(PhutilEvent $event) { + // When an event you have called listen() for in your register() method + // occurs, this method will be invoked. You should respond to the event. + + // In this case, we just echo a message out so the event test script will + // do something visible. + + $console = PhutilConsole::getConsole(); + $console->writeOut( + "PhabricatorExampleEventListener got test event at %d\n", + $event->getValue('time')); + } + +} + + + + + diff --git a/src/infrastructure/events/constant/PhabricatorEventType.php b/src/infrastructure/events/constant/PhabricatorEventType.php index dd7ef4eda9..0aa5132f26 100644 --- a/src/infrastructure/events/constant/PhabricatorEventType.php +++ b/src/infrastructure/events/constant/PhabricatorEventType.php @@ -16,14 +16,25 @@ * limitations under the License. */ +/** + * For detailed explanations of these events, see + * @{article:Events User Guide: Installing Event Listeners}. + * + * @group events + */ final class PhabricatorEventType extends PhutilEventType { - const TYPE_MANIPHEST_WILLEDITTASK = 'maniphest.willEditTask'; - const TYPE_MANIPHEST_DIDEDITTASK = 'maniphest.didEditTask'; - const TYPE_DIFFERENTIAL_WILLSENDMAIL = 'differential.willSendMail'; + const TYPE_MANIPHEST_WILLEDITTASK = 'maniphest.willEditTask'; + const TYPE_MANIPHEST_DIDEDITTASK = 'maniphest.didEditTask'; + + const TYPE_DIFFERENTIAL_WILLSENDMAIL = 'differential.willSendMail'; const TYPE_DIFFERENTIAL_WILLMARKGENERATED = 'differential.willMarkGenerated'; - const TYPE_EDGE_WILLEDITEDGES = 'edge.willEditEdges'; - const TYPE_EDGE_DIDEDITEDGES = 'edge.didEditEdges'; + const TYPE_DIFFUSION_DIDDISCOVERCOMMIT = 'diffusion.didDiscoverCommit'; + + const TYPE_EDGE_WILLEDITEDGES = 'edge.willEditEdges'; + const TYPE_EDGE_DIDEDITEDGES = 'edge.didEditEdges'; + + const TYPE_TEST_DIDRUNTEST = 'test.didRunTest'; }