-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add support for webhook listeners #45475
Conversation
6af595d
to
3b790df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good! How will this be used? Can we assume that apps know their webhook listeners when the app is just registered and the system isn't fully booted yet?
Plan is to use that for integration of an external workflow engine, but architecture is not final yet.
Hum, what is not accessible at that point? |
That works. Anything that is provided by other apps might be missing. Say information from a user backend. If you always want to wire a specific event to webhook listeners it's fine. But the lack of this information could be critical when you need to conditionally register webhooks. I haven't yet understood the full picture of the new automations so this might not be necessary. https://docs.nextcloud.com/server/latest/developer_manual/app_development/bootstrap.html#nextcloud-20-and-later for my original ideas and thoughts about the two step register/boot process. |
Webhooks are moved to an application with registrations in DB instead. They will also need to support filtering of events triggerring the webhook.
|
|
7c37986
to
7898ea4
Compare
To test current version: <?php
function callNextcloudApi(string $endpoint, array $postfields): void {
$uri = 'http://nextcloud.local/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$uri/$endpoint");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postfields));
curl_setopt($ch, CURLOPT_HTTPHEADER,
[
'Accept:application/json',
'Content-Type:application/json',
'OCS-APIRequest: true',
'Authorization: Basic '. base64_encode('admin:admin')
]);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// Receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close($ch);
var_dump($server_output);
}
callNextcloudApi(
'/ocs/v2.php/apps/webhooks/api/v1/webhooks',
[
'httpMethod' => 'POST',
'uri' => 'http://127.0.0.1/hook.php',
'event' => 'OCP\\Files\\Events\\Node\\NodeWrittenEvent',
'eventFilter' => [
'user.uid' => 'admin',
],
'headers' => ['Header1' => 'value1'],
'authMethod' => 'header',
'authData' => ['Header2' => 'value2'],
]
); 2 - react to webhook calls in your hook.php: <?php
$filename = '/tmp/record.txt';
function callNextcloudApi($fp, string $endpoint, array $postfields): void {
try {
$uri = 'http://nextcloud.local/index.php/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$uri/$endpoint");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Basic '. base64_encode('admin:admin')]);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// Receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close($ch);
var_dump($server_output);
if (fwrite($fp, $server_output."\n") === false) {
die("Cannot write in ($filename)");
}
} catch (\Throwable $t) {
if (fwrite($fp, $t."\n") === false) {
die("Cannot write in ($filename)");
}
}
}
if (is_writable($filename)) {
if (!$fp = fopen($filename, 'a')) {
die("Cannot open ($filename)");
}
$event = json_decode(file_get_contents('php://input'), associative:true);
if (fwrite($fp, file_get_contents('php://input').' - '.json_encode($_POST)."\n") === false) {
die("Cannot write in ($filename)");
}
if (isset($event['event']['node']['id']) && isset($event['event']['node']['path']) && isset($event['user']['uid'])) {
callNextcloudApi(
$fp,
'/apps/tables/api/1/tables/2/rows',
[
'data' => json_encode(
[
5 => $event['event']['node']['id'],
6 => $event['event']['node']['path'],
7 => $event['user']['uid'].' - '.($_SERVER['HTTP_HEADER1'] ?? '').' - '.($_SERVER['HTTP_HEADER2'] ?? ''),
8 => time(),
]
)
]
);
}
fclose($fp);
} else {
die("Cannot open ($filename) - no permission");
} (this one needs a /tmp/records.txt writable file, and tables application with a table with id 2 and columns with id 5/6/7/8, adapt to your needs) $ occ webhooks:list
+----+-------+--------+------------+---------------------------+----------------------------------------+----------------------+----------------------+------------+----------+
| id | appId | userId | httpMethod | uri | event | eventFilter | headers | authMethod | authData |
+----+-------+--------+------------+---------------------------+----------------------------------------+----------------------+----------------------+------------+----------+
| 1 | | admin | POST | http://127.0.0.1/hook.php | OCP\Files\Events\Node\NodeWrittenEvent | {"user.uid":"admin"} | | none | |
| 2 | | admin | POST | http://127.0.0.1/hook.php | OCP\Files\Events\Node\NodeWrittenEvent | {"user.uid":"admin"} | {"Header1":"value1"} | header | |
+----+-------+--------+------------+---------------------------+----------------------------------------+----------------------+----------------------+------------+----------+ 4 - You can check scheduled jobs with
|
Follow-up:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works great! A few minor comments and questions.
I'm unsubscribing from this PR for now, please ping me if you need a review of the (Open)API. |
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Application class cannot use DI, and having the cache in the mapper allows for invalidating it when inserting or updating a webhook registration. Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
…thout auth data Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
This way importing a certificate with occ security:certificate:import will allow to use it for webhooks. Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
…n all classes Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
There is already a webhooks application in the appstore Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
c6630d8
to
bff7d3c
Compare
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
* | ||
* @since 30.0.0 | ||
*/ | ||
interface IWebhookCompatibleEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@come-nc Please document in https://docs.nextcloud.com/server/latest/developer_manual/app_publishing_maintenance/app_upgrade_guide/upgrade_to_30.html#id1
(all changes in lib/public/)
Summary
Add support for registering webhooks as listeners of events.
Checklist