Skip to content

Commit

Permalink
feat: Allow listening to form submissions via events and webhooks
Browse files Browse the repository at this point in the history
Signed-off-by: Marcel Klehr <[email protected]>
  • Loading branch information
marcelklehr committed Sep 3, 2024
1 parent 6b0f960 commit 0005db6
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 8 deletions.
4 changes: 2 additions & 2 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ public function newSubmission(int $formId, array $answers, string $shareHash = '
$this->formsService->setLastUpdatedTimestamp($formId);

//Create Activity
$this->formsService->notifyNewSubmission($form, $submission->getUserId());
$this->formsService->notifyNewSubmission($form, $submission);

if ($form->getFileId() !== null) {
try {
Expand Down Expand Up @@ -2362,7 +2362,7 @@ public function insertSubmissionLegacy(int $formId, array $answers, string $shar
$this->formsService->setLastUpdatedTimestamp($formId);

//Create Activity
$this->formsService->notifyNewSubmission($form, $submission->getUserId());
$this->formsService->notifyNewSubmission($form, $submission);

Check warning on line 2365 in lib/Controller/ApiController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/ApiController.php#L2365

Added line #L2365 was not covered by tests

if ($form->getFileId() !== null) {
try {
Expand Down
46 changes: 46 additions & 0 deletions lib/Events/AbstractFormEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace OCA\Forms\Events;

use OCA\Forms\Db\Form;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IWebhookCompatibleEvent;

if (interface_exists(\OCP\EventDispatcher\IWebhookCompatibleEvent::class)) {
abstract class AbstractFormEvent extends Event implements IWebhookCompatibleEvent {
public function __construct(
protected Form $form,
) {
parent::__construct();
}

public function getForm(): Form {
return $this->form;

Check warning on line 18 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L18

Added line #L18 was not covered by tests
}

/**
* @inheritDoc
*/
public function getWebhookSerializable(): array {
return $this->form->read();

Check warning on line 25 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L25

Added line #L25 was not covered by tests
}
}

} else {
// need this block as long as NC < 30 is supported
abstract class AbstractFormEvent extends Event {
public function __construct(

Check warning on line 32 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L32

Added line #L32 was not covered by tests
protected Form $form,
) {
parent::__construct();

Check warning on line 35 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L35

Added line #L35 was not covered by tests
}

public function getForm(): Form {
return $this->form;

Check warning on line 39 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L38-L39

Added lines #L38 - L39 were not covered by tests
}

public function getWebhookSerializable(): array {
return $this->form->read();

Check warning on line 43 in lib/Events/AbstractFormEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/AbstractFormEvent.php#L42-L43

Added lines #L42 - L43 were not covered by tests
}
}
}
22 changes: 22 additions & 0 deletions lib/Events/FormSubmittedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace OCA\Forms\Events;

use OCA\Forms\Db\Form;
use OCA\Forms\Db\Submission;

class FormSubmittedEvent extends AbstractFormEvent {
public function __construct(
Form $form,
private Submission $submission,
) {
parent::__construct($form);
}

public function getWebhookSerializable(): array {
return [
'form' => $this->form->read(),
'submission' => $this->submission->read(),
];

Check warning on line 20 in lib/Events/FormSubmittedEvent.php

View check run for this annotation

Codecov / codecov/patch

lib/Events/FormSubmittedEvent.php#L16-L20

Added lines #L16 - L20 were not covered by tests
}
}
12 changes: 9 additions & 3 deletions lib/Service/FormsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@
use OCA\Forms\Db\QuestionMapper;
use OCA\Forms\Db\Share;
use OCA\Forms\Db\ShareMapper;
use OCA\Forms\Db\Submission;
use OCA\Forms\Db\SubmissionMapper;
use OCA\Forms\Events\FormSubmittedEvent;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\IMapperException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
Expand Down Expand Up @@ -72,6 +75,7 @@ public function __construct(
private IRootFolder $storage,
private IL10N $l10n,
private IMimeTypeDetector $mimeTypeDetector,
private IEventDispatcher $eventDispatcher,
) {
$this->currentUser = $userSession->getUser();
}
Expand Down Expand Up @@ -571,17 +575,19 @@ public function notifyNewShares(Form $form, Share $share): void {
* @param Form $form Related Form
* @param string $submitter The ID of the user who submitted the form. Can also be our 'anon-user-'-ID
*/
public function notifyNewSubmission(Form $form, string $submitter): void {
public function notifyNewSubmission(Form $form, Submission $submission): void {
$shares = $this->getShares($form->getId());
$this->activityManager->publishNewSubmission($form, $submitter);
$this->activityManager->publishNewSubmission($form, $submission->getUserId());

foreach ($shares as $share) {
if (!in_array(Constants::PERMISSION_RESULTS, $share['permissions'])) {
continue;
}

$this->activityManager->publishNewSharedSubmission($form, $share['shareType'], $share['shareWith'], $submitter);
$this->activityManager->publishNewSharedSubmission($form, $share['shareType'], $share['shareWith'], $submission->getUserId());
}

$this->eventDispatcher->dispatchTyped(new FormSubmittedEvent($form, $submission));
}

/**
Expand Down
3 changes: 1 addition & 2 deletions tests/Unit/Controller/ApiControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -846,8 +846,7 @@ public function testNewSubmission_answers() {
->with(1);

$this->formsService->expects($this->once())
->method('notifyNewSubmission')
->with($form, 'currentUser');
->method('notifyNewSubmission');

$this->formsService->expects($this->once())
->method('getFilePath')
Expand Down
15 changes: 14 additions & 1 deletion tests/Unit/Service/FormsServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ function microtime(bool|float $asFloat = false) {
use OCA\Forms\Db\QuestionMapper;
use OCA\Forms\Db\Share;
use OCA\Forms\Db\ShareMapper;
use OCA\Forms\Db\Submission;
use OCA\Forms\Db\SubmissionMapper;
use OCA\Forms\Service\CirclesService;
use OCA\Forms\Service\ConfigService;
use OCA\Forms\Service\FormsService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
Expand Down Expand Up @@ -182,6 +184,7 @@ public function setUp(): void {
$this->storage,
$this->l10n,
$this->mimeTypeDetector,
\OCP\Server::get(IEventDispatcher::class),
);
}

Expand Down Expand Up @@ -656,6 +659,7 @@ public function testGetPermissions_NotLoggedIn() {
$this->storage,
$this->l10n,
$this->mimeTypeDetector,
\OCP\Server::get(IEventDispatcher::class),
);

$form = new Form();
Expand Down Expand Up @@ -896,6 +900,7 @@ public function testPublicCanSubmit() {
$this->storage,
$this->l10n,
$this->mimeTypeDetector,
\OCP\Server::get(IEventDispatcher::class),
);

$this->assertEquals(true, $formsService->canSubmit($form));
Expand Down Expand Up @@ -1008,6 +1013,7 @@ public function testHasUserAccess_NotLoggedIn() {
$this->storage,
$this->l10n,
$this->mimeTypeDetector,
\OCP\Server::get(IEventDispatcher::class),
);

$form = new Form();
Expand Down Expand Up @@ -1213,10 +1219,14 @@ public function dataNotifyNewSubmission() {
public function testNotifyNewSubmission($shares, $shareNotifications) {
$owner = 'ownerUser';
$submitter = 'someUser';
$submission = new Submission();
$submission->setUserId($submitter);

$userSession = $this->createMock(IUserSession::class);
$userSession->method('getUser')->willReturn(null);

$eventDispatcher = $this->createMock(IEventDispatcher::class);

$formsService = $this->getMockBuilder(FormsService::class)
->onlyMethods(['getShares'])
->setConstructorArgs([
Expand All @@ -1236,6 +1246,7 @@ public function testNotifyNewSubmission($shares, $shareNotifications) {
$this->storage,
$this->l10n,
$this->mimeTypeDetector,
$eventDispatcher,
])
->getMock();

Expand All @@ -1250,7 +1261,9 @@ public function testNotifyNewSubmission($shares, $shareNotifications) {
$this->activityManager->expects($this->exactly($shareNotifications))
->method('publishNewSharedSubmission');

$formsService->notifyNewSubmission($form, $submitter);
$eventDispatcher->expects($this->exactly(1))->method('dispatchTyped')->withAnyParameters();

$formsService->notifyNewSubmission($form, $submission);
}

/**
Expand Down

0 comments on commit 0005db6

Please sign in to comment.