mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-20 01:53:11 +00:00
Silent mode for errors of unsupported event types #15
This commit is contained in:
+1
-2
@@ -134,7 +134,7 @@ class Api
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory();
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory($this->logger);
|
||||
$this->updateDispatcher = new UpdateDispatcher($this);
|
||||
}
|
||||
|
||||
@@ -685,7 +685,6 @@ class Api
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return $this->modelFactory->createMessage($response['message']);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,14 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->singleton(ModelFactory::class, function () {
|
||||
return new ModelFactory();
|
||||
$this->app->singleton(ModelFactory::class, function (Application $app) {
|
||||
/** @var Config $config */
|
||||
$config = $app->make(Config::class);
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new ModelFactory($logger);
|
||||
});
|
||||
|
||||
$this->app->singleton(Api::class, function (Application $app) {
|
||||
@@ -97,11 +103,15 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
);
|
||||
}
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new Api(
|
||||
$accessToken,
|
||||
$app->make(ClientApiInterface::class),
|
||||
$app->make(ModelFactory::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -114,19 +124,30 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
$config = $app->make(Config::class);
|
||||
$secret = $config->get('maxbot.webhook_secret');
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new WebhookHandler(
|
||||
$app->make(UpdateDispatcher::class),
|
||||
$app->make(ModelFactory::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
$secret,
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind(LongPollingHandler::class, function (Application $app) {
|
||||
/** @var Config $config */
|
||||
$config = $app->make(Config::class);
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new LongPollingHandler(
|
||||
$app->make(Api::class),
|
||||
$app->make(UpdateDispatcher::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+18
-2
@@ -67,13 +67,25 @@ use BushlanovDev\MaxMessengerBot\Models\Updates\UserRemovedFromChatUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\UploadEndpoint;
|
||||
use BushlanovDev\MaxMessengerBot\Models\VideoAttachmentDetails;
|
||||
use LogicException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Creates DTOs from raw associative arrays returned by the API client.
|
||||
*/
|
||||
class ModelFactory
|
||||
readonly class ModelFactory
|
||||
{
|
||||
private LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @param LoggerInterface|null $logger PSR LoggerInterface.
|
||||
*/
|
||||
public function __construct(?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple response to request.
|
||||
*
|
||||
@@ -341,7 +353,11 @@ class ModelFactory
|
||||
if (isset($data['updates']) && is_array($data['updates'])) {
|
||||
foreach ($data['updates'] as $updateData) {
|
||||
// Here we delegate the creation of a specific update to another factory method
|
||||
$updateObjects[] = $this->createUpdate($updateData);
|
||||
try {
|
||||
$updateObjects[] = $this->createUpdate($updateData);
|
||||
} catch (LogicException $e) {
|
||||
$this->logger->debug($e->getMessage(), ['payload' => $updateData, 'exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,12 @@ final readonly class WebhookHandler
|
||||
throw new SerializationException('Failed to decode webhook body as JSON.', 0, $e);
|
||||
}
|
||||
|
||||
$update = $this->modelFactory->createUpdate($data);
|
||||
|
||||
$this->dispatcher->dispatch($update);
|
||||
try {
|
||||
$update = $this->modelFactory->createUpdate($data);
|
||||
$this->dispatcher->dispatch($update);
|
||||
} catch (\LogicException $e) {
|
||||
$this->logger->debug($e->getMessage(), ['payload' => $payload, 'exception' => $e]);
|
||||
}
|
||||
|
||||
if (!headers_sent()) {
|
||||
http_response_code(200);
|
||||
|
||||
@@ -118,6 +118,7 @@ use RuntimeException;
|
||||
#[UsesClass(VideoAttachmentDetails::class)]
|
||||
#[UsesClass(VideoUrls::class)]
|
||||
#[UsesClass(UpdateDispatcher::class)]
|
||||
#[UsesClass(ModelFactory::class)]
|
||||
final class ApiTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
|
||||
@@ -38,6 +38,7 @@ use ReflectionClass;
|
||||
#[UsesClass(UpdateDispatcher::class)]
|
||||
#[UsesClass(MaxBotManager::class)]
|
||||
#[UsesClass(WebhookHandler::class)]
|
||||
#[UsesClass(ModelFactory::class)]
|
||||
final class MaxBotServiceProviderTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
@@ -212,6 +213,105 @@ final class MaxBotServiceProviderTest extends TestCase
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function modelFactoryIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var ModelFactory $factory */
|
||||
$factory = $this->app->make(ModelFactory::class);
|
||||
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($factory);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function modelFactoryIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var ModelFactory $factory */
|
||||
$factory = $this->app->make(ModelFactory::class);
|
||||
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($factory);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var WebhookHandler $handler */
|
||||
$handler = $this->app->make(WebhookHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var WebhookHandler $handler */
|
||||
$handler = $this->app->make(WebhookHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function longPollingHandlerIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var LongPollingHandler $handler */
|
||||
$handler = $this->app->make(LongPollingHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function longPollingHandlerIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var LongPollingHandler $handler */
|
||||
$handler = $this->app->make(LongPollingHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithSecretFromConfig(): void
|
||||
{
|
||||
@@ -240,10 +340,28 @@ final class MaxBotServiceProviderTest extends TestCase
|
||||
|
||||
$this->assertSame($this->app->make(ClientApiInterface::class), $clientProp->getValue($api));
|
||||
$this->assertSame($this->app->make(ModelFactory::class), $factoryProp->getValue($api));
|
||||
$this->assertSame($this->app->make(LoggerInterface::class), $loggerProp->getValue($api));
|
||||
$this->assertInstanceOf(NullLogger::class, $loggerProp->getValue($api));
|
||||
$this->assertSame($this->app->make(UpdateDispatcher::class), $dispatcherProp->getValue($api));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function apiIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var Api $api */
|
||||
$api = $this->app->make(Api::class);
|
||||
|
||||
$reflection = new ReflectionClass($api);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($api);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{0: string}>
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,7 @@ use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
#[CoversClass(ModelFactory::class)]
|
||||
#[UsesClass(BotInfo::class)]
|
||||
@@ -818,4 +819,55 @@ final class ModelFactoryTest extends TestCase
|
||||
|
||||
$assertionCallback($this, $attachment);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createUpdateListCatchesAndLogsLogicException(): void
|
||||
{
|
||||
$loggerMock = $this->createMock(LoggerInterface::class);
|
||||
$factory = $this->getMockBuilder(ModelFactory::class)
|
||||
->setConstructorArgs([$loggerMock])
|
||||
->onlyMethods(['createUpdate'])
|
||||
->getMock();
|
||||
|
||||
$validUpdateData = [
|
||||
'update_type' => 'bot_started',
|
||||
'timestamp' => 2,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 2,
|
||||
],
|
||||
'payload' => 'start_payload',
|
||||
'user_locale' => 'ru-RU',
|
||||
];
|
||||
$invalidUpdateData = ['update_type' => 'unknown_type'];
|
||||
$rawData = [
|
||||
'updates' => [$validUpdateData, $invalidUpdateData],
|
||||
'marker' => 123,
|
||||
];
|
||||
|
||||
$exception = new LogicException('Unknown or unsupported update type received: unknown_type');
|
||||
$factory->expects($this->exactly(2))
|
||||
->method('createUpdate')
|
||||
->willReturnCallback(function ($data) use ($validUpdateData, $invalidUpdateData, $exception) {
|
||||
if ($data === $validUpdateData) {
|
||||
return BotStartedUpdate::fromArray($data);
|
||||
}
|
||||
if ($data === $invalidUpdateData) {
|
||||
throw $exception;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$loggerMock->expects($this->once())
|
||||
->method('debug')
|
||||
->with($exception->getMessage(), ['payload' => $invalidUpdateData, 'exception' => $exception]);
|
||||
|
||||
$updateList = $factory->createUpdateList($rawData);
|
||||
|
||||
$this->assertCount(1, $updateList->updates);
|
||||
$this->assertInstanceOf(BotStartedUpdate::class, $updateList->updates[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,4 +185,43 @@ final class WebhookHandlerTest extends TestCase
|
||||
$handler = new WebhookHandler($this->dispatcher, $this->modelFactoryMock, $this->loggerMock, null);
|
||||
$handler->handle(null);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function handleCatchesAndLogsLogicExceptionFromModelFactory(): void
|
||||
{
|
||||
$payload = '{"update_type":"unknown_type","timestamp":123}';
|
||||
$updateData = json_decode($payload, true);
|
||||
$exception = new LogicException('Unknown or unsupported update type received: unknown_type');
|
||||
|
||||
$request = $this->createMockRequest($payload, self::SECRET);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createUpdate')
|
||||
->with($updateData)
|
||||
->willThrowException($exception);
|
||||
|
||||
$callIndex = 0;
|
||||
$this->loggerMock->expects($this->exactly(2))
|
||||
->method('debug')
|
||||
->willReturnCallback(
|
||||
function (string $message, array $context = []) use (&$callIndex, $payload, $exception) {
|
||||
if ($callIndex === 0) {
|
||||
$this->assertSame('Received webhook payload', $message);
|
||||
$this->assertArrayHasKey('body', $context);
|
||||
$this->assertSame($payload, $context['body']);
|
||||
} elseif ($callIndex === 1) {
|
||||
$this->assertSame('Unknown or unsupported update type received: unknown_type', $message);
|
||||
$this->assertArrayHasKey('payload', $context);
|
||||
$this->assertArrayHasKey('exception', $context);
|
||||
$this->assertSame($payload, $context['payload']);
|
||||
$this->assertSame($exception, $context['exception']);
|
||||
}
|
||||
$callIndex++;
|
||||
}
|
||||
);
|
||||
|
||||
$handler = new WebhookHandler($this->dispatcher, $this->modelFactoryMock, $this->loggerMock, self::SECRET);
|
||||
|
||||
$handler->handle($request);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user