Added unsubscribe method

This commit is contained in:
Alex
2025-07-09 22:13:01 +03:00
parent b997fa17ab
commit 341c1ab29b
8 changed files with 257 additions and 68 deletions
+31 -12
View File
@@ -4,13 +4,13 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException;
use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException;
use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\ResultModel;
use BushlanovDev\MaxMessengerBot\Models\Result;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use BushlanovDev\MaxMessengerBot\Models\SubscriptionRequestBody;
use InvalidArgumentException;
/**
@@ -23,7 +23,7 @@ class Api
{
private const string METHOD_GET = 'GET';
private const string METHOD_POST = 'POST';
// private const string METHOD_DELETE = 'DELETE';
private const string METHOD_DELETE = 'DELETE';
private const string ACTION_ME = '/me';
private const string ACTION_SUBSCRIPTIONS = '/subscriptions';
@@ -90,22 +90,41 @@ class Api
/**
* Subscribes the bot to receive updates via WebHook.
*
* @param SubscriptionRequestBody $body
* @param string $url URL webhook.
* @param string|null $secret Secret key for verifying the authenticity of requests.
* @param UpdateType[]|null $update_types List of update types.
*
* @return ResultModel
*
* @throws ClientApiException
* @throws NetworkException
* @throws SerializationException
* @return Result
*/
public function subscribe(SubscriptionRequestBody $body): ResultModel
{
public function subscribe(
string $url,
?string $secret = null,
?array $update_types = null,
): Result {
return $this->modelFactory->createResult(
$this->client->request(
self::METHOD_POST,
self::ACTION_SUBSCRIPTIONS,
[],
$body->toArray(),
compact('url', 'secret', 'update_types'),
)
);
}
/**
* Unsubscribes bot from receiving updates via WebHook.
*
* @param string $url URL webhook.
*
* @return Result
*/
public function unsubscribe(string $url): Result
{
return $this->modelFactory->createResult(
$this->client->request(
self::METHOD_DELETE,
self::ACTION_SUBSCRIPTIONS,
compact('url'),
)
);
}
+4 -4
View File
@@ -6,7 +6,7 @@ namespace BushlanovDev\MaxMessengerBot;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\ResultModel;
use BushlanovDev\MaxMessengerBot\Models\Result;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
/**
@@ -19,11 +19,11 @@ class ModelFactory
*
* @param array<string, mixed> $data
*
* @return ResultModel
* @return Result
*/
public function createResult(array $data): ResultModel
public function createResult(array $data): Result
{
return ResultModel::fromArray($data);
return Result::fromArray($data);
}
/**
@@ -7,7 +7,7 @@ namespace BushlanovDev\MaxMessengerBot\Models;
/**
* Simple response to request.
*/
final readonly class ResultModel extends AbstractModel
final readonly class Result extends AbstractModel
{
/**
* @param bool $success true if request was successful, false otherwise.
-45
View File
@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
final readonly class SubscriptionRequestBody extends AbstractModel
{
/**
* @param string $url URL webhook.
* @param string|null $secret Secret key for verifying the authenticity of requests.
* @param UpdateType[]|null $update_types List of update types.
* @param string|null $version Version of the API.
*/
public function __construct(
public string $url,
public ?string $secret = null,
public ?array $update_types = null,
public ?string $version = null,
) {
}
/**
* @inheritdoc
*/
public static function fromArray(array $data): static
{
$updateTypes = null;
if (isset($data['update_types']) && is_array($data['update_types'])) {
$updateTypes = array_map(
fn(string $typeValue): UpdateType => UpdateType::from($typeValue),
$data['update_types'],
);
}
return new static(
(string)$data['url'],
$data['secret'] ?? null,
$updateTypes,
$data['version'] ?? null,
);
}
}
+52 -6
View File
@@ -7,24 +7,31 @@ namespace BushlanovDev\MaxMessengerBot\Tests;
use BushlanovDev\MaxMessengerBot\Api;
use BushlanovDev\MaxMessengerBot\Client;
use BushlanovDev\MaxMessengerBot\ClientApiInterface;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\MockObject\Exception;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
#[CoversClass(Api::class)]
#[UsesClass(BotInfo::class)]
#[UsesClass(Client::class)]
#[UsesClass(BotInfo::class)]
#[UsesClass(Subscription::class)]
final class ApiTest extends TestCase
{
private MockObject&ClientApiInterface $clientMock;
private MockObject&ModelFactory $modelFactoryMock;
private Api $api;
/**
* @throws Exception
*/
protected function setUp(): void
{
parent::setUp();
@@ -43,11 +50,9 @@ final class ApiTest extends TestCase
$reflection = new ReflectionClass($api);
$clientProp = $reflection->getProperty('client');
$clientProp->setAccessible(true);
$this->assertInstanceOf(ClientApiInterface::class, $clientProp->getValue($api));
$factoryProp = $reflection->getProperty('modelFactory');
$factoryProp->setAccessible(true);
$this->assertInstanceOf(ModelFactory::class, $factoryProp->getValue($api));
}
@@ -56,7 +61,7 @@ final class ApiTest extends TestCase
{
$rawResponseData = ['user_id' => 123, 'first_name' => 'ApiTestBot'];
$expectedBotInfo = new BotInfo(
$botInfo = new BotInfo(
123,
'ApiTestBot',
null, null, true, 0, null, null, null, null,
@@ -72,10 +77,51 @@ final class ApiTest extends TestCase
->expects($this->once())
->method('createBotInfo')
->with($rawResponseData)
->willReturn($expectedBotInfo);
->willReturn($botInfo);
$result = $this->api->getBotInfo();
$this->assertSame($expectedBotInfo, $result);
$this->assertSame($botInfo, $result);
}
#[Test]
public function testSubscribeCallsClientAndFactoryCorrectly(): void
{
$rawResponseData = [
'subscriptions' => [
[
'url' => 'https://example.com/webhook',
'time' => 1678886400000,
'update_types' => ['message_created'],
'version' => '0.0.1',
],
],
];
$subscription = new Subscription(
'https://example.com/webhook',
1678886400000,
[UpdateType::MessageCreated],
'0.0.1',
);
$this->clientMock
->expects($this->once())
->method('request')
->with('GET', '/subscriptions')
->willReturn($rawResponseData);
$this->modelFactoryMock
->expects($this->once())
->method('createSubscriptions')
->with($rawResponseData)
->willReturn([$subscription]);
$result = $this->api->getSubscriptions();
$this->assertIsArray($result);
$this->assertCount(1, $result);
$this->assertInstanceOf(Subscription::class, $result[0]);
$this->assertSame(UpdateType::MessageCreated, $result[0]->update_types[0]);
}
}
+54
View File
@@ -4,9 +4,12 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Result;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
@@ -15,6 +18,8 @@ use PHPUnit\Framework\TestCase;
#[CoversClass(ModelFactory::class)]
#[UsesClass(BotInfo::class)]
#[UsesClass(BotCommand::class)]
#[UsesClass(Result::class)]
#[UsesClass(Subscription::class)]
final class ModelFactoryTest extends TestCase
{
private ModelFactory $factory;
@@ -25,6 +30,33 @@ final class ModelFactoryTest extends TestCase
$this->factory = new ModelFactory();
}
#[Test]
public function createResultSuccessfully(): void
{
$rawData = ['success' => true];
$result = $this->factory->createResult($rawData);
$this->assertInstanceOf(Result::class, $result);
$this->assertTrue($result->success);
$this->assertNull($result->message);
}
#[Test]
public function createResultNotSuccessfully()
{
$rawData = [
'success' => false,
'message' => 'error message',
];
$result = $this->factory->createResult($rawData);
$this->assertInstanceOf(Result::class, $result);
$this->assertFalse($result->success);
$this->assertSame('error message', $result->message);
}
#[Test]
public function createBotInfoCorrectlyHydratesCommands(): void
{
@@ -78,4 +110,26 @@ final class ModelFactoryTest extends TestCase
$this->assertInstanceOf(BotInfo::class, $botInfo);
$this->assertNull($botInfo->commands);
}
#[Test]
public function createSubscriptions(): void
{
$rawData = [
'subscriptions' => [
[
'url' => 'https://example.com/webhook',
'time' => 1678886400000,
'update_types' => ['message_created'],
'version' => '0.0.1',
],
],
];
$subscriptions = $this->factory->createSubscriptions($rawData);
$this->assertIsArray($subscriptions);
$this->assertCount(1, $subscriptions);
$this->assertInstanceOf(Subscription::class, $subscriptions[0]);
$this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->update_types[0]);
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Models\Result;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(Result::class)]
final class ResultTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = [
'success' => true,
'message' => null,
];
$result = Result::fromArray($data);
$this->assertInstanceOf(Result::class, $result);
$this->assertTrue($result->success);
$this->assertNull($result->message);
$array = $result->toArray();
$this->assertIsArray($array);
$this->assertSame($data, $array);
}
#[Test]
public function canBeCreatedFromArrayWithNullMessage(): void
{
$data = [
'success' => false,
'message' => 'error message',
];
$result = Result::fromArray($data);
$this->assertInstanceOf(Result::class, $result);
$this->assertFalse($result->success);
$this->assertSame('error message', $result->message);
$array = $result->toArray();
$this->assertIsArray($array);
$this->assertSame($data, $array);
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(Subscription::class)]
final class SubscriptionTest extends TestCase
{
#[Test]
public function canBeCreatedFromArrayWithAllData(): void
{
$data = [
'url' => 'https://example.com/webhook',
'time' => time(),
'update_types' => [UpdateType::MessageCreated->value, UpdateType::BotStarted->value],
'version' => '0.0.1',
];
$subscription = Subscription::fromArray($data);
$this->assertInstanceOf(Subscription::class, $subscription);
$this->assertSame($data['url'], $subscription->url);
$this->assertSame($data['time'], $subscription->time);
$this->assertSame([UpdateType::MessageCreated, UpdateType::BotStarted], $subscription->update_types);
$this->assertSame($data['version'], $subscription->version);
$array = $subscription->toArray();
$this->assertIsArray($array);
$this->assertSame($data, $array);
}
#[Test]
public function canBeCreatedFromArrayWithOptionalDataNull(): void
{
$data = [
'url' => 'https://example.com/webhook',
'time' => time(),
'update_types' => null,
'version' => null,
];
$subscription = Subscription::fromArray($data);
$this->assertInstanceOf(Subscription::class, $subscription);
$this->assertSame($data['url'], $subscription->url);
$this->assertSame($data['time'], $subscription->time);
$this->assertNull($subscription->update_types);
$this->assertNull($subscription->version);
$array = $subscription->toArray();
$this->assertIsArray($array);
$this->assertSame($data, $array);
}
}