Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
- Organization support
- Hyvor\Internal\Laravel\LogFake removed
- TestEventDispatcher moved
- ::enable() is removed, and no longer needed. Test env automatically sets event_dispatcher to TestEventDispatcher
- BillingInterface license() and licenses() method signatures changed
- BillingFake $license argument removed. license() calls licenses() now. Setting licenses for all called organizations is required now
- Comms API
- a shared COMMS_KEY env variable is required for component-to-componet communication
- ResourceInterface and resource registering in the core is removed
Expand All @@ -16,6 +18,9 @@
- AuthMethod removed. Deployment added.
- CommsInterface instead of using Comms directly
- UsageAbstract removed
- AuthMiddleware removed in Laravel
- Laravel built-in auth routes removed (/api/auth/check, /api/auth/login, etc.)
- assertFailed() on ApiTestingTrait is deprecated

## 3.1.8 - 2025-11-03

Expand Down
2 changes: 2 additions & 0 deletions bundle/config/services.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Hyvor\Internal\Bundle\Comms\Comms;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return function (ContainerConfigurator $container): void {
Expand Down Expand Up @@ -36,6 +37,7 @@

// Unreferenced service used in tests
$services->set(\Hyvor\Internal\Bundle\Mail\Component\Brand::class)->public();
$services->set(Comms::class)->public();
}

// $services->load('Hyvor\\Internal\\User\\', '../../src/User');
Expand Down
10 changes: 6 additions & 4 deletions bundle/src/Api/ApiExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ private function hideEnum(string $message): string
'/App\\\\[A-Za-z0-9_\\\\]+/',
function ($matches) {
$class = $matches[0];
// it should definitely be an enum
assert(enum_exists($class));
$values = array_column($class::cases(), 'value');
return implode('|', $values);
if (enum_exists($class)) {
$values = array_column($class::cases(), 'value');
return implode('|', $values);
} else {
return $class;
}
},
$message
);
Expand Down
5 changes: 3 additions & 2 deletions bundle/src/Api/SudoAuthorizationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public function __invoke(ControllerEvent $event): void
}

$request = $event->getRequest();
$user = $this->auth->check($request);
$me = $this->auth->me($request);

if ($user === false) {
if (!$me) {
throw new DataCarryingHttpException(
403,
[
Expand All @@ -48,6 +48,7 @@ public function __invoke(ControllerEvent $event): void
);
}

$user = $me->getUser();
$sudoUser = $this->sudoUserService->get($user->id);

if ($sudoUser === null) {
Expand Down
14 changes: 7 additions & 7 deletions bundle/src/BillingFakeLicenseProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Hyvor\Internal\Bundle;

use Hyvor\Internal\Billing\Dto\LicenseOf;
use Hyvor\Internal\Billing\Dto\LicensesCollection;
use Hyvor\Internal\Billing\License\License;
use Hyvor\Internal\Billing\License\Resolved\ResolvedLicense;
use Hyvor\Internal\Component\Component;
use Hyvor\Internal\InternalFake;

Expand All @@ -21,17 +20,18 @@ public function __construct(string $internalFakeClass)
$this->internalFake = new $internalFakeClass();
}

public function license(int $userId, ?int $blogId, Component $component): ?License
public function license(int $userId, Component $component): ?License
{
return $this->internalFake->license($userId, $blogId, $component);
return $this->internalFake->license($userId, $component);
}

/**
* @param LicenseOf[] $of
* @param int[] $organizationIds
* @return array<int, ResolvedLicense>
*/
public function licenses(array $of, Component $component): LicensesCollection
public function licenses(array $organizationIds, Component $component): array
{
return $this->internalFake->licenses($of, $component);
return $this->internalFake->licenses($organizationIds, $component);
}

}
5 changes: 3 additions & 2 deletions bundle/src/Command/Sudo/SudoAddCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function __invoke(
if (count($usersWithThatEmail) === 1) {
$user = $usersWithThatEmail[0];
} else {
/** @var array<\Stringable> $answers */
$answers = array_map(fn($u) => $this->userRow($u), $usersWithThatEmail);
$helper = new QuestionHelper();
$question = new ChoiceQuestion(
Expand Down Expand Up @@ -84,7 +85,7 @@ public function __invoke(

private function userRow(AuthUser $user): object
{
return new readonly class($user) {
return new readonly class($user) implements \Stringable {

public function __construct(public AuthUser $user)
{
Expand All @@ -107,4 +108,4 @@ public function __toString(): string
};
}

}
}
2 changes: 1 addition & 1 deletion bundle/src/Comms/Comms.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function sendAsync(AbstractEvent $event, ?Component $to = null, string $t
]);
}

public function validateAndGetTo(AbstractEvent $event, ?Component $to): Component
protected function validateAndGetTo(AbstractEvent $event, ?Component $to): Component
{
$allowedFrom = $event->from();
$allowedTo = $event->to();
Expand Down
13 changes: 3 additions & 10 deletions bundle/src/Comms/MockComms.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
use Hyvor\Internal\Component\Component;
use PHPUnit\Framework\Assert;

class MockComms implements CommsInterface {

public function __construct(private Comms $comms) {}
class MockComms extends Comms {

/**
* @var array{event: AbstractEvent, to: Component, async: bool}[]
Expand All @@ -20,14 +18,9 @@ public function __construct(private Comms $comms) {}
*/
private array $responses = [];

public function signature(string $content): string
{
throw new \RuntimeException('not implemented');
}

public function send(AbstractEvent $event, ?Component $to = null,): object|null
{
$to = $this->comms->validateAndGetTo($event, $to);
$to = $this->validateAndGetTo($event, $to);

$this->sent[] = [
'event' => $event,
Expand All @@ -47,7 +40,7 @@ public function send(AbstractEvent $event, ?Component $to = null,): object|null

public function sendAsync(AbstractEvent $event, ?Component $to = null, string $transport = 'async'): void
{
$to = $this->comms->validateAndGetTo($event, $to);
$to = $this->validateAndGetTo($event, $to);

$this->sent[] = [
'event' => $event,
Expand Down
1 change: 1 addition & 0 deletions bundle/src/Controller/OidcController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Hyvor\Internal\Auth\Oidc\OidcConfig;
use Hyvor\Internal\Auth\Oidc\OidcApiService;
use Hyvor\Internal\Auth\Oidc\OidcUserService;
use Hyvor\Internal\Bundle\Comms\Comms;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
Expand Down
25 changes: 9 additions & 16 deletions bundle/src/EventDispatcher/TestEventDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ public function __construct(
parent::__construct();
}

/**
* @param false|string[] $mockEvents
*/
public function setMockEvents(false|array $mockEvents): void
{
$this->mockEvents = $mockEvents;
}

public function dispatch(object $event, ?string $eventName = null): object
{
$eventName ??= $event::class;
Expand Down Expand Up @@ -90,19 +98,4 @@ public function assertDispatchedCount(string $eventName, int $count): void
);
}

/**
* @param false|string[] $mockEvents false to not mock any events or an array of event names to mock.
* Listeners of those events will not be called when the event is dispatched.
* Useful to prevent side effects.
*/
public static function enable(Container $container, false|array $mockEvents = false): self
{
$dispatcher = new self($mockEvents);
$container->set(
EventDispatcherInterface::class,
$dispatcher
);
return $dispatcher;
}

}
}
20 changes: 8 additions & 12 deletions bundle/src/InternalBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
->set('internal.default_private_instance', null)
->set('internal.default_fake', false);

$services = $container->services();

// InternalConfig class
$container->services()
$services
->get(InternalConfig::class)
->args([
'%env(APP_SECRET)%',
Expand All @@ -86,29 +88,23 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
$config['i18n']['default'],
]);

$container
->services()
$services
->set(AuthInterface::class)
->factory([service(AuthFactory::class), 'create']);

$container
->services()
$services
->set(BillingInterface::class)
->public() // because this is not used from outside, so tests fail (inlined)
->factory([service(BillingFactory::class), 'create']);

if ($container->env() === 'test') {
$container
->services()
->alias(CommsInterface::class, MockComms::class);
$services->alias(CommsInterface::class, MockComms::class);
} else {
$container
->services()
->alias(CommsInterface::class, Comms::class);
$services->alias(CommsInterface::class, Comms::class);
}

// other services
$container->services()->alias(SelfHostedTelemetryInterface::class, SelfHostedTelemetry::class);
$services->alias(SelfHostedTelemetryInterface::class, SelfHostedTelemetry::class);
}

}
27 changes: 22 additions & 5 deletions bundle/src/Testing/ApiTestingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Hyvor\Internal\Bundle\Testing;

use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\HttpFoundation\Response;

trait ApiTestingTrait
Expand All @@ -14,7 +15,9 @@ trait ApiTestingTrait
*/
public function getJson(): array
{
$response = self::getClient()->getResponse();
/** @var AbstractBrowser<object, \Symfony\Component\BrowserKit\Response> $client */
$client = self::getClient();
$response = $client->getResponse();
$content = $response->getContent();
$this->assertNotFalse($content);
$this->assertJson($content);
Expand All @@ -23,9 +26,19 @@ public function getJson(): array
return $json;
}

/**
* @deprecated use assertResponseFailed
*/
public function assertFailed(int $code, ?string $message = null): void
{
$response = self::getClient()->getResponse();
$this->assertResponseFailed($code, $message);
}

public function assertResponseFailed(int $code, ?string $message = null): void
{
/** @var AbstractBrowser<object, \Symfony\Component\BrowserKit\Response> $client */
$client = self::getClient();
$response = $client->getResponse();
$this->assertSame($code, $response->getStatusCode());

if ($message !== null) {
Expand All @@ -36,7 +49,9 @@ public function assertFailed(int $code, ?string $message = null): void

public function assertViolationCount(int $count): void
{
$response = self::getClient()->getResponse();
/** @var AbstractBrowser<object, \Symfony\Component\BrowserKit\Response> $client */
$client = self::getClient();
$response = $client->getResponse();
$this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode());

$response = $this->getJson();
Expand All @@ -48,7 +63,9 @@ public function assertViolationCount(int $count): void

public function assertHasViolation(string $property, string $message = ''): void
{
$response = self::getClient()->getResponse();
/** @var AbstractBrowser<object, \Symfony\Component\BrowserKit\Response> $client */
$client = self::getClient();
$response = $client->getResponse();
$this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode());

$response = $this->getJson();
Expand All @@ -70,4 +87,4 @@ public function assertHasViolation(string $property, string $message = ''): void
$this->assertTrue($found, 'Violation not found');
}

}
}
6 changes: 3 additions & 3 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ services:
- ./types:/app/types
- ./composer.json:/app/composer.json
- ./composer.lock:/app/composer.lock
- ./phpunit.xml:/app/phpunit.xml
- ./phpstan.neon:/app/phpstan.neon
- ./extension.neon:/app/extension.neon
- ./phpunit.dist.xml:/app/phpunit.dist.xml
- ./phpstan.dist.neon:/app/phpstan.dist.neon
- ./extension.neon:/app/extension.neon
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions src/Auth/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Hyvor\Internal\Auth;

use Hyvor\Internal\Auth\Dto\Me;
use Hyvor\Internal\Bundle\Comms\Comms;
use Hyvor\Internal\Bundle\Comms\CommsInterface;
use Hyvor\Internal\Bundle\Comms\Event\ToCore\User\GetMe;
use Hyvor\Internal\Bundle\Comms\Exception\CommsApiFailedException;
use Hyvor\Internal\Component\Component;
Expand All @@ -22,7 +22,7 @@ class Auth implements AuthInterface
public function __construct(
private InternalApi $internalApi,
private InstanceUrlResolver $instanceUrlResolver,
private Comms $comms,
private CommsInterface $comms,
) {
}

Expand Down
3 changes: 1 addition & 2 deletions src/Auth/AuthFake.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ public static function enableForSymfony(

public function me(string|Request $request): ?Me
{
return null; // TODO
return $this->user ?: false;
return $this->user ? new Me($this->user, null) : null;
}

public function authUrl(string $page, null|string|Request $redirect = null): string
Expand Down
Loading