Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
4rthem committed Oct 21, 2024
1 parent c3c9806 commit 18e19b3
Show file tree
Hide file tree
Showing 8 changed files with 1,553 additions and 373 deletions.
2 changes: 1 addition & 1 deletion databox/api/src/Entity/Core/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ private function computePrivacyRoots(): array
{
$roots = [];
for ($i = WorkspaceItemPrivacyInterface::PRIVATE_IN_WORKSPACE; $i <= WorkspaceItemPrivacyInterface::PUBLIC; ++$i) {
$roots[$i] = $this->privacy === $i;
$roots[$i] = $this->privacy >= $i;
}

if (null !== $this->parent) {
Expand Down
3 changes: 3 additions & 0 deletions databox/api/tests/DataboxTestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ protected function createCollection(array $options = []): Collection
if ($options['public'] ?? false) {
$collection->setPrivacy(WorkspaceItemPrivacyInterface::PUBLIC);
}
if ($options['parent'] ?? false) {
$collection->setParent($options['parent']);
}

$em->persist($collection);
if (!($options['no_flush'] ?? false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

declare(strict_types=1);

namespace App\Tests;
namespace App\Tests\Search;

use Alchemy\AclBundle\Model\AccessControlEntryInterface;
use Alchemy\AclBundle\Security\PermissionInterface;
use Alchemy\AuthBundle\Tests\Client\KeycloakClientTestMock;
use App\Tests\Search\AbstractSearchTest;

class CollectionSearchTest extends AbstractSearchTest
{
Expand All @@ -16,6 +15,114 @@ private static function releaseIndex(): void
self::forceNewEntitiesToBeIndexed();
self::waitForESIndex('collection');
}
public function testSearchRootWithCollectionsInNonPublicWorkspaceAsAnonymousUser(): void
{
$A = $this->createCollection([
'title' => 'A',
]);
$this->createCollection([
'title' => 'B',
'parent' => $A,
'public'=> true,
]);
self::releaseIndex();

$response = $this->request(
null,
'GET',
'/collections',
);

$data = $this->getDataFromResponse($response, 200);
$this->assertCount(0, $data);
}

public function testSearchRootWithNonPublicCollectionsInPublicWorkspaceAsAnonymousUser(): void
{
$workspace = $this->createWorkspace([
'title' => 'Workspace',
'public' => true,
]);
$A = $this->createCollection([
'title' => 'A',
'workspace' => $workspace,
]);
$this->createCollection([
'title' => 'B',
'parent' => $A,
'workspace' => $workspace,
]);
self::releaseIndex();

$response = $this->request(
null,
'GET',
'/collections',
);

$data = $this->getDataFromResponse($response, 200);
$this->assertCount(0, $data);
}

public function testSearchRootWithOnePublicSubCollectionInPublicWorkspaceAsAnonymousUser(): void
{
$workspace = $this->createWorkspace([
'title' => 'Workspace',
'public' => true,
]);
$A = $this->createCollection([
'title' => 'A',
'workspace' => $workspace,
]);
$this->createCollection([
'title' => 'B',
'parent' => $A,
'public'=> true,
'workspace' => $workspace,
]);
self::releaseIndex();

$response = $this->request(
null,
'GET',
'/collections',
);

$data = $this->getDataFromResponse($response, 200);
$this->assertCount(1, $data);
$this->assertSame('B', $data[0]['title']);
}


public function testSearchRootWithTwoPublicSubCollectionsInPublicWorkspaceAsAnonymousUser(): void
{
$workspace = $this->createWorkspace([
'title' => 'Workspace',
'public' => true,
]);
$A = $this->createCollection([
'title' => 'A',
'public'=> true,
'workspace' => $workspace,
]);
$this->createCollection([
'title' => 'B',
'parent' => $A,
'public'=> true,
'workspace' => $workspace,
]);
self::releaseIndex();

$response = $this->request(
null,
'GET',
'/collections',
);

$data = $this->getDataFromResponse($response, 200);
$this->assertCount(1, $data);
$this->assertSame('A', $data[0]['title']);
}

public function testSearchPublicCollectionsInPrivateWorkspaceAsAnonymousUser(): void
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public function load(array $configs, ContainerBuilder $container): void
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

$loader->load('services.yaml');

$bundles = $container->getParameter('kernel.bundles');
if (isset($bundles['SentryBundle'])) {
$loader->load('sentry.yaml');
}
}

public function prepend(ContainerBuilder $container): void
Expand Down
124 changes: 124 additions & 0 deletions lib/php/messenger-bundle/Listener/SentryMessengerListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

namespace Alchemy\MessengerBundle\Listener;

use Sentry\Event;
use Sentry\EventHint;
use Sentry\ExceptionMechanism;
use Sentry\SentryBundle\EventListener\MessengerListener;
use Sentry\State\HubInterface;
use Sentry\State\Scope;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\Exception\WrappedExceptionsInterface;
use Symfony\Component\Messenger\Stamp\BusNameStamp;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

#[AsDecorator(decorates: MessengerListener::class)]
final readonly class SentryMessengerListener
{
public function __construct(
private SerializerInterface $serializer,
private HubInterface $hub,
private bool $captureSoftFails = true,
)
{
}

public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event): void
{
if (!$this->captureSoftFails && $event->willRetry()) {
return;
}

$this->hub->withScope(function (Scope $scope) use ($event): void {
$envelope = $event->getEnvelope();
$exception = $event->getThrowable();

$scope->setTag('messenger.receiver_name', $event->getReceiverName());
$scope->setTag('messenger.message_class', \get_class($envelope->getMessage()));

/** @var BusNameStamp|null $messageBusStamp */
$messageBusStamp = $envelope->last(BusNameStamp::class);

if (null !== $messageBusStamp) {
$scope->setTag('messenger.message_bus', $messageBusStamp->getBusName());
}

$scope->setExtras([
'Messenger Message' => get_debug_type($envelope->getMessage()),
'Messenger Payload' => $this->serializer->serialize(
$envelope->getMessage(),
JsonEncoder::FORMAT, [
JsonEncode::OPTIONS => JSON_PRETTY_PRINT
]
),
]);

$this->captureException($exception, $event->willRetry());
});

$this->flushClient();
}

/**
* This method is called for each handled message.
*
* @param WorkerMessageHandledEvent $event The event
*/
public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event): void
{
// Flush normally happens at shutdown... which only happens in the worker if it is run with a lifecycle limit
// such as --time=X or --limit=Y. Flush immediately in a background worker.
$this->flushClient();
}

/**
* Creates Sentry events from the given exception.
*
* Unpacks multiple exceptions wrapped in a HandlerFailedException and notifies
* Sentry of each individual exception.
*
* If the message will be retried the exceptions will be marked as handled
* in Sentry.
*/
private function captureException(\Throwable $exception, bool $willRetry): void
{
if ($exception instanceof WrappedExceptionsInterface) {
$exception = $exception->getWrappedExceptions();
} elseif ($exception instanceof HandlerFailedException && method_exists($exception, 'getNestedExceptions')) {
$exception = $exception->getNestedExceptions();
} elseif ($exception instanceof DelayedMessageHandlingException && method_exists($exception, 'getExceptions')) {
$exception = $exception->getExceptions();
}

if (\is_array($exception)) {
foreach ($exception as $nestedException) {
$this->captureException($nestedException, $willRetry);
}

return;
}

$hint = EventHint::fromArray([
'exception' => $exception,
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, $willRetry),
]);

$this->hub->captureEvent(Event::createEvent(), $hint);
}

private function flushClient(): void
{
$client = $this->hub->getClient();

if (null !== $client) {
$client->flush();
}
}
}
6 changes: 6 additions & 0 deletions lib/php/messenger-bundle/Resources/config/sentry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
_defaults:
autowire: true
autoconfigure: true

Alchemy\MessengerBundle\Listener\SentryMessengerListener: ~
1 change: 1 addition & 0 deletions lib/php/messenger-bundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"require": {
"php": "^8.3",
"symfony/framework-bundle": "^6.4",
"symfony/serializer": "^6.4",
"symfony/messenger": "^6.4",
"symfony/doctrine-messenger": "^6.4.7"
},
Expand Down
Loading

0 comments on commit 18e19b3

Please sign in to comment.