diff --git a/config/xml/Bitrix24.Lib.Journal.Entity.JournalItem.dcm.xml b/config/xml/Bitrix24.Lib.Journal.Entity.JournalItem.dcm.xml
index fd2ac06..99fb5c3 100644
--- a/config/xml/Bitrix24.Lib.Journal.Entity.JournalItem.dcm.xml
+++ b/config/xml/Bitrix24.Lib.Journal.Entity.JournalItem.dcm.xml
@@ -6,6 +6,8 @@
+
+
@@ -13,12 +15,17 @@
+
+
+
+
-
+
-
+
+
diff --git a/config/xml/Bitrix24.Lib.Journal.ValueObjects.JournalContext.dcm.xml b/config/xml/Bitrix24.Lib.Journal.Entity.ValueObjects.Context.dcm.xml
similarity index 67%
rename from config/xml/Bitrix24.Lib.Journal.ValueObjects.JournalContext.dcm.xml
rename to config/xml/Bitrix24.Lib.Journal.Entity.ValueObjects.Context.dcm.xml
index 97fdee3..fff5a50 100644
--- a/config/xml/Bitrix24.Lib.Journal.ValueObjects.JournalContext.dcm.xml
+++ b/config/xml/Bitrix24.Lib.Journal.Entity.ValueObjects.Context.dcm.xml
@@ -1,13 +1,11 @@
-
-
-
+
-
+
diff --git a/rector.php b/rector.php
index 3ce8e42..c957618 100644
--- a/rector.php
+++ b/rector.php
@@ -14,7 +14,8 @@
use Rector\Config\RectorConfig;
use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
-use Rector\Set\ValueObject\DowngradeLevelSetList;
+use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector;
+use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector;
return RectorConfig::configure()
->withPaths([
@@ -48,5 +49,7 @@
strictBooleans: true
)
->withSkip([
- RenamePropertyToMatchTypeRector::class
+ RenamePropertyToMatchTypeRector::class,
+ RenameParamToMatchTypeRector::class,
+ FlipTypeControlToUseExclusiveTypeRector::class,
]);
\ No newline at end of file
diff --git a/src/ApplicationInstallations/UseCase/Install/Command.php b/src/ApplicationInstallations/UseCase/Install/Command.php
index 224f9ba..8e5e191 100644
--- a/src/ApplicationInstallations/UseCase/Install/Command.php
+++ b/src/ApplicationInstallations/UseCase/Install/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\ApplicationInstallations\UseCase\Install;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\SDK\Application\ApplicationStatus;
use Bitrix24\SDK\Application\PortalLicenseFamily;
use Bitrix24\SDK\Core\Credentials\AuthToken;
diff --git a/src/ApplicationInstallations/UseCase/OnAppInstall/Command.php b/src/ApplicationInstallations/UseCase/OnAppInstall/Command.php
index 5ce5b06..a0a88fc 100644
--- a/src/ApplicationInstallations/UseCase/OnAppInstall/Command.php
+++ b/src/ApplicationInstallations/UseCase/OnAppInstall/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\ApplicationInstallations\UseCase\OnAppInstall;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
* Command is called when installation occurs through UI.
diff --git a/src/ApplicationInstallations/UseCase/Uninstall/Command.php b/src/ApplicationInstallations/UseCase/Uninstall/Command.php
index 84debaa..a3a0ee8 100644
--- a/src/ApplicationInstallations/UseCase/Uninstall/Command.php
+++ b/src/ApplicationInstallations/UseCase/Uninstall/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\ApplicationInstallations\UseCase\Uninstall;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
readonly class Command
{
diff --git a/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php
index 0d640a8..8ceb9e6 100644
--- a/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php
+++ b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\Bitrix24Accounts\UseCase\ChangeDomainUrl;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
readonly class Command
{
diff --git a/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php
index 979b0e1..30de5e0 100644
--- a/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php
+++ b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\Bitrix24Accounts\UseCase\InstallFinish;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
readonly class Command
{
diff --git a/src/Bitrix24Accounts/UseCase/InstallStart/Command.php b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php
index e480182..10f989e 100644
--- a/src/Bitrix24Accounts/UseCase/InstallStart/Command.php
+++ b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php
@@ -4,7 +4,7 @@
namespace Bitrix24\Lib\Bitrix24Accounts\UseCase\InstallStart;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\SDK\Core\Credentials\AuthToken;
use Bitrix24\SDK\Core\Credentials\Scope;
diff --git a/src/Journal/Controller/JournalAdminController.php b/src/Journal/Controller/JournalAdminController.php
index 0aab3bb..8dd1560 100644
--- a/src/Journal/Controller/JournalAdminController.php
+++ b/src/Journal/Controller/JournalAdminController.php
@@ -14,7 +14,8 @@
namespace Bitrix24\Lib\Journal\Controller;
use Bitrix24\Lib\Journal\Entity\LogLevel;
-use Bitrix24\Lib\Journal\ReadModel\JournalItemReadRepository;
+use Bitrix24\Lib\Journal\Infrastructure\Doctrine\JournalItemReadRepository;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -22,22 +23,22 @@
/**
* Admin controller for journal management
- * Developer should configure routes in their application
+ * Developer should configure routes in their application.
*/
class JournalAdminController extends AbstractController
{
public function __construct(
private readonly JournalItemReadRepository $journalReadRepository
- ) {
- }
+ ) {}
/**
- * List journal items with filters and pagination
+ * List journal items with filters and pagination.
*/
public function list(Request $request): Response
{
$page = max(1, $request->query->getInt('page', 1));
$domainUrl = $request->query->get('domain');
+ $memberId = $request->query->get('member_id');
$levelValue = $request->query->get('level');
$label = $request->query->get('label');
@@ -47,8 +48,9 @@ public function list(Request $request): Response
}
$pagination = $this->journalReadRepository->findWithFilters(
- domainUrl: $domainUrl ?: null,
- level: $level,
+ memberId: $memberId ?: null,
+ domain: $domainUrl ? new Domain($domainUrl) : null,
+ logLevel: $level,
label: $label ?: null,
page: $page,
limit: 50
@@ -61,6 +63,7 @@ public function list(Request $request): Response
'pagination' => $pagination,
'currentFilters' => [
'domain' => $domainUrl,
+ 'member_id' => $memberId,
'level' => $levelValue,
'label' => $label,
],
@@ -71,7 +74,7 @@ public function list(Request $request): Response
}
/**
- * Show journal item details
+ * Show journal item details.
*/
public function show(string $id): Response
{
@@ -83,7 +86,7 @@ public function show(string $id): Response
$journalItem = $this->journalReadRepository->findById($uuid);
- if (!$journalItem) {
+ if (null === $journalItem) {
throw $this->createNotFoundException('Journal item not found');
}
diff --git a/src/Journal/Docs/README.md b/src/Journal/Docs/README.md
index d30442c..7e8b9a5 100644
--- a/src/Journal/Docs/README.md
+++ b/src/Journal/Docs/README.md
@@ -20,7 +20,8 @@ $factory = $container->get(JournalLoggerFactory::class);
// Создаем логгер для конкретной установки приложения
$installationId = Uuid::fromString('...');
-$logger = $factory->createLogger($installationId);
+$memberId = '...';
+$logger = $factory->createLogger($memberId, $installationId);
// Используем как обычный PSR-3 логгер
$logger->info('Синхронизация завершена', [
@@ -50,6 +51,7 @@ use Bitrix24\Lib\Journal\Services\JournalLogger;
use Bitrix24\Lib\Journal\Infrastructure\Doctrine\DoctrineDbalJournalItemRepository;
$logger = new JournalLogger(
+ memberId: $memberId,
applicationInstallationId: $installationId,
repository: $repository,
entityManager: $entityManager
@@ -74,13 +76,14 @@ $logger->debug('Отладочная информация');
use Bitrix24\Lib\Journal\Entity\JournalItem;
// Создание через статические методы
-$item = JournalItem::info($installationId, 'Сообщение', [
+$item = JournalItem::info($memberId, $installationId, 'Сообщение', [
'label' => 'custom.label',
'payload' => ['key' => 'value']
]);
// Или через create с явным указанием уровня
$item = JournalItem::create(
+ memberId: $memberId,
applicationInstallationId: $installationId,
level: LogLevel::error,
message: 'Сообщение об ошибке',
@@ -103,10 +106,9 @@ $entityManager->flush();
// Поиск
$item = $repository->findById($uuid);
-$items = $repository->findByApplicationInstallationId($installationId, LogLevel::error, 50, 0);
+$items = $repository->findByApplicationInstallationId($memberId, $installationId, LogLevel::error, 50, 0);
// Очистка
-$deleted = $repository->deleteByApplicationInstallationId($installationId);
$deleted = $repository->deleteOlderThan(new CarbonImmutable('-30 days'));
```
@@ -126,14 +128,15 @@ $repository->clear();
### 4. Admin UI (ReadModel)
```php
-use Bitrix24\Lib\Journal\ReadModel\JournalItemReadRepository;
+use Bitrix24\Lib\Journal\Infrastructure\Doctrine\JournalItemReadRepository;
$readRepo = new JournalItemReadRepository($entityManager, $paginator);
// Получение с фильтрами и пагинацией
$pagination = $readRepo->findWithFilters(
- domainUrl: 'example.bitrix24.ru',
- level: LogLevel::error,
+ memberId: '66c9893d5f30e6.45265697',
+ domain: new Domain('example.bitrix24.ru'),
+ logLevel: LogLevel::error,
label: 'b24.api.error',
page: 1,
limit: 50
@@ -185,6 +188,7 @@ class MyTest extends TestCase
$entityManager = $this->createMock(EntityManagerInterface::class);
$this->logger = new JournalLogger(
+ '66c9893d5f30e6.45265697',
Uuid::v7(),
$this->repository,
$entityManager
@@ -215,6 +219,7 @@ class MyTest extends TestCase
Таблица `journal_item` с полями:
- `id` (UUID) - PK
+- `member_id` (string) - ID портала Bitrix24
- `application_installation_id` (UUID) - FK к установке приложения
- `created_at_utc` (timestamp) - время создания
- `level` (string) - уровень логирования
@@ -222,6 +227,7 @@ class MyTest extends TestCase
- `label`, `payload`, `bitrix24_user_id`, `ip_address` - поля контекста
Индексы:
-- `application_installation_id`
+- `member_id, application_installation_id, level, created_at_utc` (composite)
+- `member_id`
- `created_at_utc`
- `level`
diff --git a/src/Journal/Entity/JournalItem.php b/src/Journal/Entity/JournalItem.php
index 36a1011..327b8e6 100644
--- a/src/Journal/Entity/JournalItem.php
+++ b/src/Journal/Entity/JournalItem.php
@@ -14,14 +14,14 @@
namespace Bitrix24\Lib\Journal\Entity;
use Bitrix24\Lib\AggregateRoot;
-use Bitrix24\Lib\Journal\ValueObjects\JournalContext;
+use Bitrix24\Lib\Journal\Entity\ValueObjects\Context;
use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
use Carbon\CarbonImmutable;
use Symfony\Component\Uid\Uuid;
/**
* Journal item entity
- * Each journal record contains domain business events for technical support staff
+ * Each journal record contains domain business events for technical support staff.
*/
class JournalItem extends AggregateRoot implements JournalItemInterface
{
@@ -30,15 +30,26 @@ class JournalItem extends AggregateRoot implements JournalItemInterface
private readonly CarbonImmutable $createdAt;
public function __construct(
- private Uuid $applicationInstallationId,
- private LogLevel $level,
- private string $message,
- private JournalContext $context
+ private readonly string $memberId,
+ private readonly Uuid $applicationInstallationId,
+ private readonly LogLevel $level,
+ private readonly string $message,
+ private readonly string $label,
+ private readonly ?string $userId,
+ private readonly Context $context
) {
+ if ('' === trim($this->memberId)) {
+ throw new InvalidArgumentException('memberId cannot be empty');
+ }
+
if ('' === trim($this->message)) {
throw new InvalidArgumentException('Journal message cannot be empty');
}
+ if ('' === trim($this->label)) {
+ throw new InvalidArgumentException('Journal label cannot be empty');
+ }
+
$this->id = Uuid::v7();
$this->createdAt = new CarbonImmutable();
}
@@ -55,6 +66,12 @@ public function getApplicationInstallationId(): Uuid
return $this->applicationInstallationId;
}
+ #[\Override]
+ public function getMemberId(): string
+ {
+ return $this->memberId;
+ }
+
#[\Override]
public function getCreatedAt(): CarbonImmutable
{
@@ -74,68 +91,86 @@ public function getMessage(): string
}
#[\Override]
- public function getContext(): JournalContext
+ public function getLabel(): string
+ {
+ return $this->label;
+ }
+
+ #[\Override]
+ public function getUserId(): ?string
+ {
+ return $this->userId;
+ }
+
+ #[\Override]
+ public function getContext(): Context
{
return $this->context;
}
/**
- * Create journal item with custom log level
+ * Create journal item with custom log level.
*/
public static function create(
+ string $memberId,
Uuid $applicationInstallationId,
LogLevel $level,
string $message,
- JournalContext $context
+ string $label,
+ ?string $userId,
+ Context $context
): self {
return new self(
+ memberId: $memberId,
applicationInstallationId: $applicationInstallationId,
level: $level,
message: $message,
+ label: $label,
+ userId: $userId,
context: $context
);
}
/**
- * PSR-3 compatible factory methods
+ * PSR-3 compatible factory methods.
*/
- public static function emergency(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function emergency(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::emergency, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::emergency, $message, $label, $userId, $context);
}
- public static function alert(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function alert(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::alert, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::alert, $message, $label, $userId, $context);
}
- public static function critical(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function critical(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::critical, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::critical, $message, $label, $userId, $context);
}
- public static function error(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function error(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::error, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::error, $message, $label, $userId, $context);
}
- public static function warning(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function warning(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::warning, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::warning, $message, $label, $userId, $context);
}
- public static function notice(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function notice(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::notice, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::notice, $message, $label, $userId, $context);
}
- public static function info(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function info(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::info, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::info, $message, $label, $userId, $context);
}
- public static function debug(Uuid $applicationInstallationId, string $message, JournalContext $context): self
+ public static function debug(string $memberId, Uuid $applicationInstallationId, string $message, string $label, ?string $userId, Context $context): self
{
- return self::create($applicationInstallationId, LogLevel::debug, $message, $context);
+ return self::create($memberId, $applicationInstallationId, LogLevel::debug, $message, $label, $userId, $context);
}
}
diff --git a/src/Journal/Entity/JournalItemInterface.php b/src/Journal/Entity/JournalItemInterface.php
index bccbddb..b87edd6 100644
--- a/src/Journal/Entity/JournalItemInterface.php
+++ b/src/Journal/Entity/JournalItemInterface.php
@@ -13,12 +13,12 @@
namespace Bitrix24\Lib\Journal\Entity;
-use Bitrix24\Lib\Journal\ValueObjects\JournalContext;
+use Bitrix24\Lib\Journal\Entity\ValueObjects\Context;
use Carbon\CarbonImmutable;
use Symfony\Component\Uid\Uuid;
/**
- * Journal item interface for SDK contract extraction
+ * Journal item interface for SDK contract extraction.
*/
interface JournalItemInterface
{
@@ -26,11 +26,17 @@ public function getId(): Uuid;
public function getApplicationInstallationId(): Uuid;
+ public function getMemberId(): string;
+
public function getCreatedAt(): CarbonImmutable;
public function getLevel(): LogLevel;
public function getMessage(): string;
- public function getContext(): JournalContext;
+ public function getUserId(): ?string;
+
+ public function getLabel(): string;
+
+ public function getContext(): Context;
}
diff --git a/src/Journal/Entity/LogLevel.php b/src/Journal/Entity/LogLevel.php
index bc4dfbb..1f8c891 100644
--- a/src/Journal/Entity/LogLevel.php
+++ b/src/Journal/Entity/LogLevel.php
@@ -14,7 +14,7 @@
namespace Bitrix24\Lib\Journal\Entity;
/**
- * PSR-3 compatible log level enum
+ * PSR-3 compatible log level enum.
*/
enum LogLevel: string
{
@@ -28,7 +28,7 @@ enum LogLevel: string
case debug = 'debug';
/**
- * Creates LogLevel from PSR-3 log level string
+ * Creates LogLevel from PSR-3 log level string.
*/
public static function fromPsr3Level(string $level): self
{
diff --git a/src/Journal/ValueObjects/JournalContext.php b/src/Journal/Entity/ValueObjects/Context.php
similarity index 77%
rename from src/Journal/ValueObjects/JournalContext.php
rename to src/Journal/Entity/ValueObjects/Context.php
index 627e5cd..046e987 100644
--- a/src/Journal/ValueObjects/JournalContext.php
+++ b/src/Journal/Entity/ValueObjects/Context.php
@@ -11,27 +11,20 @@
declare(strict_types=1);
-namespace Bitrix24\Lib\Journal\ValueObjects;
+namespace Bitrix24\Lib\Journal\Entity\ValueObjects;
use Darsyn\IP\Version\Multi as IP;
/**
- * Journal context value object
+ * Journal context value object.
*/
-readonly class JournalContext
+readonly class Context
{
public function __construct(
- private string $label,
private ?array $payload = null,
private ?int $bitrix24UserId = null,
private ?IP $ipAddress = null
- ) {
- }
-
- public function getLabel(): string
- {
- return $this->label;
- }
+ ) {}
public function getPayload(): ?array
{
@@ -49,12 +42,11 @@ public function getIpAddress(): ?IP
}
/**
- * Convert to array
+ * Convert to array.
*/
public function toArray(): array
{
return [
- 'label' => $this->label,
'payload' => $this->payload,
'bitrix24UserId' => $this->bitrix24UserId,
'ipAddress' => $this->ipAddress?->getCompactedAddress(),
diff --git a/src/Journal/Infrastructure/Doctrine/DoctrineDbalJournalItemRepository.php b/src/Journal/Infrastructure/Doctrine/DoctrineDbalJournalItemRepository.php
index a9bccc7..927856e 100644
--- a/src/Journal/Infrastructure/Doctrine/DoctrineDbalJournalItemRepository.php
+++ b/src/Journal/Infrastructure/Doctrine/DoctrineDbalJournalItemRepository.php
@@ -22,24 +22,26 @@
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Uid\Uuid;
-class DoctrineDbalJournalItemRepository extends EntityRepository implements JournalItemRepositoryInterface
+class DoctrineDbalJournalItemRepository implements JournalItemRepositoryInterface
{
+ private readonly EntityRepository $repository;
+
public function __construct(
- EntityManagerInterface $entityManager
+ private readonly EntityManagerInterface $entityManager
) {
- parent::__construct($entityManager, $entityManager->getClassMetadata(JournalItem::class));
+ $this->repository = $this->entityManager->getRepository(JournalItem::class);
}
#[\Override]
public function save(JournalItemInterface $journalItem): void
{
- $this->getEntityManager()->persist($journalItem);
+ $this->entityManager->persist($journalItem);
}
#[\Override]
- public function findById(Uuid $id): ?JournalItemInterface
+ public function findById(Uuid $uuid): ?JournalItemInterface
{
- return $this->getEntityManager()->getRepository(JournalItem::class)->find($id);
+ return $this->repository->find($uuid);
}
/**
@@ -47,69 +49,88 @@ public function findById(Uuid $id): ?JournalItemInterface
*/
#[\Override]
public function findByApplicationInstallationId(
+ string $memberId,
Uuid $applicationInstallationId,
- ?LogLevel $level = null,
+ ?LogLevel $logLevel = null,
?int $limit = null,
?int $offset = null
): array {
- $qb = $this->getEntityManager()->getRepository(JournalItem::class)
+ $queryBuilder = $this->repository
->createQueryBuilder('j')
- ->where('j.applicationInstallationId = :appId')
+ ->where('j.memberId = :memberId')
+ ->setParameter('memberId', $memberId)
+ ->andWhere('j.applicationInstallationId = :appId')
->setParameter('appId', $applicationInstallationId)
- ->orderBy('j.createdAt', 'DESC');
+ ->orderBy('j.createdAt', 'DESC')
+ ;
- if (null !== $level) {
- $qb->andWhere('j.level = :level')
- ->setParameter('level', $level);
+ if (null !== $logLevel) {
+ $queryBuilder->andWhere('j.level = :level')
+ ->setParameter('level', $logLevel)
+ ;
}
if (null !== $limit) {
- $qb->setMaxResults($limit);
+ $queryBuilder->setMaxResults($limit);
}
if (null !== $offset) {
- $qb->setFirstResult($offset);
+ $queryBuilder->setFirstResult($offset);
}
- return $qb->getQuery()->getResult();
+ return $queryBuilder->getQuery()->getResult();
}
+ /**
+ * @return JournalItemInterface[]
+ */
#[\Override]
- public function deleteByApplicationInstallationId(Uuid $applicationInstallationId): int
- {
- return $this->getEntityManager()->createQueryBuilder()
- ->delete(JournalItem::class, 'j')
- ->where('j.applicationInstallationId = :appId')
- ->setParameter('appId', $applicationInstallationId)
- ->getQuery()
- ->execute();
+ public function findByMemberId(
+ string $memberId,
+ ?LogLevel $logLevel = null,
+ ?int $limit = null,
+ ?int $offset = null
+ ): array {
+ $queryBuilder = $this->repository
+ ->createQueryBuilder('j')
+ ->where('j.memberId = :memberId')
+ ->setParameter('memberId', $memberId)
+ ->orderBy('j.createdAt', 'DESC')
+ ;
+
+ if (null !== $logLevel) {
+ $queryBuilder->andWhere('j.level = :level')
+ ->setParameter('level', $logLevel)
+ ;
+ }
+
+ if (null !== $limit) {
+ $queryBuilder->setMaxResults($limit);
+ }
+
+ if (null !== $offset) {
+ $queryBuilder->setFirstResult($offset);
+ }
+
+ return $queryBuilder->getQuery()->getResult();
}
#[\Override]
- public function deleteOlderThan(CarbonImmutable $date): int
- {
- return $this->getEntityManager()->createQueryBuilder()
+ public function deleteOlderThan(
+ string $memberId,
+ Uuid $applicationInstallationId,
+ CarbonImmutable $date
+ ): int {
+ return $this->entityManager->createQueryBuilder()
->delete(JournalItem::class, 'j')
- ->where('j.createdAt < :date')
+ ->where('j.memberId = :memberId')
+ ->andWhere('j.applicationInstallationId = :appId')
+ ->andWhere('j.createdAt < :date')
+ ->setParameter('memberId', $memberId)
+ ->setParameter('appId', $applicationInstallationId)
->setParameter('date', $date)
->getQuery()
- ->execute();
- }
-
- #[\Override]
- public function countByApplicationInstallationId(Uuid $applicationInstallationId, ?LogLevel $level = null): int
- {
- $qb = $this->getEntityManager()->getRepository(JournalItem::class)
- ->createQueryBuilder('j')
- ->select('COUNT(j.id)')
- ->where('j.applicationInstallationId = :appId')
- ->setParameter('appId', $applicationInstallationId);
-
- if (null !== $level) {
- $qb->andWhere('j.level = :level')
- ->setParameter('level', $level);
- }
-
- return (int) $qb->getQuery()->getSingleScalarResult();
+ ->execute()
+ ;
}
}
diff --git a/src/Journal/Infrastructure/Doctrine/JournalItemReadRepository.php b/src/Journal/Infrastructure/Doctrine/JournalItemReadRepository.php
new file mode 100644
index 0000000..404cd8e
--- /dev/null
+++ b/src/Journal/Infrastructure/Doctrine/JournalItemReadRepository.php
@@ -0,0 +1,156 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\Lib\Journal\Infrastructure\Doctrine;
+
+use Bitrix24\Lib\ApplicationInstallations\Entity\ApplicationInstallation;
+use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account;
+use Bitrix24\Lib\Journal\Entity\JournalItem;
+use Bitrix24\Lib\Journal\Entity\JournalItemInterface;
+use Bitrix24\Lib\Journal\Entity\LogLevel;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\QueryBuilder;
+use Knp\Component\Pager\Pagination\PaginationInterface;
+use Knp\Component\Pager\PaginatorInterface;
+use Symfony\Component\Uid\Uuid;
+
+/**
+ * Read the model repository for journal items with filtering and pagination.
+ */
+readonly class JournalItemReadRepository
+{
+ public function __construct(
+ private EntityManagerInterface $entityManager,
+ private PaginatorInterface $paginator
+ ) {}
+
+ /**
+ * Find journal items with filters and pagination.
+ *
+ * @return PaginationInterface
+ */
+ public function findWithFilters(
+ ?string $memberId = null,
+ ?Domain $domain = null,
+ ?LogLevel $logLevel = null,
+ ?string $label = null,
+ int $page = 1,
+ int $limit = 50
+ ): PaginationInterface {
+ $queryBuilder = $this->createFilteredQueryBuilder($memberId, $domain, $logLevel, $label);
+
+ return $this->paginator->paginate(
+ $queryBuilder,
+ $page,
+ $limit,
+ [
+ 'defaultSortFieldName' => 'j.createdAt',
+ 'defaultSortDirection' => 'desc',
+ ]
+ );
+ }
+
+ /**
+ * Find journal item by ID.
+ */
+ public function findById(Uuid $uuid): ?JournalItemInterface
+ {
+ return $this->entityManager->getRepository(JournalItem::class)->find($uuid);
+ }
+
+ /**
+ * Get available domain URLs from journal.
+ *
+ * @return string[]
+ */
+ public function getAvailableDomains(): array
+ {
+ // Join with ApplicationInstallation and then Bitrix24Account to get domain URLs
+ $queryBuilder = $this->entityManager->createQueryBuilder();
+ $queryBuilder->select('DISTINCT b24.domainUrl')
+ ->from(JournalItem::class, 'j')
+ ->innerJoin(ApplicationInstallation::class, 'ai', 'WITH', 'ai.id = j.applicationInstallationId')
+ ->innerJoin(Bitrix24Account::class, 'b24', 'WITH', 'b24.id = ai.bitrix24AccountId')
+ ->orderBy('b24.domainUrl', 'ASC')
+ ;
+
+ $results = $queryBuilder->getQuery()->getScalarResult();
+
+ return array_column($results, 'domainUrl');
+ }
+
+ /**
+ * Get available labels from journal.
+ *
+ * @return string[]
+ */
+ public function getAvailableLabels(): array
+ {
+ $queryBuilder = $this->entityManager->createQueryBuilder();
+ $queryBuilder->select('DISTINCT j.label')
+ ->from(JournalItem::class, 'j')
+ ->where('j.label IS NOT NULL')
+ ->orderBy('j.label', 'ASC')
+ ;
+
+ $results = $queryBuilder->getQuery()->getScalarResult();
+
+ return array_filter(array_column($results, 'label'));
+ }
+
+ /**
+ * Create query builder with filters.
+ */
+ private function createFilteredQueryBuilder(
+ ?string $memberId = null,
+ ?Domain $domain = null,
+ ?LogLevel $logLevel = null,
+ ?string $label = null
+ ): QueryBuilder {
+ $queryBuilder = $this->entityManager->createQueryBuilder();
+ $queryBuilder->select('j')
+ ->from(JournalItem::class, 'j')
+ ;
+
+ if (null !== $memberId) {
+ $queryBuilder->andWhere('j.memberId = :memberId')
+ ->setParameter('memberId', $memberId)
+ ;
+ }
+
+ if (null !== $domain) {
+ $queryBuilder->innerJoin(ApplicationInstallation::class, 'ai', 'WITH', 'ai.id = j.applicationInstallationId')
+ ->innerJoin(Bitrix24Account::class, 'b24', 'WITH', 'b24.id = ai.bitrix24AccountId')
+ ->andWhere('b24.domainUrl = :domainUrl')
+ ->setParameter('domainUrl', $domain->value)
+ ;
+ }
+
+ if (null !== $logLevel) {
+ $queryBuilder->andWhere('j.level = :level')
+ ->setParameter('level', $logLevel)
+ ;
+ }
+
+ if (null !== $label) {
+ $queryBuilder->andWhere('j.label = :label')
+ ->setParameter('label', $label)
+ ;
+ }
+
+ $queryBuilder->orderBy('j.createdAt', 'DESC');
+
+ return $queryBuilder;
+ }
+}
diff --git a/src/Journal/Infrastructure/JournalItemRepositoryInterface.php b/src/Journal/Infrastructure/JournalItemRepositoryInterface.php
index eb1fe4d..099c2cd 100644
--- a/src/Journal/Infrastructure/JournalItemRepositoryInterface.php
+++ b/src/Journal/Infrastructure/JournalItemRepositoryInterface.php
@@ -19,44 +19,51 @@
use Symfony\Component\Uid\Uuid;
/**
- * Journal item repository interface for SDK contract extraction
+ * Journal item repository interface for SDK contract extraction.
*/
interface JournalItemRepositoryInterface
{
/**
- * Save journal item
+ * Save journal item.
*/
public function save(JournalItemInterface $journalItem): void;
/**
- * Find journal item by ID
+ * Find journal item by ID.
*/
- public function findById(Uuid $id): ?JournalItemInterface;
+ public function findById(Uuid $uuid): ?JournalItemInterface;
/**
- * Find journal items by application installation ID
+ * Find journal items by application installation ID.
*
* @return JournalItemInterface[]
*/
public function findByApplicationInstallationId(
+ string $memberId,
Uuid $applicationInstallationId,
- ?LogLevel $level = null,
+ ?LogLevel $logLevel = null,
?int $limit = null,
?int $offset = null
): array;
/**
- * Delete all journal items by application installation ID
- */
- public function deleteByApplicationInstallationId(Uuid $applicationInstallationId): int;
-
- /**
- * Delete journal items older than specified date
+ * Find journal items by member ID.
+ *
+ * @return JournalItemInterface[]
*/
- public function deleteOlderThan(CarbonImmutable $date): int;
+ public function findByMemberId(
+ string $memberId,
+ ?LogLevel $logLevel = null,
+ ?int $limit = null,
+ ?int $offset = null
+ ): array;
/**
- * Count journal items by application installation ID
+ * Delete journal items older than specified date.
*/
- public function countByApplicationInstallationId(Uuid $applicationInstallationId, ?LogLevel $level = null): int;
+ public function deleteOlderThan(
+ string $memberId,
+ Uuid $applicationInstallationId,
+ CarbonImmutable $date
+ ): int;
}
diff --git a/src/Journal/ReadModel/JournalItemReadRepository.php b/src/Journal/ReadModel/JournalItemReadRepository.php
deleted file mode 100644
index 6faa9be..0000000
--- a/src/Journal/ReadModel/JournalItemReadRepository.php
+++ /dev/null
@@ -1,140 +0,0 @@
-
- *
- * For the full copyright and license information, please view the MIT-LICENSE.txt
- * file that was distributed with this source code.
- */
-
-declare(strict_types=1);
-
-namespace Bitrix24\Lib\Journal\ReadModel;
-
-use Bitrix24\Lib\Journal\Entity\JournalItem;
-use Bitrix24\Lib\Journal\Entity\JournalItemInterface;
-use Bitrix24\Lib\Journal\Entity\LogLevel;
-use Doctrine\ORM\EntityManagerInterface;
-use Doctrine\ORM\QueryBuilder;
-use Knp\Component\Pager\PaginatorInterface;
-use Knp\Component\Pager\Pagination\PaginationInterface;
-use Symfony\Component\Uid\Uuid;
-
-/**
- * Read model repository for journal items with filtering and pagination
- */
-readonly class JournalItemReadRepository
-{
- public function __construct(
- private EntityManagerInterface $entityManager,
- private PaginatorInterface $paginator
- ) {
- }
-
- /**
- * Find journal items with filters and pagination
- *
- * @return PaginationInterface
- */
- public function findWithFilters(
- ?string $domainUrl = null,
- ?LogLevel $level = null,
- ?string $label = null,
- int $page = 1,
- int $limit = 50
- ): PaginationInterface {
- $qb = $this->createFilteredQueryBuilder($domainUrl, $level, $label);
-
- return $this->paginator->paginate(
- $qb,
- $page,
- $limit,
- [
- 'defaultSortFieldName' => 'j.createdAt',
- 'defaultSortDirection' => 'desc',
- ]
- );
- }
-
- /**
- * Find journal item by ID
- */
- public function findById(Uuid $id): ?JournalItemInterface
- {
- return $this->entityManager->getRepository(JournalItem::class)->find($id);
- }
-
- /**
- * Get available domain URLs from journal
- *
- * @return string[]
- */
- public function getAvailableDomains(): array
- {
- // Join with ApplicationInstallation and then Bitrix24Account to get domain URLs
- $qb = $this->entityManager->createQueryBuilder();
- $qb->select('DISTINCT b24.domainUrl')
- ->from(JournalItem::class, 'j')
- ->innerJoin('Bitrix24\Lib\ApplicationInstallations\Entity\ApplicationInstallation', 'ai', 'WITH', 'ai.id = j.applicationInstallationId')
- ->innerJoin('Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account', 'b24', 'WITH', 'b24.id = ai.bitrix24AccountId')
- ->orderBy('b24.domainUrl', 'ASC');
-
- $results = $qb->getQuery()->getScalarResult();
-
- return array_column($results, 'domainUrl');
- }
-
- /**
- * Get available labels from journal
- *
- * @return string[]
- */
- public function getAvailableLabels(): array
- {
- $qb = $this->entityManager->createQueryBuilder();
- $qb->select('DISTINCT j.context.label')
- ->from(JournalItem::class, 'j')
- ->where('j.context.label IS NOT NULL')
- ->orderBy('j.context.label', 'ASC');
-
- $results = $qb->getQuery()->getScalarResult();
-
- return array_filter(array_column($results, 'label'));
- }
-
- /**
- * Create query builder with filters
- */
- private function createFilteredQueryBuilder(
- ?string $domainUrl = null,
- ?LogLevel $level = null,
- ?string $label = null
- ): QueryBuilder {
- $qb = $this->entityManager->createQueryBuilder();
- $qb->select('j')
- ->from(JournalItem::class, 'j');
-
- if ($domainUrl) {
- $qb->innerJoin('Bitrix24\Lib\ApplicationInstallations\Entity\ApplicationInstallation', 'ai', 'WITH', 'ai.id = j.applicationInstallationId')
- ->innerJoin('Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account', 'b24', 'WITH', 'b24.id = ai.bitrix24AccountId')
- ->andWhere('b24.domainUrl = :domainUrl')
- ->setParameter('domainUrl', $domainUrl);
- }
-
- if ($level) {
- $qb->andWhere('j.level = :level')
- ->setParameter('level', $level);
- }
-
- if ($label) {
- $qb->andWhere('j.context.label = :label')
- ->setParameter('label', $label);
- }
-
- $qb->orderBy('j.createdAt', 'DESC');
-
- return $qb;
- }
-}
diff --git a/src/Journal/Services/JournalLogger.php b/src/Journal/Services/JournalLogger.php
index 1af06ac..3a6c024 100644
--- a/src/Journal/Services/JournalLogger.php
+++ b/src/Journal/Services/JournalLogger.php
@@ -15,8 +15,8 @@
use Bitrix24\Lib\Journal\Entity\JournalItem;
use Bitrix24\Lib\Journal\Entity\LogLevel;
+use Bitrix24\Lib\Journal\Entity\ValueObjects\Context;
use Bitrix24\Lib\Journal\Infrastructure\JournalItemRepositoryInterface;
-use Bitrix24\Lib\Journal\ValueObjects\JournalContext;
use Darsyn\IP\Version\Multi as IP;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
@@ -25,36 +25,40 @@
/**
* PSR-3 compatible journal logger
- * Writes log entries to the journal repository
+ * Writes log entries to the journal repository.
*/
class JournalLogger implements LoggerInterface
{
use LoggerTrait;
public function __construct(
+ private readonly string $memberId,
private readonly Uuid $applicationInstallationId,
private readonly JournalItemRepositoryInterface $repository,
private readonly EntityManagerInterface $entityManager
- ) {
- }
+ ) {}
/**
- * Logs with an arbitrary level
+ * Logs with an arbitrary level.
*
- * @param mixed $level
- * @param string|\Stringable $message
+ * @param mixed $level
* @param array $context
*/
#[\Override]
public function log($level, string|\Stringable $message, array $context = []): void
{
$logLevel = $this->convertLevel($level);
+ $label = $context['label'] ?? 'application.log';
+ $userId = $context['userId'] ?? null;
$journalContext = $this->createContext($context);
$journalItem = JournalItem::create(
+ memberId: $this->memberId,
applicationInstallationId: $this->applicationInstallationId,
level: $logLevel,
message: (string) $message,
+ label: (string) $label,
+ userId: $userId,
context: $journalContext
);
@@ -63,7 +67,7 @@ public function log($level, string|\Stringable $message, array $context = []): v
}
/**
- * Convert PSR-3 log level to LogLevel enum
+ * Convert PSR-3 log level to LogLevel enum.
*/
private function convertLevel(mixed $level): LogLevel
{
@@ -81,12 +85,10 @@ private function convertLevel(mixed $level): LogLevel
}
/**
- * Create JournalContext from PSR-3 context array
+ * Create Context from PSR-3 context array.
*/
- private function createContext(array $context): JournalContext
+ private function createContext(array $context): Context
{
- $label = $context['label'] ?? 'application.log';
-
$ipAddress = null;
if (isset($context['ipAddress']) && is_string($context['ipAddress'])) {
try {
@@ -96,8 +98,7 @@ private function createContext(array $context): JournalContext
}
}
- return new JournalContext(
- label: $label,
+ return new Context(
payload: $context['payload'] ?? null,
bitrix24UserId: isset($context['bitrix24UserId']) ? (int) $context['bitrix24UserId'] : null,
ipAddress: $ipAddress
diff --git a/src/Journal/Services/JournalLoggerFactory.php b/src/Journal/Services/JournalLoggerFactory.php
index 3b09d5e..eb4f1e8 100644
--- a/src/Journal/Services/JournalLoggerFactory.php
+++ b/src/Journal/Services/JournalLoggerFactory.php
@@ -19,22 +19,22 @@
use Symfony\Component\Uid\Uuid;
/**
- * Factory for creating JournalLogger instances
+ * Factory for creating JournalLogger instances.
*/
readonly class JournalLoggerFactory
{
public function __construct(
private JournalItemRepositoryInterface $repository,
private EntityManagerInterface $entityManager
- ) {
- }
+ ) {}
/**
- * Create logger for specific application installation
+ * Create logger for specific application installation.
*/
- public function createLogger(Uuid $applicationInstallationId): LoggerInterface
+ public function createLogger(string $memberId, Uuid $applicationInstallationId): LoggerInterface
{
return new JournalLogger(
+ memberId: $memberId,
applicationInstallationId: $applicationInstallationId,
repository: $this->repository,
entityManager: $this->entityManager
diff --git a/src/Bitrix24Accounts/ValueObjects/Domain.php b/src/Kernel/ValueObjects/Domain.php
similarity index 97%
rename from src/Bitrix24Accounts/ValueObjects/Domain.php
rename to src/Kernel/ValueObjects/Domain.php
index e172b22..d53fcf3 100644
--- a/src/Bitrix24Accounts/ValueObjects/Domain.php
+++ b/src/Kernel/ValueObjects/Domain.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace Bitrix24\Lib\Bitrix24Accounts\ValueObjects;
+namespace Bitrix24\Lib\Kernel\ValueObjects;
readonly class Domain
{
diff --git a/tests/EntityManagerFactory.php b/tests/EntityManagerFactory.php
index e3935cf..621244e 100644
--- a/tests/EntityManagerFactory.php
+++ b/tests/EntityManagerFactory.php
@@ -7,6 +7,7 @@
use Bitrix24\SDK\Core\Exceptions\WrongConfigurationException;
use Carbon\Doctrine\CarbonImmutableType;
use Doctrine\DBAL\DriverManager;
+use Darsyn\IP\Doctrine\MultiType;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
@@ -66,6 +67,10 @@ public static function get(): EntityManagerInterface
Type::addType('carbon_immutable', CarbonImmutableType::class);
}
+ if (!Type::hasType('ip_address')) {
+ Type::addType('ip_address', MultiType::class);
+ }
+
$configuration = ORMSetup::createXMLMetadataConfiguration($paths, $isDevMode);
$connection = DriverManager::getConnection($connectionParams, $configuration);
diff --git a/tests/Functional/ApplicationInstallations/UseCase/Install/HandlerTest.php b/tests/Functional/ApplicationInstallations/UseCase/Install/HandlerTest.php
index 45846d3..d1a8876 100644
--- a/tests/Functional/ApplicationInstallations/UseCase/Install/HandlerTest.php
+++ b/tests/Functional/ApplicationInstallations/UseCase/Install/HandlerTest.php
@@ -16,7 +16,7 @@
use Bitrix24\Lib\Bitrix24Accounts;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Services\Flusher;
use Bitrix24\Lib\ApplicationInstallations;
use Bitrix24\Lib\Tests\EntityManagerFactory;
diff --git a/tests/Functional/ApplicationInstallations/UseCase/OnAppInstall/HandlerTest.php b/tests/Functional/ApplicationInstallations/UseCase/OnAppInstall/HandlerTest.php
index ea179a5..9ed7384 100644
--- a/tests/Functional/ApplicationInstallations/UseCase/OnAppInstall/HandlerTest.php
+++ b/tests/Functional/ApplicationInstallations/UseCase/OnAppInstall/HandlerTest.php
@@ -16,7 +16,7 @@
use Bitrix24\Lib\Bitrix24Accounts;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Services\Flusher;
use Bitrix24\Lib\ApplicationInstallations;
use Bitrix24\Lib\Tests\EntityManagerFactory;
diff --git a/tests/Functional/ApplicationInstallations/UseCase/Uninstall/HandlerTest.php b/tests/Functional/ApplicationInstallations/UseCase/Uninstall/HandlerTest.php
index 2728053..c3ecbfa 100644
--- a/tests/Functional/ApplicationInstallations/UseCase/Uninstall/HandlerTest.php
+++ b/tests/Functional/ApplicationInstallations/UseCase/Uninstall/HandlerTest.php
@@ -16,7 +16,7 @@
use Bitrix24\Lib\Bitrix24Accounts;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Services\Flusher;
use Bitrix24\Lib\ApplicationInstallations;
use Bitrix24\Lib\Tests\EntityManagerFactory;
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
index 01e2d5a..e9c70e5 100644
--- a/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
+++ b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
@@ -29,7 +29,7 @@
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Uid\Uuid;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
* @internal
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php
index 5d84d7e..66e22a8 100644
--- a/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php
+++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php
@@ -30,7 +30,7 @@
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Uid\Uuid;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
* @internal
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php
index d179e46..578c497 100644
--- a/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php
+++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php
@@ -33,7 +33,7 @@
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Stopwatch\Stopwatch;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
* @internal
diff --git a/tests/Unit/ApplicationInstallations/UseCase/Install/CommandTest.php b/tests/Unit/ApplicationInstallations/UseCase/Install/CommandTest.php
index 0afee06..03c3182 100644
--- a/tests/Unit/ApplicationInstallations/UseCase/Install/CommandTest.php
+++ b/tests/Unit/ApplicationInstallations/UseCase/Install/CommandTest.php
@@ -5,7 +5,7 @@
namespace Bitrix24\Lib\Tests\Unit\ApplicationInstallations\UseCase\Install;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
use Bitrix24\SDK\Application\ApplicationStatus;
use Bitrix24\SDK\Application\PortalLicenseFamily;
diff --git a/tests/Unit/ApplicationInstallations/UseCase/OnAppInstall/CommandTest.php b/tests/Unit/ApplicationInstallations/UseCase/OnAppInstall/CommandTest.php
index d530274..c9b3d26 100644
--- a/tests/Unit/ApplicationInstallations/UseCase/OnAppInstall/CommandTest.php
+++ b/tests/Unit/ApplicationInstallations/UseCase/OnAppInstall/CommandTest.php
@@ -5,7 +5,7 @@
namespace Bitrix24\Lib\Tests\Unit\ApplicationInstallations\UseCase\OnAppInstall;
use Bitrix24\Lib\ApplicationInstallations\UseCase\OnAppInstall\Command;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Tests\Functional\ApplicationInstallations\Builders\ApplicationInstallationBuilder;
use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
use Bitrix24\SDK\Application\ApplicationStatus;
diff --git a/tests/Unit/ApplicationInstallations/UseCase/Uninstall/CommandTest.php b/tests/Unit/ApplicationInstallations/UseCase/Uninstall/CommandTest.php
index 75cc13f..51b7d6a 100644
--- a/tests/Unit/ApplicationInstallations/UseCase/Uninstall/CommandTest.php
+++ b/tests/Unit/ApplicationInstallations/UseCase/Uninstall/CommandTest.php
@@ -5,7 +5,7 @@
namespace Bitrix24\Lib\Tests\Unit\ApplicationInstallations\UseCase\Uninstall;
use Bitrix24\Lib\ApplicationInstallations\UseCase\Uninstall\Command;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
use Bitrix24\SDK\Core\Credentials\Scope;
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php
index f8deb49..8dc5c47 100644
--- a/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php
+++ b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php
@@ -5,7 +5,7 @@
namespace Bitrix24\Lib\Tests\Unit\Bitrix24Accounts\UseCase\ChangeDomainUrl;
use Bitrix24\Lib\Bitrix24Accounts\UseCase\ChangeDomainUrl\Command;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php
index 44559ef..dfc1200 100644
--- a/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php
+++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php
@@ -12,7 +12,7 @@
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Uid\Uuid;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php
index 27a39a4..bf4de15 100644
--- a/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php
+++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php
@@ -14,7 +14,7 @@
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Uid\Uuid;
-use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+use Bitrix24\Lib\Kernel\ValueObjects\Domain;
/**
diff --git a/tests/Unit/Journal/Entity/JournalItemTest.php b/tests/Unit/Journal/Entity/JournalItemTest.php
index 0aeef62..352caac 100644
--- a/tests/Unit/Journal/Entity/JournalItemTest.php
+++ b/tests/Unit/Journal/Entity/JournalItemTest.php
@@ -15,7 +15,7 @@
use Bitrix24\Lib\Journal\Entity\JournalItem;
use Bitrix24\Lib\Journal\Entity\LogLevel;
-use Bitrix24\Lib\Journal\ValueObjects\JournalContext;
+use Bitrix24\Lib\Journal\Entity\ValueObjects\Context;
use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Uid\Uuid;
@@ -24,104 +24,111 @@ class JournalItemTest extends TestCase
{
private Uuid $applicationInstallationId;
+ private string $memberId;
+
+ #[\Override]
protected function setUp(): void
{
$this->applicationInstallationId = Uuid::v7();
+ $this->memberId = 'test-member-id';
}
public function testCreateJournalItemWithInfoLevel(): void
{
$message = 'Test info message';
- $context = new JournalContext(
- label: 'test.label',
+ $label = 'test.label';
+ $userName = 'test-user';
+ $journalContext = new Context(
payload: ['key' => 'value'],
bitrix24UserId: 123
);
- $item = JournalItem::info($this->applicationInstallationId, $message, $context);
-
- $this->assertInstanceOf(JournalItem::class, $item);
- $this->assertSame(LogLevel::info, $item->getLevel());
- $this->assertSame($message, $item->getMessage());
- $this->assertTrue($item->getApplicationInstallationId()->equals($this->applicationInstallationId));
- $this->assertSame('test.label', $item->getContext()->getLabel());
- $this->assertSame(['key' => 'value'], $item->getContext()->getPayload());
- $this->assertSame(123, $item->getContext()->getBitrix24UserId());
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, $message, $label, $userName, $journalContext);
+
+ $this->assertInstanceOf(JournalItem::class, $journalItem);
+ $this->assertSame(LogLevel::info, $journalItem->getLevel());
+ $this->assertSame($this->memberId, $journalItem->getMemberId());
+ $this->assertSame($message, $journalItem->getMessage());
+ $this->assertSame($label, $journalItem->getLabel());
+ $this->assertSame($userName, $journalItem->getUserId());
+ $this->assertTrue($journalItem->getApplicationInstallationId()->equals($this->applicationInstallationId));
+ $this->assertSame(['key' => 'value'], $journalItem->getContext()->getPayload());
+ $this->assertSame(123, $journalItem->getContext()->getBitrix24UserId());
}
public function testCreateJournalItemWithEmergencyLevel(): void
{
- $context = new JournalContext('emergency.label');
- $item = JournalItem::emergency($this->applicationInstallationId, 'Emergency message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::emergency($this->memberId, $this->applicationInstallationId, 'Emergency message', 'emergency.label', null, $journalContext);
- $this->assertSame(LogLevel::emergency, $item->getLevel());
- $this->assertSame('Emergency message', $item->getMessage());
+ $this->assertSame(LogLevel::emergency, $journalItem->getLevel());
+ $this->assertSame('Emergency message', $journalItem->getMessage());
}
public function testCreateJournalItemWithAlertLevel(): void
{
- $context = new JournalContext('alert.label');
- $item = JournalItem::alert($this->applicationInstallationId, 'Alert message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::alert($this->memberId, $this->applicationInstallationId, 'Alert message', 'alert.label', null, $journalContext);
- $this->assertSame(LogLevel::alert, $item->getLevel());
+ $this->assertSame(LogLevel::alert, $journalItem->getLevel());
}
public function testCreateJournalItemWithCriticalLevel(): void
{
- $context = new JournalContext('critical.label');
- $item = JournalItem::critical($this->applicationInstallationId, 'Critical message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::critical($this->memberId, $this->applicationInstallationId, 'Critical message', 'critical.label', null, $journalContext);
- $this->assertSame(LogLevel::critical, $item->getLevel());
+ $this->assertSame(LogLevel::critical, $journalItem->getLevel());
}
public function testCreateJournalItemWithErrorLevel(): void
{
- $context = new JournalContext('error.label');
- $item = JournalItem::error($this->applicationInstallationId, 'Error message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::error($this->memberId, $this->applicationInstallationId, 'Error message', 'error.label', null, $journalContext);
- $this->assertSame(LogLevel::error, $item->getLevel());
+ $this->assertSame(LogLevel::error, $journalItem->getLevel());
}
public function testCreateJournalItemWithWarningLevel(): void
{
- $context = new JournalContext('warning.label');
- $item = JournalItem::warning($this->applicationInstallationId, 'Warning message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::warning($this->memberId, $this->applicationInstallationId, 'Warning message', 'warning.label', null, $journalContext);
- $this->assertSame(LogLevel::warning, $item->getLevel());
+ $this->assertSame(LogLevel::warning, $journalItem->getLevel());
}
public function testCreateJournalItemWithNoticeLevel(): void
{
- $context = new JournalContext('notice.label');
- $item = JournalItem::notice($this->applicationInstallationId, 'Notice message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::notice($this->memberId, $this->applicationInstallationId, 'Notice message', 'notice.label', null, $journalContext);
- $this->assertSame(LogLevel::notice, $item->getLevel());
+ $this->assertSame(LogLevel::notice, $journalItem->getLevel());
}
public function testCreateJournalItemWithDebugLevel(): void
{
- $context = new JournalContext('debug.label');
- $item = JournalItem::debug($this->applicationInstallationId, 'Debug message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::debug($this->memberId, $this->applicationInstallationId, 'Debug message', 'debug.label', null, $journalContext);
- $this->assertSame(LogLevel::debug, $item->getLevel());
+ $this->assertSame(LogLevel::debug, $journalItem->getLevel());
}
public function testJournalItemHasUniqueId(): void
{
- $context = new JournalContext('test.label');
- $item1 = JournalItem::info($this->applicationInstallationId, 'Message 1', $context);
- $item2 = JournalItem::info($this->applicationInstallationId, 'Message 2', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 1', 'test.label', null, $journalContext);
+ $item2 = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 2', 'test.label', null, $journalContext);
- $this->assertNotEquals($item1->getId()->toRfc4122(), $item2->getId()->toRfc4122());
+ $this->assertNotEquals($journalItem->getId()->toRfc4122(), $item2->getId()->toRfc4122());
}
public function testJournalItemHasCreatedAt(): void
{
- $context = new JournalContext('test.label');
- $item = JournalItem::info($this->applicationInstallationId, 'Test message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Test message', 'test.label', null, $journalContext);
- $this->assertNotNull($item->getCreatedAt());
- $this->assertInstanceOf(\Carbon\CarbonImmutable::class, $item->getCreatedAt());
+ $this->assertNotNull($journalItem->getCreatedAt());
+ $this->assertInstanceOf(\Carbon\CarbonImmutable::class, $journalItem->getCreatedAt());
}
public function testCreateJournalItemWithEmptyMessageThrowsException(): void
@@ -129,8 +136,8 @@ public function testCreateJournalItemWithEmptyMessageThrowsException(): void
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Journal message cannot be empty');
- $context = new JournalContext('test.label');
- JournalItem::info($this->applicationInstallationId, '', $context);
+ $journalContext = new Context();
+ JournalItem::info($this->memberId, $this->applicationInstallationId, '', 'test.label', null, $journalContext);
}
public function testCreateJournalItemWithWhitespaceMessageThrowsException(): void
@@ -138,19 +145,28 @@ public function testCreateJournalItemWithWhitespaceMessageThrowsException(): voi
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Journal message cannot be empty');
- $context = new JournalContext('test.label');
- JournalItem::info($this->applicationInstallationId, ' ', $context);
+ $journalContext = new Context();
+ JournalItem::info($this->memberId, $this->applicationInstallationId, ' ', 'test.label', null, $journalContext);
+ }
+
+ public function testCreateJournalItemWithEmptyMemberIdThrowsException(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('memberId cannot be empty');
+
+ $journalContext = new Context();
+ JournalItem::info('', $this->applicationInstallationId, 'Message', 'test.label', null, $journalContext);
}
- public function testJournalItemContextWithOnlyLabel(): void
+ public function testJournalItemContextWithoutLabel(): void
{
- $context = new JournalContext('test.label');
- $item = JournalItem::info($this->applicationInstallationId, 'Test message', $context);
+ $journalContext = new Context();
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Test message', 'test.label', null, $journalContext);
- $this->assertSame('test.label', $item->getContext()->getLabel());
- $this->assertNull($item->getContext()->getPayload());
- $this->assertNull($item->getContext()->getBitrix24UserId());
- $this->assertNull($item->getContext()->getIpAddress());
+ $this->assertSame('test.label', $journalItem->getLabel());
+ $this->assertNull($journalItem->getContext()->getPayload());
+ $this->assertNull($journalItem->getContext()->getBitrix24UserId());
+ $this->assertNull($journalItem->getContext()->getIpAddress());
}
public function testJournalItemWithComplexPayload(): void
@@ -164,13 +180,16 @@ public function testJournalItemWithComplexPayload(): void
],
];
- $context = new JournalContext('sync.label', $payload);
- $item = JournalItem::info(
+ $journalContext = new Context(payload: $payload);
+ $journalItem = JournalItem::info(
+ $this->memberId,
$this->applicationInstallationId,
'Sync completed',
- $context
+ 'sync.label',
+ null,
+ $journalContext
);
- $this->assertSame($payload, $item->getContext()->getPayload());
+ $this->assertSame($payload, $journalItem->getContext()->getPayload());
}
}
diff --git a/tests/Unit/Journal/Entity/LogLevelTest.php b/tests/Unit/Journal/Entity/LogLevelTest.php
index 0b20b42..76e7715 100644
--- a/tests/Unit/Journal/Entity/LogLevelTest.php
+++ b/tests/Unit/Journal/Entity/LogLevelTest.php
@@ -20,50 +20,50 @@ class LogLevelTest extends TestCase
{
public function testFromPsr3LevelEmergency(): void
{
- $level = LogLevel::fromPsr3Level('emergency');
- $this->assertSame(LogLevel::emergency, $level);
+ $logLevel = LogLevel::fromPsr3Level('emergency');
+ $this->assertSame(LogLevel::emergency, $logLevel);
}
public function testFromPsr3LevelAlert(): void
{
- $level = LogLevel::fromPsr3Level('alert');
- $this->assertSame(LogLevel::alert, $level);
+ $logLevel = LogLevel::fromPsr3Level('alert');
+ $this->assertSame(LogLevel::alert, $logLevel);
}
public function testFromPsr3LevelCritical(): void
{
- $level = LogLevel::fromPsr3Level('critical');
- $this->assertSame(LogLevel::critical, $level);
+ $logLevel = LogLevel::fromPsr3Level('critical');
+ $this->assertSame(LogLevel::critical, $logLevel);
}
public function testFromPsr3LevelError(): void
{
- $level = LogLevel::fromPsr3Level('error');
- $this->assertSame(LogLevel::error, $level);
+ $logLevel = LogLevel::fromPsr3Level('error');
+ $this->assertSame(LogLevel::error, $logLevel);
}
public function testFromPsr3LevelWarning(): void
{
- $level = LogLevel::fromPsr3Level('warning');
- $this->assertSame(LogLevel::warning, $level);
+ $logLevel = LogLevel::fromPsr3Level('warning');
+ $this->assertSame(LogLevel::warning, $logLevel);
}
public function testFromPsr3LevelNotice(): void
{
- $level = LogLevel::fromPsr3Level('notice');
- $this->assertSame(LogLevel::notice, $level);
+ $logLevel = LogLevel::fromPsr3Level('notice');
+ $this->assertSame(LogLevel::notice, $logLevel);
}
public function testFromPsr3LevelInfo(): void
{
- $level = LogLevel::fromPsr3Level('info');
- $this->assertSame(LogLevel::info, $level);
+ $logLevel = LogLevel::fromPsr3Level('info');
+ $this->assertSame(LogLevel::info, $logLevel);
}
public function testFromPsr3LevelDebug(): void
{
- $level = LogLevel::fromPsr3Level('debug');
- $this->assertSame(LogLevel::debug, $level);
+ $logLevel = LogLevel::fromPsr3Level('debug');
+ $this->assertSame(LogLevel::debug, $logLevel);
}
public function testFromPsr3LevelCaseInsensitive(): void
@@ -98,7 +98,7 @@ public function testAllLogLevelsExist(): void
$cases = LogLevel::cases();
$this->assertCount(8, $cases);
- $values = array_map(static fn (LogLevel $level): string => $level->value, $cases);
+ $values = array_map(static fn (LogLevel $logLevel): string => $logLevel->value, $cases);
$this->assertContains('emergency', $values);
$this->assertContains('alert', $values);
diff --git a/tests/Unit/Journal/Infrastructure/InMemory/InMemoryJournalItemRepository.php b/tests/Unit/Journal/Infrastructure/InMemory/InMemoryJournalItemRepository.php
index 5b8789b..cdaf183 100644
--- a/tests/Unit/Journal/Infrastructure/InMemory/InMemoryJournalItemRepository.php
+++ b/tests/Unit/Journal/Infrastructure/InMemory/InMemoryJournalItemRepository.php
@@ -20,7 +20,7 @@
use Symfony\Component\Uid\Uuid;
/**
- * In-memory implementation of JournalItemRepository for testing
+ * In-memory implementation of JournalItemRepository for testing.
*/
class InMemoryJournalItemRepository implements JournalItemRepositoryInterface
{
@@ -36,9 +36,9 @@ public function save(JournalItemInterface $journalItem): void
}
#[\Override]
- public function findById(Uuid $id): ?JournalItemInterface
+ public function findById(Uuid $uuid): ?JournalItemInterface
{
- return $this->items[$id->toRfc4122()] ?? null;
+ return $this->items[$uuid->toRfc4122()] ?? null;
}
/**
@@ -46,19 +46,24 @@ public function findById(Uuid $id): ?JournalItemInterface
*/
#[\Override]
public function findByApplicationInstallationId(
+ string $memberId,
Uuid $applicationInstallationId,
- ?LogLevel $level = null,
+ ?LogLevel $logLevel = null,
?int $limit = null,
?int $offset = null
): array {
$filtered = array_filter(
$this->items,
- static function (JournalItemInterface $item) use ($applicationInstallationId, $level): bool {
- if (!$item->getApplicationInstallationId()->equals($applicationInstallationId)) {
+ static function (JournalItemInterface $journalItem) use ($applicationInstallationId, $memberId, $logLevel): bool {
+ if ($journalItem->getMemberId() !== $memberId) {
return false;
}
- if ($level !== null && $item->getLevel() !== $level) {
+ if (!$journalItem->getApplicationInstallationId()->equals($applicationInstallationId)) {
+ return false;
+ }
+
+ if (null !== $logLevel && $journalItem->getLevel() !== $logLevel) {
return false;
}
@@ -67,41 +72,70 @@ static function (JournalItemInterface $item) use ($applicationInstallationId, $l
);
// Sort by created date descending
- usort($filtered, static function (JournalItemInterface $a, JournalItemInterface $b): int {
- return $b->getCreatedAt()->getTimestamp() <=> $a->getCreatedAt()->getTimestamp();
- });
+ usort($filtered, static fn (JournalItemInterface $a, JournalItemInterface $b): int => $b->getCreatedAt()->getTimestamp() <=> $a->getCreatedAt()->getTimestamp());
- if ($offset !== null) {
+ if (null !== $offset) {
$filtered = array_slice($filtered, $offset);
}
- if ($limit !== null) {
- $filtered = array_slice($filtered, 0, $limit);
+ if (null !== $limit) {
+ return array_slice($filtered, 0, $limit);
}
return $filtered;
}
+ /**
+ * @return JournalItemInterface[]
+ */
#[\Override]
- public function deleteByApplicationInstallationId(Uuid $applicationInstallationId): int
- {
- $count = 0;
- foreach ($this->items as $key => $item) {
- if ($item->getApplicationInstallationId()->equals($applicationInstallationId)) {
- unset($this->items[$key]);
- ++$count;
+ public function findByMemberId(
+ string $memberId,
+ ?LogLevel $logLevel = null,
+ ?int $limit = null,
+ ?int $offset = null
+ ): array {
+ $filtered = array_filter(
+ $this->items,
+ static function (JournalItemInterface $journalItem) use ($memberId, $logLevel): bool {
+ if ($journalItem->getMemberId() !== $memberId) {
+ return false;
+ }
+
+ if (null !== $logLevel && $journalItem->getLevel() !== $logLevel) {
+ return false;
+ }
+
+ return true;
}
+ );
+
+ // Sort by created date descending
+ usort($filtered, static fn (JournalItemInterface $a, JournalItemInterface $b): int => $b->getCreatedAt()->getTimestamp() <=> $a->getCreatedAt()->getTimestamp());
+
+ if (null !== $offset) {
+ $filtered = array_slice($filtered, $offset);
}
- return $count;
+ if (null !== $limit) {
+ return array_slice($filtered, 0, $limit);
+ }
+
+ return $filtered;
}
#[\Override]
- public function deleteOlderThan(CarbonImmutable $date): int
- {
+ public function deleteOlderThan(
+ string $memberId,
+ Uuid $applicationInstallationId,
+ CarbonImmutable $date
+ ): int {
$count = 0;
foreach ($this->items as $key => $item) {
- if ($item->getCreatedAt()->isBefore($date)) {
+ if ($item->getMemberId() === $memberId
+ && $item->getApplicationInstallationId()->equals($applicationInstallationId)
+ && $item->getCreatedAt()->isBefore($date)
+ ) {
unset($this->items[$key]);
++$count;
}
@@ -110,14 +144,8 @@ public function deleteOlderThan(CarbonImmutable $date): int
return $count;
}
- #[\Override]
- public function countByApplicationInstallationId(Uuid $applicationInstallationId, ?LogLevel $level = null): int
- {
- return count($this->findByApplicationInstallationId($applicationInstallationId, $level));
- }
-
/**
- * Get all items (for testing purposes)
+ * Get all items (for testing purposes).
*
* @return JournalItemInterface[]
*/
@@ -127,7 +155,7 @@ public function findAll(): array
}
/**
- * Clear all items (for testing purposes)
+ * Clear all items (for testing purposes).
*/
public function clear(): void
{
diff --git a/tests/Unit/Journal/Infrastructure/InMemoryJournalItemRepositoryTest.php b/tests/Unit/Journal/Infrastructure/InMemoryJournalItemRepositoryTest.php
index 19c3d52..86c3b21 100644
--- a/tests/Unit/Journal/Infrastructure/InMemoryJournalItemRepositoryTest.php
+++ b/tests/Unit/Journal/Infrastructure/InMemoryJournalItemRepositoryTest.php
@@ -15,36 +15,45 @@
use Bitrix24\Lib\Journal\Entity\JournalItem;
use Bitrix24\Lib\Journal\Entity\LogLevel;
-use Bitrix24\Lib\Journal\ValueObjects\JournalContext;
+use Bitrix24\Lib\Journal\Entity\ValueObjects\Context;
use Bitrix24\Lib\Tests\Unit\Journal\Infrastructure\InMemory\InMemoryJournalItemRepository;
use Carbon\CarbonImmutable;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Uid\Uuid;
+/**
+ * @internal
+ *
+ * @coversNothing
+ */
class InMemoryJournalItemRepositoryTest extends TestCase
{
private InMemoryJournalItemRepository $repository;
private Uuid $applicationInstallationId;
+ private string $memberId;
+
+ #[\Override]
protected function setUp(): void
{
$this->repository = new InMemoryJournalItemRepository();
$this->applicationInstallationId = Uuid::v7();
+ $this->memberId = 'test-member-id';
}
public function testSaveAndFindById(): void
{
- $context = new JournalContext('test.label');
- $item = JournalItem::info($this->applicationInstallationId, 'Test message', $context);
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Test message', 'test.label', null, $journalContext);
- $this->repository->save($item);
+ $this->repository->save($journalItem);
- $found = $this->repository->findById($item->getId());
+ $found = $this->repository->findById($journalItem->getId());
$this->assertNotNull($found);
- $this->assertSame($item->getId()->toRfc4122(), $found->getId()->toRfc4122());
- $this->assertSame($item->getMessage(), $found->getMessage());
+ $this->assertSame($journalItem->getId()->toRfc4122(), $found->getId()->toRfc4122());
+ $this->assertSame($journalItem->getMessage(), $found->getMessage());
}
public function testFindByIdReturnsNullForNonexistent(): void
@@ -56,34 +65,35 @@ public function testFindByIdReturnsNullForNonexistent(): void
public function testFindByApplicationInstallationId(): void
{
- $context = new JournalContext('test.label');
- $item1 = JournalItem::info($this->applicationInstallationId, 'Message 1', $context);
- $item2 = JournalItem::error($this->applicationInstallationId, 'Message 2', $context);
- $item3 = JournalItem::info(Uuid::v7(), 'Message 3', $context); // Different installation
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 1', 'test.label', null, $journalContext);
+ $item2 = JournalItem::error($this->memberId, $this->applicationInstallationId, 'Message 2', 'test.label', null, $journalContext);
+ $item3 = JournalItem::info('other-member', Uuid::v7(), 'Message 3', 'test.label', null, $journalContext); // Different installation
- $this->repository->save($item1);
+ $this->repository->save($journalItem);
$this->repository->save($item2);
$this->repository->save($item3);
- $items = $this->repository->findByApplicationInstallationId($this->applicationInstallationId);
+ $items = $this->repository->findByApplicationInstallationId($this->memberId, $this->applicationInstallationId);
$this->assertCount(2, $items);
}
public function testFindByApplicationInstallationIdWithLevelFilter(): void
{
- $context = new JournalContext('test.label');
- $item1 = JournalItem::info($this->applicationInstallationId, 'Message 1', $context);
- $item2 = JournalItem::error($this->applicationInstallationId, 'Message 2', $context);
- $item3 = JournalItem::info($this->applicationInstallationId, 'Message 3', $context);
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 1', 'test.label', null, $journalContext);
+ $item2 = JournalItem::error($this->memberId, $this->applicationInstallationId, 'Message 2', 'test.label', null, $journalContext);
+ $item3 = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 3', 'test.label', null, $journalContext);
- $this->repository->save($item1);
+ $this->repository->save($journalItem);
$this->repository->save($item2);
$this->repository->save($item3);
$items = $this->repository->findByApplicationInstallationId(
+ $this->memberId,
$this->applicationInstallationId,
- LogLevel::info
+ logLevel: LogLevel::info
);
$this->assertCount(2, $items);
@@ -92,15 +102,35 @@ public function testFindByApplicationInstallationIdWithLevelFilter(): void
}
}
+ public function testFindByMemberId(): void
+ {
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 1', 'test.label', null, $journalContext);
+ $item2 = JournalItem::error($this->memberId, $this->applicationInstallationId, 'Message 2', 'test.label', null, $journalContext);
+ $item3 = JournalItem::info('other-member', Uuid::v7(), 'Message 3', 'test.label', null, $journalContext);
+
+ $this->repository->save($journalItem);
+ $this->repository->save($item2);
+ $this->repository->save($item3);
+
+ $items = $this->repository->findByMemberId($this->memberId);
+
+ $this->assertCount(2, $items);
+ foreach ($items as $item) {
+ $this->assertSame($this->memberId, $item->getMemberId());
+ }
+ }
+
public function testFindByApplicationInstallationIdWithLimit(): void
{
- $context = new JournalContext('test.label');
+ $journalContext = new Context(['key' => 'value']);
for ($i = 1; $i <= 5; ++$i) {
- $item = JournalItem::info($this->applicationInstallationId, "Message {$i}", $context);
+ $item = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message '.$i, 'test.label', null, $journalContext);
$this->repository->save($item);
}
$items = $this->repository->findByApplicationInstallationId(
+ $this->memberId,
$this->applicationInstallationId,
limit: 3
);
@@ -110,13 +140,14 @@ public function testFindByApplicationInstallationIdWithLimit(): void
public function testFindByApplicationInstallationIdWithOffset(): void
{
- $context = new JournalContext('test.label');
+ $journalContext = new Context(['key' => 'value']);
for ($i = 1; $i <= 5; ++$i) {
- $item = JournalItem::info($this->applicationInstallationId, "Message {$i}", $context);
+ $item = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message '.$i, 'test.label', null, $journalContext);
$this->repository->save($item);
}
$items = $this->repository->findByApplicationInstallationId(
+ $this->memberId,
$this->applicationInstallationId,
offset: 2
);
@@ -126,13 +157,14 @@ public function testFindByApplicationInstallationIdWithOffset(): void
public function testFindByApplicationInstallationIdWithLimitAndOffset(): void
{
- $context = new JournalContext('test.label');
+ $journalContext = new Context(['key' => 'value']);
for ($i = 1; $i <= 10; ++$i) {
- $item = JournalItem::info($this->applicationInstallationId, "Message {$i}", $context);
+ $item = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message '.$i, 'test.label', null, $journalContext);
$this->repository->save($item);
}
$items = $this->repository->findByApplicationInstallationId(
+ $this->memberId,
$this->applicationInstallationId,
limit: 3,
offset: 2
@@ -141,71 +173,24 @@ public function testFindByApplicationInstallationIdWithLimitAndOffset(): void
$this->assertCount(3, $items);
}
- public function testDeleteByApplicationInstallationId(): void
- {
- $context = new JournalContext('test.label');
- $item1 = JournalItem::info($this->applicationInstallationId, 'Message 1', $context);
- $item2 = JournalItem::info($this->applicationInstallationId, 'Message 2', $context);
- $otherInstallationId = Uuid::v7();
- $item3 = JournalItem::info($otherInstallationId, 'Message 3', $context);
-
- $this->repository->save($item1);
- $this->repository->save($item2);
- $this->repository->save($item3);
-
- $deleted = $this->repository->deleteByApplicationInstallationId($this->applicationInstallationId);
-
- $this->assertSame(2, $deleted);
- $this->assertEmpty($this->repository->findByApplicationInstallationId($this->applicationInstallationId));
- $this->assertCount(1, $this->repository->findByApplicationInstallationId($otherInstallationId));
- }
-
public function testDeleteOlderThan(): void
{
- $context = new JournalContext('test.label');
- $item = JournalItem::info($this->applicationInstallationId, 'Message', $context);
- $this->repository->save($item);
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message', 'test.label', null, $journalContext);
+ $this->repository->save($journalItem);
$futureDate = new CarbonImmutable('+1 day');
- $deleted = $this->repository->deleteOlderThan($futureDate);
+ $deleted = $this->repository->deleteOlderThan($this->memberId, $this->applicationInstallationId, $futureDate);
// Item should be deleted as it's older than future date
$this->assertSame(1, $deleted);
}
- public function testCountByApplicationInstallationId(): void
- {
- $context = new JournalContext('test.label');
- for ($i = 1; $i <= 5; ++$i) {
- $item = JournalItem::info($this->applicationInstallationId, "Message {$i}", $context);
- $this->repository->save($item);
- }
-
- $count = $this->repository->countByApplicationInstallationId($this->applicationInstallationId);
-
- $this->assertSame(5, $count);
- }
-
- public function testCountByApplicationInstallationIdWithLevelFilter(): void
- {
- $context = new JournalContext('test.label');
- $this->repository->save(JournalItem::info($this->applicationInstallationId, 'Info 1', $context));
- $this->repository->save(JournalItem::info($this->applicationInstallationId, 'Info 2', $context));
- $this->repository->save(JournalItem::error($this->applicationInstallationId, 'Error 1', $context));
-
- $count = $this->repository->countByApplicationInstallationId(
- $this->applicationInstallationId,
- LogLevel::info
- );
-
- $this->assertSame(2, $count);
- }
-
public function testClear(): void
{
- $context = new JournalContext('test.label');
- $item = JournalItem::info($this->applicationInstallationId, 'Message', $context);
- $this->repository->save($item);
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message', 'test.label', null, $journalContext);
+ $this->repository->save($journalItem);
$this->assertNotEmpty($this->repository->findAll());
@@ -216,11 +201,11 @@ public function testClear(): void
public function testFindAll(): void
{
- $context = new JournalContext('test.label');
- $item1 = JournalItem::info($this->applicationInstallationId, 'Message 1', $context);
- $item2 = JournalItem::error(Uuid::v7(), 'Message 2', $context);
+ $journalContext = new Context(['key' => 'value']);
+ $journalItem = JournalItem::info($this->memberId, $this->applicationInstallationId, 'Message 1', 'test.label', null, $journalContext);
+ $item2 = JournalItem::error('other-member', Uuid::v7(), 'Message 2', 'test.label', null, $journalContext);
- $this->repository->save($item1);
+ $this->repository->save($journalItem);
$this->repository->save($item2);
$all = $this->repository->findAll();
diff --git a/tests/Unit/Journal/Services/JournalLoggerTest.php b/tests/Unit/Journal/Services/JournalLoggerTest.php
index 66ad4ea..98b207e 100644
--- a/tests/Unit/Journal/Services/JournalLoggerTest.php
+++ b/tests/Unit/Journal/Services/JournalLoggerTest.php
@@ -28,15 +28,20 @@ class JournalLoggerTest extends TestCase
private Uuid $applicationInstallationId;
+ private string $memberId;
+
private JournalLogger $logger;
+ #[\Override]
protected function setUp(): void
{
$this->repository = new InMemoryJournalItemRepository();
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->applicationInstallationId = Uuid::v7();
+ $this->memberId = 'test-member-id';
$this->logger = new JournalLogger(
+ $this->memberId,
$this->applicationInstallationId,
$this->repository,
$this->entityManager
@@ -47,13 +52,15 @@ public function testLogInfoMessage(): void
{
$this->entityManager->expects($this->once())->method('flush');
- $this->logger->info('Test info message', ['label' => 'test.label']);
+ $this->logger->info('Test info message', ['label' => 'test.label', 'userId' => 'test-user']);
$items = $this->repository->findAll();
$this->assertCount(1, $items);
$this->assertSame(LogLevel::info, $items[0]->getLevel());
+ $this->assertSame($this->memberId, $items[0]->getMemberId());
$this->assertSame('Test info message', $items[0]->getMessage());
- $this->assertSame('test.label', $items[0]->getContext()->getLabel());
+ $this->assertSame('test.label', $items[0]->getLabel());
+ $this->assertSame('test-user', $items[0]->getUserId());
}
public function testLogErrorMessage(): void
@@ -65,7 +72,7 @@ public function testLogErrorMessage(): void
$items = $this->repository->findAll();
$this->assertCount(1, $items);
$this->assertSame(LogLevel::error, $items[0]->getLevel());
- $this->assertSame('error.label', $items[0]->getContext()->getLabel());
+ $this->assertSame('error.label', $items[0]->getLabel());
}
public function testLogWarningMessage(): void
@@ -144,7 +151,7 @@ public function testLogWithContext(): void
$items = $this->repository->findAll();
$item = $items[0];
- $this->assertSame('test.label', $item->getContext()->getLabel());
+ $this->assertSame('test.label', $item->getLabel());
$this->assertSame(['key' => 'value'], $item->getContext()->getPayload());
$this->assertSame(123, $item->getContext()->getBitrix24UserId());
$this->assertNotNull($item->getContext()->getIpAddress());
@@ -157,7 +164,7 @@ public function testLogWithoutLabelUsesDefault(): void
$this->logger->info('Test message without label');
$items = $this->repository->findAll();
- $this->assertSame('application.log', $items[0]->getContext()->getLabel());
+ $this->assertSame('application.log', $items[0]->getLabel());
}
public function testLogMultipleMessages(): void