diff --git a/.allowed-licenses.php b/.allowed-licenses.php
new file mode 100644
index 0000000..3b28e83
--- /dev/null
+++ b/.allowed-licenses.php
@@ -0,0 +1,14 @@
+addLicenses(
+ // And other licenses you wish to allow.
+ 'MIT',
+ 'Apache-2.0',
+ 'BSD-3-Clause',
+ )
+ ->build();
\ No newline at end of file
diff --git a/.env b/.env
new file mode 100644
index 0000000..f9de590
--- /dev/null
+++ b/.env
@@ -0,0 +1,18 @@
+# In all environments, the following files are loaded if they exist,
+# the latter taking precedence over the former:
+#
+# * .env contains default values for the environment variables needed by the app
+# * .env.local uncommitted file with local overrides
+# * .env.$APP_ENV committed environment-specific defaults
+# * .env.$APP_ENV.local uncommitted environment-specific overrides
+#
+# Real environment variables win over .env files.
+#
+# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
+#
+# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
+# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
+DATABASE_HOST=
+DATABASE_USER=
+DATABASE_PASSWORD=
+DATABASE_NAME=
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/config.yml
rename to .github/ISSUE_TEMPLATE/config.yml
diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml
new file mode 100644
index 0000000..f628913
--- /dev/null
+++ b/.github/workflows/license-check.yml
@@ -0,0 +1,50 @@
+name: "Allowed licenses checks"
+on:
+ push:
+ pull_request:
+
+jobs:
+ static-analysis:
+ name: "composer-license-checker"
+ runs-on: ${{ matrix.operating-system }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "8.3"
+ dependencies: [ highest ]
+ operating-system: [ ubuntu-latest]
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ extensions: json, bcmath, curl, intl, mbstring
+ tools: composer:v2
+
+ - name: "Install lowest dependencies"
+ if: ${{ matrix.dependencies == 'lowest' }}
+ run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+
+ - name: "Install highest dependencies"
+ if: ${{ matrix.dependencies == 'highest' }}
+ run: "composer update --no-interaction --no-progress --no-suggest"
+
+ - name: "composer-license-checker"
+ run: "make lint-allowed-licenses"
+
+ - name: "is allowed licenses check succeeded"
+ if: ${{ success() }}
+ run: |
+ echo '✅ allowed licenses check pass, congratulations!'
+
+ - name: "is allowed licenses check failed"
+ if: ${{ failure() }}
+ run: |
+ echo '::error:: ❗️ allowed licenses check failed (╯°益°)╯彡┻━┻'
\ No newline at end of file
diff --git a/.github/workflows/lint-cs-fixer.yml b/.github/workflows/lint-cs-fixer.yml
new file mode 100644
index 0000000..6d2fd84
--- /dev/null
+++ b/.github/workflows/lint-cs-fixer.yml
@@ -0,0 +1,51 @@
+on:
+ push:
+ pull_request:
+
+name: Lint CS-Fixer
+
+jobs:
+ static-analysis:
+ name: "CS-Fixer"
+ runs-on: ${{ matrix.operating-system }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "8.3"
+ dependencies: [ highest ]
+ operating-system: [ ubuntu-latest]
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ extensions: json, bcmath, curl, intl, mbstring
+ tools: composer:v2
+
+ - name: "Install lowest dependencies"
+ if: ${{ matrix.dependencies == 'lowest' }}
+ run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest"
+
+ - name: "Install highest dependencies"
+ if: ${{ matrix.dependencies == 'highest' }}
+ run: "composer update --no-interaction --no-progress --no-suggest"
+
+ - name: "CS-Fixer"
+ run: "vendor/bin/php-cs-fixer fix --dry-run --diff --verbose"
+
+ - name: "is CS-Fixer check succeeded"
+ if: ${{ success() }}
+ run: |
+ echo '✅ CS-Fixer check pass, congratulations!'
+
+ - name: "is CS-Fixer check failed"
+ if: ${{ failure() }}
+ run: |
+ echo '::error:: ❗️ CS-Fixer check failed (╯°益°)╯彡┻━┻'
\ No newline at end of file
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/lint-phpstan.yml
similarity index 95%
rename from .github/workflows/phpstan.yml
rename to .github/workflows/lint-phpstan.yml
index 03760bc..9cf8ab2 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/lint-phpstan.yml
@@ -38,7 +38,7 @@ jobs:
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "PHPStan"
- run: "make lint-phpstan"
+ run: "vendor/bin/phpstan --memory-limit=2G analyse"
- name: "is PHPStan check succeeded"
if: ${{ success() }}
diff --git a/.github/workflows/rector.yml b/.github/workflows/lint-rector.yml
similarity index 96%
rename from .github/workflows/rector.yml
rename to .github/workflows/lint-rector.yml
index 4a0bf8a..ec35b52 100644
--- a/.github/workflows/rector.yml
+++ b/.github/workflows/lint-rector.yml
@@ -38,7 +38,7 @@ jobs:
run: "composer update --no-interaction --no-progress --no-suggest"
- name: "Rector"
- run: "make lint-rector"
+ run: "vendor/bin/rector process --dry-run"
- name: "is Rector check succeeded"
if: ${{ success() }}
diff --git a/.github/workflows/tests-functional.yml b/.github/workflows/tests-functional.yml
new file mode 100644
index 0000000..b17a851
--- /dev/null
+++ b/.github/workflows/tests-functional.yml
@@ -0,0 +1,86 @@
+name: "Functional tests"
+
+on:
+ push:
+ pull_request:
+
+env:
+ COMPOSER_FLAGS: "--ansi --no-interaction --no-progress"
+ DATABASE_HOST: localhost
+ DATABASE_USER: b24phpLibTest
+ DATABASE_PASSWORD: b24phpLibTest
+ DATABASE_NAME: b24phpLibTest
+
+jobs:
+ tests:
+ name: "Functional tests"
+
+ runs-on: ${{ matrix.operating-system }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "8.3"
+ dependencies: [ highest ]
+ operating-system: [ ubuntu-latest ]
+ services:
+ bitrix24-php-lib-test-database:
+ image: postgres:16-alpine
+ ports:
+ - 5432:5432
+ options: >-
+ --health-cmd="pg_isready -U b24phpLibTest"
+ --health-interval=10s
+ --health-timeout=5s
+ --health-retries=5
+ env:
+ POSTGRES_USER: b24phpLibTest
+ POSTGRES_PASSWORD: b24phpLibTest
+ POSTGRES_DB: b24phpLibTest
+
+ steps:
+ - name: "Checkout code"
+ uses: "actions/checkout@v2"
+
+ - name: "Setup PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ extensions: json, bcmath, curl, intl, mbstring, pdo_pgsql, pdo
+
+ - name: "Install dependencies with Composer"
+ run: |
+ composer update ${{ env.COMPOSER_FLAGS }}
+
+ - name: "Install PostgreSQL client"
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y postgresql-client
+
+ - name: "Wait for PostgreSQL to be ready"
+ run: |
+ until pg_isready -h localhost -p 5432 -U b24phpLibTest; do
+ echo "Waiting for PostgreSQL to start..."
+ sleep 2
+ done
+
+ - name: "Run functional tests"
+ run: |
+ php bin/doctrine orm:schema-tool:drop --force
+ php bin/doctrine orm:schema-tool:create --dump-sql
+ php bin/doctrine orm:schema-tool:update --force
+ php bin/doctrine orm:info
+ # Запуск тестов с очисткой состояния между тестами
+ php vendor/bin/phpunit --testsuite=functional_tests --display-warnings --testdox --process-isolation
+
+ - name: "is functional tests succeeded"
+ if: ${{ success() }}
+ run: |
+ echo '✅ functional tests pass, congratulations!'
+
+ - name: "is functional tests failed"
+ if: ${{ failure() }}
+ run: |
+ echo '::error:: ❗️ functional tests failed (╯°益°)╯彡┻━┻'
diff --git a/.github/workflows/phpunit.yml b/.github/workflows/tests-unit.yml
similarity index 86%
rename from .github/workflows/phpunit.yml
rename to .github/workflows/tests-unit.yml
index 32152d5..c714207 100644
--- a/.github/workflows/phpunit.yml
+++ b/.github/workflows/tests-unit.yml
@@ -1,11 +1,11 @@
-name: "PHPUnit tests"
+name: "Unit tests"
on:
push:
pull_request:
env:
- COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist"
+ COMPOSER_FLAGS: "--ansi --no-interaction --no-progress"
jobs:
tests:
@@ -37,7 +37,7 @@ jobs:
composer update ${{ env.COMPOSER_FLAGS }}
- name: "run unit tests"
- run: "make test-unit"
+ run: "php vendor/bin/phpunit --testsuite=unit_tests --display-warnings --testdox"
- name: "is unit tests tests succeeded"
if: ${{ success() }}
diff --git a/.gitignore b/.gitignore
index 66c3dc1..b5709f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,10 @@
/.idea*
/vendor
/.cache
+/docker/db
composer.phar
composer.lock
.phpunit.result.cache
*.log
-.env.local
\ No newline at end of file
+.env.local
+*.cache
\ No newline at end of file
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..3508ea9
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,20 @@
+in(__DIR__.'/src');
+
+return (new Config())
+ ->setParallelConfig(ParallelConfigFactory::detect())
+ ->setCacheFile(__DIR__.'/.php-cs-fixer.cache')
+ ->setFinder($finder)
+ ->setRules([
+ '@Symfony' => true,
+ '@DoctrineAnnotation' => true,
+ '@PhpCsFixer' => true,
+ 'phpdoc_to_comment' => false,
+ ]);
diff --git a/Makefile b/Makefile
index a1f253a..5927f0a 100644
--- a/Makefile
+++ b/Makefile
@@ -4,22 +4,123 @@
#
# For the full copyright and license information, please view the MIT-LICENSE.txt
# file that was distributed with this source code.
+#!/usr/bin/env make
+
+export COMPOSE_HTTP_TIMEOUT=120
+export DOCKER_CLIENT_TIMEOUT=120
+
+# load default and personal env-variables
+ENV := $(PWD)/.env
+ENV_LOCAL := $(PWD)/.env.local
+include $(ENV)
+-include $(ENV_LOCAL)
+
+
+start-rector: vendor
+ vendor/bin/rector process tests --config=rector.php
+
+coding-standards: vendor
+ vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --diff --verbose
default:
@echo "make needs target:"
@egrep -e '^\S+' ./Makefile | grep -v default | sed -r 's/://' | sed -r 's/^/ - /'
+%:
+ @: # silence
+
+# Rule to print all environment variables for debugging
+debug-print-env:
+ @echo "DATABASE_HOST=$(DATABASE_HOST)"
+ @echo "DATABASE_NAME=$(DATABASE_NAME)"
+ @echo "DATABASE_USER=$(DATABASE_USER)"
+ @echo "DATABASE_PASSWORD=$(DATABASE_PASSWORD)"
+
+init:
+ @echo "remove all containers"
+ docker-compose down --remove-orphans
+ @echo "build containers"
+ docker-compose build
+ @echo "install dependencies"
+ docker-compose run --rm php-cli composer install
+ @echo "change owner of var folder for access from container"
+ docker-compose run --rm php-cli chown -R www-data:www-data /var/www/html/var/
+ @echo "run application…"
+ docker-compose up -d
+
+
+clear:
+ docker-compose run --rm php-cli composer clear-cache
+
+up:
+ @echo "run application…"
+ docker-compose up --build -d
+
+down:
+ @echo "stop application and remove containers"
+ docker-compose down --remove-orphans
+
+down-clear:
+ @echo "stop application and remove containers with volumes"
+ docker-compose down -v --remove-orphans
+
+restart: down up
+
+# работа с контейнерами
+php-cli-bash:
+ docker-compose run --rm php-cli sh $(filter-out $@,$(MAKECMDGOALS))
+
+# работа с composer
+composer-install:
+ @echo "install dependencies…"
+ docker-compose run --rm php-cli composer install
+
+composer-update:
+ @echo "update dependencies…"
+ docker-compose run --rm php-cli composer update
+
+composer-dumpautoload:
+ docker-compose run --rm php-cli composer dumpautoload
+# вызов composer с любыми параметрами
+# Примеры:
+# make composer install
+# make composer "install --no-dev"
+composer:
+ docker-compose run --rm php-cli composer $(filter-out $@,$(MAKECMDGOALS))
+
+# check allowed licenses
+lint-allowed-licenses:
+ vendor/bin/composer-license-checker
# linters
lint-phpstan:
- vendor/bin/phpstan --memory-limit=1G analyse
+ docker-compose run --rm php-cli php vendor/bin/phpstan analyse --memory-limit 2G
lint-rector:
- vendor/bin/rector process --dry-run
+ docker-compose run --rm php-cli php vendor/bin/rector process --dry-run
lint-rector-fix:
- vendor/bin/rector process
+ docker-compose run --rm php-cli php vendor/bin/rector process
+lint-cs-fixer:
+ docker-compose run --rm php-cli php vendor/bin/php-cs-fixer fix --dry-run --diff --verbose
+lint-cs-fixer-fix:
+ docker-compose run --rm php-cli php vendor/bin/php-cs-fixer fix --diff --verbose
+
+# unit-tests
+test-run-unit:
+ docker-compose run --rm php-cli php vendor/bin/phpunit --testsuite=unit_tests --display-warnings --testdox
+
+# functional-tests, work with test database
+test-run-functional: debug-print-env
+ docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:drop --force
+ docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:create
+ docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:update --dump-sql
+ docker-compose run --rm php-cli php vendor/bin/phpunit --testsuite=functional_tests --display-warnings --testdox
+
+# Запустить один функциональный тест с дебагером
+run-one-functional-test: debug-print-env
+ docker-compose run --rm php-cli php -dxdebug.start_with_request=yes vendor/bin/phpunit --filter 'testChangeDomainUrlWithHappyPath' tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
+
+schema-drop:
+ docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:drop --force
-# unit tests
-test-unit:
- vendor/bin/phpunit --testsuite unit_tests --display-warnings
+schema-create:
+ docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:create
-test-integration:
- vendor/bin/phpunit --testsuite integration_tests --display-warnings
\ No newline at end of file
diff --git a/bin/doctrine b/bin/doctrine
new file mode 100644
index 0000000..cf20dbe
--- /dev/null
+++ b/bin/doctrine
@@ -0,0 +1,12 @@
+#!/usr/bin/env php
+ 'pdo_pgsql',
+ 'memory' => true,
+ 'dbname' => $_ENV['DATABASE_NAME'],
+ ]);
+
+return DependencyFactory::fromConnection($config, new ExistingConnection($conn));
\ No newline at end of file
diff --git a/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml b/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml
new file mode 100644
index 0000000..c2e57d9
--- /dev/null
+++ b/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml b/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml
new file mode 100644
index 0000000..b229a88
--- /dev/null
+++ b/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml b/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml
new file mode 100644
index 0000000..0df218b
--- /dev/null
+++ b/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..fbdff87
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,28 @@
+services:
+ php-cli:
+ build:
+ context: ./docker/php-cli
+ depends_on:
+ - database
+ volumes:
+ - .:/var/www/html
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
+ environment:
+ PHP_IDE_CONFIG: "serverName=docker-cli"
+ working_dir: /var/www/html
+ database:
+ image: postgres:${POSTGRES_VERSION:-16}-alpine
+ restart: unless-stopped
+ environment:
+ POSTGRES_DB: ${POSTGRES_DB:-b24phpLibTest}
+ # You should definitely change the password in production
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-b24phpLibTest}
+ POSTGRES_USER: ${POSTGRES_USER:-b24phpLibTest}
+ PGDATA: "/var/lib/postgresql/data/pgdata"
+ container_name: bitrix24-php-lib-test-database
+ ports:
+ - '5438:5432'
+ volumes:
+ - ./docker/init_database/:/docker-entrypoint-initdb.d
+ - ./docker/db:/var/lib/postgresql/data
diff --git a/docker/php-cli/Dockerfile b/docker/php-cli/Dockerfile
new file mode 100644
index 0000000..a154dfd
--- /dev/null
+++ b/docker/php-cli/Dockerfile
@@ -0,0 +1,17 @@
+FROM php:8.3-cli-alpine
+
+RUN apk add unzip libpq-dev git icu-dev autoconf build-base linux-headers \
+ && docker-php-ext-install bcmath pdo pdo_pgsql intl \
+ && docker-php-ext-enable bcmath pdo pdo_pgsql intl
+
+RUN pecl install xdebug \
+ && docker-php-ext-enable xdebug \
+ && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.client_host = host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.client_port = 9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+
+RUN git config --global --add safe.directory /var/www/html
+
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/bin --filename=composer --quiet
+
+ENV COMPOSER_ALLOW_SUPERUSER=1
diff --git a/migrations.php b/migrations.php
new file mode 100644
index 0000000..73e85a8
--- /dev/null
+++ b/migrations.php
@@ -0,0 +1,20 @@
+ [
+ 'table_name' => 'b24AccountsDoctrineMigrationVersions',
+ 'version_column_name' => 'version',
+ 'version_column_length' => 1024,
+ 'executed_at_column_name' => 'executed_at',
+ 'execution_time_column_name' => 'execution_time',
+ ],
+ 'migrations_paths' => [
+ 'B24io\Core\Bitrix24\Account\Migrations' => '/migrations/',
+ ],
+ 'all_or_nothing' => true,
+ 'transactional' => true,
+ 'check_database_platform' => true,
+ 'organize_migrations' => 'none',
+ 'connection' => null,
+ 'em' => null,
+];
\ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 0f572c3..fee6376 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -10,8 +10,8 @@
./tests/Unit
-
- ./tests/Integration
+
+ ./tests/Functional
diff --git a/src/AggregateRoot.php b/src/AggregateRoot.php
new file mode 100644
index 0000000..abb2be6
--- /dev/null
+++ b/src/AggregateRoot.php
@@ -0,0 +1,19 @@
+events;
+ $this->events = [];
+
+ return $events;
+ }
+}
diff --git a/src/Bitrix24Accounts/Entity/Bitrix24Account.php b/src/Bitrix24Accounts/Entity/Bitrix24Account.php
index 527f5b1..616f75d 100644
--- a/src/Bitrix24Accounts/Entity/Bitrix24Account.php
+++ b/src/Bitrix24Accounts/Entity/Bitrix24Account.php
@@ -1,4 +1,5 @@
accessToken = $authToken->getAccessToken();
- $this->refreshToken = $authToken->getRefreshToken();
- $this->expires = $authToken->getExpires();
- $this->applicationScope = $applicationScope->getScopeCodes();
+ private CarbonImmutable $updatedAt,
+ private int $applicationVersion,
+ private Scope $applicationScope,
+ bool $isEmitBitrix24AccountCreatedEvent = false,
+ ) {
+ $this->addAccountCreatedEventIfNeeded($isEmitBitrix24AccountCreatedEvent);
}
- #[Override]
+ #[\Override]
public function getId(): Uuid
{
return $this->id;
}
- #[Override]
+ #[\Override]
public function getBitrix24UserId(): int
{
return $this->bitrix24UserId;
}
- #[Override]
+ #[\Override]
public function isBitrix24UserAdmin(): bool
{
return $this->isBitrix24UserAdmin;
}
- #[Override]
+ #[\Override]
public function getMemberId(): string
{
return $this->memberId;
}
- #[Override]
+ #[\Override]
public function getDomainUrl(): string
{
return $this->domainUrl;
}
- #[Override]
+ #[\Override]
public function getStatus(): Bitrix24AccountStatus
{
- return $this->accountStatus;
+ return $this->status;
}
- #[Override]
+ /**
+ * @throws InvalidArgumentException
+ */
+ #[\Override]
public function getAuthToken(): AuthToken
{
- return new AuthToken($this->accessToken, $this->refreshToken, $this->expires);
+ return $this->authToken;
}
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function renewAuthToken(RenewedAuthToken $renewedAuthToken): void
{
if ($this->memberId !== $renewedAuthToken->memberId) {
@@ -117,118 +116,114 @@ public function renewAuthToken(RenewedAuthToken $renewedAuthToken): void
);
}
- $this->accessToken = $renewedAuthToken->authToken->getAccessToken();
- $this->refreshToken = $renewedAuthToken->authToken->getRefreshToken();
- $this->expires = $renewedAuthToken->authToken->getExpires();
+ $this->authToken = new AuthToken(
+ $renewedAuthToken->authToken->accessToken,
+ $renewedAuthToken->authToken->refreshToken,
+ $renewedAuthToken->authToken->expires
+ );
+
$this->updatedAt = new CarbonImmutable();
}
- #[Override]
+ #[\Override]
public function getApplicationVersion(): int
{
return $this->applicationVersion;
}
- /**
- * @throws UnknownScopeCodeException
- */
- #[Override]
+ #[\Override]
public function getApplicationScope(): Scope
{
- return new Scope($this->applicationScope);
+ return $this->applicationScope;
}
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function changeDomainUrl(string $newDomainUrl): void
{
- if ($newDomainUrl === '') {
+ if ('' === $newDomainUrl) {
throw new InvalidArgumentException('new domain url cannot be empty');
}
- if (Bitrix24AccountStatus::blocked === $this->accountStatus || Bitrix24AccountStatus::deleted === $this->accountStatus) {
+ if (Bitrix24AccountStatus::blocked === $this->status || Bitrix24AccountStatus::deleted === $this->status) {
throw new InvalidArgumentException(
sprintf(
'bitrix24 account %s for domain %s must be in active or new state, now account in %s state. domain url cannot be changed',
$this->id->toRfc4122(),
$this->domainUrl,
- $this->accountStatus->name
+ $this->status->name
)
);
}
$this->domainUrl = $newDomainUrl;
$this->updatedAt = new CarbonImmutable();
+ $this->events[] = new Bitrix24AccountDomainUrlChangedEvent(
+ $this->id,
+ new CarbonImmutable()
+ );
}
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function applicationInstalled(string $applicationToken): void
{
- if (Bitrix24AccountStatus::new !== $this->accountStatus) {
- throw new InvalidArgumentException(sprintf(
- 'for finish installation bitrix24 account must be in status «new», current status - «%s»',
- $this->accountStatus->name));
+ if (Bitrix24AccountStatus::new !== $this->status) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'for finish installation bitrix24 account must be in status «new», current status - «%s»',
+ $this->status->name
+ )
+ );
}
- if ($applicationToken === '') {
+ if ('' === $applicationToken) {
throw new InvalidArgumentException('application token cannot be empty');
}
- $this->accountStatus = Bitrix24AccountStatus::active;
+ $this->status = Bitrix24AccountStatus::active;
$this->applicationToken = $applicationToken;
$this->updatedAt = new CarbonImmutable();
+ $this->events[] = new Bitrix24AccountApplicationInstalledEvent(
+ $this->id,
+ new CarbonImmutable()
+ );
}
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function applicationUninstalled(string $applicationToken): void
{
- if ($applicationToken === '') {
- throw new InvalidArgumentException('application token cannot be empty');
- }
-
- if (Bitrix24AccountStatus::active !== $this->accountStatus) {
- throw new InvalidArgumentException(sprintf(
- 'for uninstall account must be in status «active», current status - «%s»',
- $this->accountStatus->name));
- }
-
- if ($this->applicationToken !== $applicationToken) {
- throw new InvalidArgumentException(
- sprintf(
- 'application token «%s» mismatch with application token «%s» for bitrix24 account %s for domain %s',
- $applicationToken,
- $this->applicationToken,
- $this->id->toRfc4122(),
- $this->domainUrl
- )
- );
- }
-
- $this->accountStatus = Bitrix24AccountStatus::deleted;
+ $this->guardEmptyToken($applicationToken);
+ $this->guardTokenMismatch($applicationToken);
+ $this->guardApplicationIsActive();
+ $this->status = Bitrix24AccountStatus::deleted;
$this->updatedAt = new CarbonImmutable();
+ $this->events[] = new Bitrix24AccountApplicationUninstalledEvent(
+ $this->id,
+ new CarbonImmutable()
+ );
}
- #[Override]
+ #[\Override]
public function isApplicationTokenValid(string $applicationToken): bool
{
return $this->applicationToken === $applicationToken;
}
- #[Override]
+ #[\Override]
public function getCreatedAt(): CarbonImmutable
{
return $this->createdAt;
}
- #[Override]
+ #[\Override]
public function getUpdatedAt(): CarbonImmutable
{
return $this->updatedAt;
@@ -237,41 +232,53 @@ public function getUpdatedAt(): CarbonImmutable
/**
* @throws InvalidArgumentException
*/
- #[Override]
- public function updateApplicationVersion(int $version, ?Scope $newScope): void
+ #[\Override]
+ public function updateApplicationVersion(AuthToken $authToken, int $b24UserId, int $version, ?Scope $newScope): void
{
- if (Bitrix24AccountStatus::active !== $this->accountStatus) {
- throw new InvalidArgumentException(sprintf('account must be in status «active», but now account in status «%s»', $this->accountStatus->name));
+ if (Bitrix24AccountStatus::active !== $this->status) {
+ throw new InvalidArgumentException(
+ sprintf('account must be in status «active», but now account in status «%s»', $this->status->name)
+ );
}
if ($this->applicationVersion >= $version) {
throw new InvalidArgumentException(
- sprintf('you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»',
+ sprintf(
+ 'you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»',
$this->applicationVersion,
- $version));
+ $version
+ )
+ );
}
$this->applicationVersion = $version;
- if ($newScope instanceof \Bitrix24\SDK\Core\Credentials\Scope) {
- $this->applicationScope = $newScope->getScopeCodes();
+ if ($newScope instanceof Scope) {
+ $this->applicationScope = $newScope;
}
$this->updatedAt = new CarbonImmutable();
+ $this->events[] = new Bitrix24AccountApplicationVersionUpdatedEvent(
+ $this->id,
+ new CarbonImmutable()
+ );
}
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function markAsActive(?string $comment): void
{
- if (Bitrix24AccountStatus::blocked !== $this->accountStatus) {
+ if (Bitrix24AccountStatus::blocked !== $this->status) {
throw new InvalidArgumentException(
- sprintf('you can activate account only in status blocked, now account in status %s',
- $this->accountStatus->name));
+ sprintf(
+ 'you can activate account only in status «blocked», now account in status «%s»',
+ $this->status->name
+ )
+ );
}
- $this->accountStatus = Bitrix24AccountStatus::active;
+ $this->status = Bitrix24AccountStatus::active;
$this->comment = $comment;
$this->updatedAt = new CarbonImmutable();
}
@@ -279,21 +286,71 @@ public function markAsActive(?string $comment): void
/**
* @throws InvalidArgumentException
*/
- #[Override]
+ #[\Override]
public function markAsBlocked(?string $comment): void
{
- if (Bitrix24AccountStatus::deleted === $this->accountStatus) {
+ if (Bitrix24AccountStatus::deleted === $this->status) {
throw new InvalidArgumentException('you cannot block account in status «deleted»');
}
- $this->accountStatus = Bitrix24AccountStatus::blocked;
+ $this->status = Bitrix24AccountStatus::blocked;
$this->comment = $comment;
$this->updatedAt = new CarbonImmutable();
+ $this->events[] = new Bitrix24AccountBlockedEvent(
+ $this->id,
+ new CarbonImmutable(),
+ $this->comment
+ );
}
- #[Override]
+ #[\Override]
public function getComment(): ?string
{
return $this->comment;
}
-}
\ No newline at end of file
+
+ private function guardEmptyToken(string $applicationToken): void
+ {
+ if ('' === $applicationToken) {
+ throw new InvalidArgumentException('application token cannot be empty');
+ }
+ }
+
+ private function guardTokenMismatch(string $applicationToken): void
+ {
+ if ($this->applicationToken !== $applicationToken) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'application token «%s» mismatch with application token «%s» for bitrix24 account %s for domain %s',
+ $applicationToken,
+ $this->applicationToken,
+ $this->id->toRfc4122(),
+ $this->domainUrl
+ )
+ );
+ }
+ }
+
+ private function guardApplicationIsActive(): void
+ {
+ if (Bitrix24AccountStatus::active !== $this->status) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'for uninstall account must be in status «active», current status - «%s»',
+ $this->status->name
+ )
+ );
+ }
+ }
+
+ private function addAccountCreatedEventIfNeeded(bool $isEmitCreatedEvent): void
+ {
+ if ($isEmitCreatedEvent) {
+ // Создание события и добавление его в массив событий
+ $this->events[] = new Bitrix24AccountCreatedEvent(
+ $this->id,
+ $this->createdAt
+ );
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php b/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php
new file mode 100644
index 0000000..5fb37ad
--- /dev/null
+++ b/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php
@@ -0,0 +1,215 @@
+getClassMetadata(Bitrix24Account::class));
+ }
+
+ /**
+ * @phpstan-return Bitrix24AccountInterface&AggregateRootEventsEmitterInterface
+ *
+ * @throws Bitrix24AccountNotFoundException
+ */
+ #[\Override]
+ public function getById(Uuid $uuid): Bitrix24AccountInterface
+ {
+ $account = $this->getEntityManager()->getRepository(Bitrix24Account::class)
+ ->createQueryBuilder('b24')
+ ->where('b24.id = :id')
+ ->andWhere('b24.status != :status')
+ ->setParameter('id', $uuid)
+ ->setParameter('status', Bitrix24AccountStatus::deleted)
+ ->getQuery()
+ ->getOneOrNullResult()
+ ;
+
+ if (null === $account) {
+ throw new Bitrix24AccountNotFoundException(
+ sprintf('bitrix24 account not found by id %s', $uuid->toRfc4122())
+ );
+ }
+
+ return $account;
+ }
+
+ public function existsById(Uuid $uuid): bool
+ {
+ $account = $this->getEntityManager()->getRepository(Bitrix24Account::class)
+ ->createQueryBuilder('b24')
+ ->where('b24.id = :id')
+ ->andWhere('b24.status != :status')
+ ->setParameter('id', $uuid)
+ ->setParameter('status', Bitrix24AccountStatus::deleted)
+ ->getQuery()
+ ->getOneOrNullResult()
+ ;
+
+ return (bool) $account;
+ }
+
+ #[\Override]
+ public function save(Bitrix24AccountInterface $bitrix24Account): void
+ {
+ $this->getEntityManager()->persist($bitrix24Account);
+ }
+
+ /**
+ * @phpstan-return array
+ *
+ * @throws InvalidArgumentException
+ */
+ #[\Override]
+ public function findByMemberId(
+ string $memberId,
+ ?Bitrix24AccountStatus $bitrix24AccountStatus = null,
+ ?int $bitrix24UserId = null,
+ ?bool $isAdmin = null
+ ): array {
+ if ('' === trim($memberId)) {
+ throw new InvalidArgumentException('memberId cannot be empty');
+ }
+
+ $criteria = [
+ 'memberId' => $memberId,
+ ];
+ if ($bitrix24AccountStatus instanceof Bitrix24AccountStatus) {
+ $criteria['status'] = $bitrix24AccountStatus->name;
+ }
+
+ if (null !== $bitrix24UserId) {
+ $criteria['bitrix24UserId'] = $bitrix24UserId;
+ }
+
+ if (null !== $isAdmin) {
+ $criteria['isBitrix24UserAdmin'] = $isAdmin;
+ }
+
+ return $this->findBy($criteria);
+ }
+
+ #[\Override]
+ public function delete(Uuid $uuid): void
+ {
+ $bitrix24Account = $this->getEntityManager()->getRepository(Bitrix24Account::class)->find($uuid);
+
+ if (null === $bitrix24Account) {
+ throw new Bitrix24AccountNotFoundException(
+ sprintf('bitrix24 account not found by id %s', $uuid->toRfc4122())
+ );
+ }
+
+ if (Bitrix24AccountStatus::deleted !== $bitrix24Account->getStatus()) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'you cannot delete bitrix24account «%s», they must be in status «deleted», current status «%s»',
+ $bitrix24Account->getId()->toRfc4122(),
+ $bitrix24Account->getStatus()->name
+ )
+ );
+ }
+
+ $this->save($bitrix24Account);
+ }
+
+ public function findAllActive(?int $limit = null, ?int $offset = null): array
+ {
+ return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy(
+ [
+ 'status' => Bitrix24AccountStatus::active,
+ ],
+ null,
+ $limit,
+ $offset
+ );
+ }
+
+ /**
+ * @param non-empty-string $applicationToken
+ *
+ * @phpstan-return array
+ *
+ * @throws InvalidArgumentException
+ */
+ #[\Override]
+ public function findByApplicationToken(string $applicationToken): array
+ {
+ if ('' === $applicationToken) {
+ throw new InvalidArgumentException('application token cannot be an empty string');
+ }
+
+ return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy(
+ [
+ 'applicationToken' => $applicationToken,
+ ]
+ );
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ *
+ * @phpstan-return Bitrix24AccountInterface&AggregateRootEventsEmitterInterface
+ */
+ #[\Override]
+ public function findOneAdminByMemberId(string $memberId): ?Bitrix24AccountInterface
+ {
+ if ('' === trim($memberId)) {
+ throw new InvalidArgumentException('memberId cannot be an empty string');
+ }
+
+ return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findOneBy(
+ [
+ 'memberId' => $memberId,
+ 'isBitrix24UserAdmin' => true,
+ 'status' => [Bitrix24AccountStatus::active, Bitrix24AccountStatus::new],
+ ]
+ );
+ }
+
+ /**
+ * @phpstan-return array
+ *
+ * @return array
+ *
+ * @throws InvalidArgumentException
+ */
+ #[\Override]
+ public function findByDomain(
+ string $domainUrl,
+ ?Bitrix24AccountStatus $bitrix24AccountStatus = null,
+ ?bool $isAdmin = null
+ ): array {
+ if ('' === trim($domainUrl)) {
+ throw new InvalidArgumentException('domainUrl cannot be an empty string');
+ }
+
+ $criteria = ['domainUrl' => $domainUrl];
+
+ if ($bitrix24AccountStatus instanceof Bitrix24AccountStatus) {
+ $criteria['status'] = $bitrix24AccountStatus->name;
+ }
+
+ if (null !== $isAdmin) {
+ $criteria['isBitrix24UserAdmin'] = $isAdmin;
+ }
+
+ return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy($criteria);
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php
new file mode 100644
index 0000000..0d640a8
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php
@@ -0,0 +1,12 @@
+logger->info('Bitrix24Accounts.ChangeDomainUrl.start', [
+ 'b24_domain_url_old' => $command->oldDomain->value,
+ 'b24_domain_url_new' => $command->newDomain->value,
+ ]);
+
+ /** @var AggregateRootEventsEmitterInterface[]|Bitrix24AccountInterface[] $accounts */
+ $accounts = $this->bitrix24AccountRepository->findByDomain($command->oldDomain->value);
+ foreach ($accounts as $account) {
+ $account->changeDomainUrl($command->newDomain->value);
+ $this->bitrix24AccountRepository->save($account);
+ }
+
+ // используется как оператор распаковки (splat operator) для передачи массива как отдельных аргументов:
+ $this->flusher->flush(...$accounts);
+
+ $this->logger->info(
+ 'Bitrix24Accounts.ChangeDomainUrl.Finish',
+ [
+ 'b24_domain_url_old' => $command->oldDomain,
+ 'b24_domain_url_new' => $command->newDomain,
+ ]
+ );
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php
new file mode 100644
index 0000000..979b0e1
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php
@@ -0,0 +1,34 @@
+validate();
+ }
+
+ private function validate(): void
+ {
+ if ('' === $this->applicationToken) {
+ throw new \InvalidArgumentException('Application token cannot be empty.');
+ }
+
+ if ('' === $this->memberId) {
+ throw new \InvalidArgumentException('Member ID cannot be empty.');
+ }
+
+ if ($this->bitrix24UserId <= 0) {
+ throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.');
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php b/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php
new file mode 100644
index 0000000..e5b805d
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php
@@ -0,0 +1,88 @@
+logger->info('Bitrix24Accounts.InstallFinish.start', [
+ 'b24_domain_url' => $command->domain,
+ 'b24_member_id' => $command->memberId,
+ 'b24_application_id' => $command->applicationToken,
+ 'b24_user_id' => $command->bitrix24UserId,
+ ]);
+
+ /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */
+ $bitrix24Account = $this->getSingleAccountByMemberId($command->domain->value, $command->memberId, $command->bitrix24UserId);
+
+ $bitrix24Account->applicationInstalled($command->applicationToken);
+
+ $this->bitrix24AccountRepository->save($bitrix24Account);
+ $this->flusher->flush($bitrix24Account);
+
+ $this->logger->info(
+ 'Bitrix24Accounts.InstallFinish.Finish',
+ [
+ 'b24_domain_url' => $command->domain,
+ 'b24_member_id' => $command->memberId,
+ 'b24_application_id' => $command->applicationToken,
+ 'b24_user_id' => $command->bitrix24UserId,
+ ]
+ );
+ }
+
+ private function getSingleAccountByMemberId(string $domainUrl, string $memberId, ?int $bitrix24UserId): Bitrix24AccountInterface
+ {
+ $accounts = $this->bitrix24AccountRepository->findByMemberId(
+ $memberId,
+ Bitrix24AccountStatus::new,
+ $bitrix24UserId
+ );
+
+ if ([] === $accounts) {
+ throw new Bitrix24AccountNotFoundException(
+ sprintf(
+ 'bitrix24 account for domain %s with member id %s in status «new» not found',
+ $domainUrl,
+ $memberId
+ )
+ );
+ }
+
+ if (count($accounts) > 1) {
+ throw new MultipleBitrix24AccountsFoundException(
+ sprintf(
+ 'multiple bitrix24 accounts for domain %s with member id %s in status «new» found',
+ $domainUrl,
+ $memberId
+ )
+ );
+ }
+
+ return $accounts[0];
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/InstallStart/Command.php b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php
new file mode 100644
index 0000000..a63c792
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php
@@ -0,0 +1,41 @@
+validate();
+ }
+
+ private function validate(): void
+ {
+ if ($this->bitrix24UserId <= 0) {
+ throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.');
+ }
+
+ if ('' === $this->memberId) {
+ throw new \InvalidArgumentException('Member ID must be a non-empty string.');
+ }
+
+ if ($this->applicationVersion <= 0) {
+ throw new \InvalidArgumentException('Application version must be a positive integer.');
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php b/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php
new file mode 100644
index 0000000..01e7ee0
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php
@@ -0,0 +1,75 @@
+logger->info('Bitrix24Accounts.InstallStart.start', [
+ 'id' => $command->uuid->toRfc4122(),
+ 'domain' => $command->domain,
+ 'member_id' => $command->memberId,
+ ]);
+
+ $bitrix24Account = new Bitrix24Account(
+ $command->uuid,
+ $command->bitrix24UserId,
+ $command->isBitrix24UserAdmin,
+ $command->memberId,
+ $command->domain->value,
+ Bitrix24AccountStatus::new,
+ $command->authToken,
+ new CarbonImmutable(),
+ new CarbonImmutable(),
+ $command->applicationVersion,
+ $command->applicationScope,
+ true
+ );
+
+ $isAccountExists = $this->bitrix24AccountRepository->existsById($bitrix24Account->getId());
+
+ if (!$isAccountExists) {
+ $this->bitrix24AccountRepository->save($bitrix24Account);
+ $this->flusher->flush($bitrix24Account);
+
+ $this->logger->info(
+ 'Bitrix24Accounts.InstallStart.Finish',
+ [
+ 'id' => $command->uuid->toRfc4122(),
+ 'domain_url' => $command->domain,
+ 'member_id' => $command->memberId,
+ ]
+ );
+ } else {
+ $this->logger->info(
+ 'Bitrix24Accounts.InstallStart.AlreadyExists',
+ [
+ 'id' => $command->uuid->toRfc4122(),
+ 'domain' => $command->domain,
+ 'member_id' => $command->memberId,
+ ]
+ );
+
+ throw new Bitrix24AccountNotFoundException(
+ sprintf('bitrix24account with uuid "%s" already exists', $command->uuid->toRfc4122())
+ );
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php b/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php
new file mode 100644
index 0000000..e33957d
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php
@@ -0,0 +1,15 @@
+logger->info('Bitrix24Accounts.RenewAuthToken.start', [
+ 'domain_url' => $command->renewedAuthToken->domain,
+ 'member_id' => $command->renewedAuthToken->memberId,
+ 'bitrix24_user_id' => $command->bitrix24UserId,
+ ]);
+
+ /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */
+ $bitrix24Account = $this->getSingleAccountByMemberId(
+ $command->renewedAuthToken->domain,
+ $command->renewedAuthToken->memberId,
+ $command->bitrix24UserId
+ );
+
+ $bitrix24Account->renewAuthToken($command->renewedAuthToken);
+
+ $this->bitrix24AccountRepository->save($bitrix24Account);
+
+ $this->flusher->flush($bitrix24Account);
+
+ $this->logger->info(
+ 'Bitrix24Accounts.RenewAuthToken.finish',
+ [
+ 'domain_url' => $command->renewedAuthToken->domain,
+ 'member_id' => $command->renewedAuthToken->memberId,
+ 'bitrix24_user_id' => $command->bitrix24UserId,
+ ]
+ );
+ }
+
+ /**
+ * @throws MultipleBitrix24AccountsFoundException
+ */
+ private function getSingleAccountByMemberId(string $domainUrl, string $memberId, ?int $bitrix24UserId): Bitrix24AccountInterface
+ {
+ $accounts = $this->bitrix24AccountRepository->findByMemberId(
+ $memberId,
+ Bitrix24AccountStatus::active,
+ $bitrix24UserId
+ );
+
+ if (null === $bitrix24UserId && count($accounts) > 1) {
+ throw new MultipleBitrix24AccountsFoundException(
+ sprintf(
+ 'updating auth token failure - for domain %s with member id %s found multiple active accounts, try pass bitrix24_user_id in command',
+ $domainUrl,
+ $memberId
+ )
+ );
+ }
+
+ if (null !== $bitrix24UserId && count($accounts) > 1) {
+ throw new MultipleBitrix24AccountsFoundException(
+ sprintf(
+ 'updating auth token failure - for domain %s with member id %s and bitrix24 user id %s found multiple active accounts',
+ $domainUrl,
+ $memberId,
+ $bitrix24UserId
+ )
+ );
+ }
+
+ return $accounts[0];
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/Uninstall/Command.php b/src/Bitrix24Accounts/UseCase/Uninstall/Command.php
new file mode 100644
index 0000000..5a92c6a
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/Uninstall/Command.php
@@ -0,0 +1,24 @@
+validate();
+ }
+
+ private function validate(): void
+ {
+ if ('' === $this->applicationToken) {
+ throw new \InvalidArgumentException('Application token must be a non-empty string.');
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php b/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php
new file mode 100644
index 0000000..516126b
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php
@@ -0,0 +1,49 @@
+logger->info('Bitrix24Accounts.Uninstall.start', [
+ 'b24_application_token' => $command->applicationToken,
+ ]);
+
+ /** @var AggregateRootEventsEmitterInterface[]|Bitrix24AccountInterface[] $accounts */
+ $accounts = $this->bitrix24AccountRepository->findByApplicationToken($command->applicationToken);
+ $accountsCount = count($accounts);
+ foreach ($accounts as $account) {
+ $account->applicationUninstalled($command->applicationToken);
+ $this->bitrix24AccountRepository->save($account);
+ }
+
+ $this->flusher->flush(...$accounts);
+
+ $this->logger->info(
+ 'Bitrix24Accounts.Uninstall.Finish',
+ [
+ 'accountsCount' => $accountsCount,
+ 'b24_application_token' => $command->applicationToken,
+ ]
+ );
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php b/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php
new file mode 100644
index 0000000..ed6c4f0
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php
@@ -0,0 +1,39 @@
+validate();
+ }
+
+ private function validate(): void
+ {
+ if ($this->bitrix24UserId <= 0) {
+ throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.');
+ }
+
+ if ('' === $this->memberId) {
+ throw new \InvalidArgumentException('Member ID must be a non-empty string.');
+ }
+
+ if ($this->newApplicationVersion <= 0) {
+ throw new \InvalidArgumentException('Application version must be a positive integer.');
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php b/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php
new file mode 100644
index 0000000..997e500
--- /dev/null
+++ b/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php
@@ -0,0 +1,92 @@
+logger->info('Bitrix24Accounts.UpdateVersion.start', [
+ 'uuid' => $command->uuid,
+ 'bitrix24_user_id' => $command->bitrix24UserId,
+ 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin,
+ 'member_id' => $command->memberId,
+ 'auth_token' => $command->authToken,
+ 'new_application_version' => $command->newApplicationVersion,
+ 'new_application_scope' => $command->newApplicationScope,
+ ]);
+
+ $accounts = $this->bitrix24AccountRepository->findByMemberId(
+ $command->memberId,
+ Bitrix24AccountStatus::active,
+ $command->bitrix24UserId,
+ );
+
+ if ([] !== $accounts) {
+ /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */
+ $bitrix24Account = $accounts[0];
+ $bitrix24Account->updateApplicationVersion(
+ $command->authToken,
+ $command->bitrix24UserId,
+ $command->newApplicationVersion,
+ $command->newApplicationScope,
+ );
+
+ $this->bitrix24AccountRepository->save($bitrix24Account);
+ $this->flusher->flush($bitrix24Account);
+
+ $this->logger->info('Bitrix24Accounts.UpdateVersion.finish', [
+ 'uuid' => $command->uuid,
+ 'bitrix24_user_id' => $command->bitrix24UserId,
+ 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin,
+ 'member_id' => $command->memberId,
+ 'auth_token' => $command->authToken,
+ 'new_application_version' => $command->newApplicationVersion,
+ 'new_application_scope' => $command->newApplicationScope,
+ ]);
+ } else {
+ $this->logger->info(
+ 'Bitrix24Accounts.UpdateVersion.NotFoundAccount',
+ [
+ 'uuid' => $command->uuid,
+ 'bitrix24_user_id' => $command->bitrix24UserId,
+ 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin,
+ 'member_id' => $command->memberId,
+ 'auth_token' => $command->authToken,
+ 'new_application_version' => $command->newApplicationVersion,
+ 'new_application_scope' => $command->newApplicationScope,
+ ]
+ );
+
+ throw new MultipleBitrix24AccountsFoundException(
+ sprintf(
+ 'bitrix24account not found by memberId %s, status %s and bitrix24UserId %s ',
+ $command->memberId,
+ 'active',
+ $command->bitrix24UserId
+ )
+ );
+ }
+ }
+}
diff --git a/src/Bitrix24Accounts/ValueObjects/Domain.php b/src/Bitrix24Accounts/ValueObjects/Domain.php
new file mode 100644
index 0000000..a399ee7
--- /dev/null
+++ b/src/Bitrix24Accounts/ValueObjects/Domain.php
@@ -0,0 +1,34 @@
+validate($domain);
+ $this->value = $domain;
+ }
+
+ private function validate(string $domain): void
+ {
+ // Регулярное выражение для проверки допустимых символов (латиница и кириллица)
+ $patternValidChars = '/^((?!-)[A-Za-zА-Яа-яЁё0-9-]{1,63}(?em->flush();
+ foreach ($aggregateRootEventsEmitter as $aggregateRootEventEmitter) {
+ $events = $aggregateRootEventEmitter->emitEvents();
+ foreach ($events as $event) {
+ $this->eventDispatcher->dispatch($event);
+ }
+ }
+ }
+}
diff --git a/tests/EntityManagerFactory.php b/tests/EntityManagerFactory.php
new file mode 100644
index 0000000..e3935cf
--- /dev/null
+++ b/tests/EntityManagerFactory.php
@@ -0,0 +1,77 @@
+ 'pdo_pgsql',
+ 'host' => $_ENV['DATABASE_HOST'],
+ 'user' => $_ENV['DATABASE_USER'],
+ 'password' => $_ENV['DATABASE_PASSWORD'],
+ 'dbname' => $_ENV['DATABASE_NAME'],
+ ];
+
+ if (!Type::hasType(UuidType::NAME)) {
+ Type::addType(UuidType::NAME, UuidType::class);
+ }
+
+ if (!Type::hasType('carbon_immutable')) {
+ Type::addType('carbon_immutable', CarbonImmutableType::class);
+ }
+
+ $configuration = ORMSetup::createXMLMetadataConfiguration($paths, $isDevMode);
+
+ $connection = DriverManager::getConnection($connectionParams, $configuration);
+ self::$entityManager = new EntityManager($connection, $configuration);
+ }
+
+ return self::$entityManager;
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php b/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php
new file mode 100644
index 0000000..bff547e
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php
@@ -0,0 +1,123 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\Builders;
+
+use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Events\AggregateRootEventsEmitterInterface;
+use Bitrix24\SDK\Core\Credentials\AuthToken;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Carbon\CarbonImmutable;
+use Symfony\Component\Uid\Uuid;
+
+class Bitrix24AccountBuilder
+{
+ private readonly Uuid $id;
+
+ private readonly int $bitrix24UserId;
+
+ private readonly bool $isBitrix24UserAdmin;
+
+ /** bitrix24 portal unique id */
+ private string $memberId;
+
+ private string $domainUrl;
+
+ private Bitrix24AccountStatus $status = Bitrix24AccountStatus::active;
+
+ private readonly AuthToken $authToken;
+
+ private readonly CarbonImmutable $createdAt;
+
+ private readonly CarbonImmutable $updatedAt;
+
+ private readonly int $applicationVersion;
+
+ private Scope $applicationScope;
+
+ private ?string $applicationToken = null;
+
+ public function __construct()
+ {
+ $this->id = Uuid::v7();
+ $this->bitrix24UserId = random_int(1, 1_000_000);
+ $this->isBitrix24UserAdmin = true;
+ $this->memberId = Uuid::v4()->toRfc4122();
+ $this->domainUrl = Uuid::v4()->toRfc4122().'-example.com';
+ $this->authToken = new AuthToken('old_1', 'old_2', 3600);
+ $this->createdAt = CarbonImmutable::now();
+ $this->updatedAt = CarbonImmutable::now();
+ $this->applicationVersion = 1;
+ $this->applicationScope = new Scope();
+ }
+
+ public function withMemberId(string $memberId): self
+ {
+ $this->memberId = $memberId;
+
+ return $this;
+ }
+
+ public function withDomainUrl(string $domainUrl): self
+ {
+ $this->domainUrl = $domainUrl;
+
+ return $this;
+ }
+
+ public function withApplicationScope(Scope $applicationScope): self
+ {
+ $this->applicationScope = $applicationScope;
+
+ return $this;
+ }
+
+ public function withApplicationToken(string $applicationToken): self
+ {
+ $this->applicationToken = $applicationToken;
+
+ return $this;
+ }
+
+ public function withStatus(Bitrix24AccountStatus $bitrix24AccountStatus): self
+ {
+ $this->status = $bitrix24AccountStatus;
+
+ return $this;
+ }
+
+ public function build(): Bitrix24Account
+ {
+ $bitrix24Account = new Bitrix24Account(
+ $this->id,
+ $this->bitrix24UserId,
+ $this->isBitrix24UserAdmin,
+ $this->memberId,
+ $this->domainUrl,
+ $this->status,
+ $this->authToken,
+ $this->createdAt,
+ $this->updatedAt,
+ $this->applicationVersion,
+ $this->applicationScope
+ );
+
+ if ($this->applicationToken !== null && Bitrix24AccountStatus::new == $this->status) {
+ $bitrix24Account->applicationInstalled($this->applicationToken);
+ }
+
+ return $bitrix24Account;
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php b/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php
new file mode 100644
index 0000000..4736e55
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php
@@ -0,0 +1,84 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\Infrastructure\Doctrine;
+
+use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\FlusherDecorator;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use Bitrix24\SDK\Core\Credentials\AuthToken;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Tests\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterfaceTest;
+use Bitrix24\SDK\Tests\Application\Contracts\TestRepositoryFlusherInterface;
+use Carbon\CarbonImmutable;
+use PHPUnit\Framework\Attributes\CoversClass;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Uid\Uuid;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24AccountRepository::class)]
+class Bitrix24AccountRepositoryTest extends Bitrix24AccountRepositoryInterfaceTest
+{
+ #[\Override]
+ protected function createBitrix24AccountImplementation(
+ Uuid $uuid,
+ int $bitrix24UserId,
+ bool $isBitrix24UserAdmin,
+ string $memberId,
+ string $domainUrl,
+ Bitrix24AccountStatus $bitrix24AccountStatus,
+ AuthToken $authToken,
+ CarbonImmutable $createdAt,
+ CarbonImmutable $updatedAt,
+ int $applicationVersion,
+ Scope $applicationScope
+ ): Bitrix24AccountInterface {
+ return new Bitrix24Account(
+ $uuid,
+ $bitrix24UserId,
+ $isBitrix24UserAdmin,
+ $memberId,
+ $domainUrl,
+ $bitrix24AccountStatus,
+ $authToken,
+ $createdAt,
+ $updatedAt,
+ $applicationVersion,
+ $applicationScope
+ );
+ }
+
+ #[\Override]
+ protected function createBitrix24AccountRepositoryImplementation(): Bitrix24AccountRepositoryInterface
+ {
+ $entityManager = EntityManagerFactory::get();
+
+ return new Bitrix24AccountRepository($entityManager);
+ }
+
+ #[\Override]
+ protected function createRepositoryFlusherImplementation(): TestRepositoryFlusherInterface
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+
+ return new FlusherDecorator(new Flusher($entityManager, $eventDispatcher));
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
new file mode 100644
index 0000000..01e2d5a
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php
@@ -0,0 +1,160 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\ChangeDomainUrl;
+
+use Bitrix24\Lib\Bitrix24Accounts;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountDomainUrlChangedEvent;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\Uid\Uuid;
+use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch());
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+ $this->handler = new Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger()
+ );
+ }
+
+ #[Test]
+ #[TestDox('Test change domain url for one account')]
+ public function testChangeDomainUrlForAccount(): void
+ {
+ $oldDomainUrl = Uuid::v7()->toRfc4122().'-test.bitrix24.com';
+ $newDomainUrl = 'new-'.$oldDomainUrl;
+
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withDomainUrl($oldDomainUrl)
+ ->build();
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\ChangeDomainUrl\Command(
+ new Domain($oldDomainUrl),
+ new Domain($newDomainUrl)
+ )
+ );
+
+ $updated = $this->repository->getById($bitrix24Account->getId());
+ $this->assertEquals(
+ $newDomainUrl,
+ $updated->getDomainUrl(),
+ sprintf(
+ 'New domain url %s must be equals domain url %s after update',
+ $newDomainUrl,
+ $updated->getDomainUrl()
+ )
+ );
+
+ $this->assertTrue(
+ in_array(
+ Bitrix24AccountDomainUrlChangedEvent::class,
+ $this->eventDispatcher->getOrphanedEvents(),
+ ),
+ sprintf(
+ 'Event %s was expected to be in the list of orphan events, but it is missing',
+ Bitrix24AccountDomainUrlChangedEvent::class
+ )
+ );
+ }
+
+ #[Test]
+ #[TestDox('Test change domain url for many accounts')]
+ public function testChangeDomainUrlForManyAccounts(): void
+ {
+ $oldDomainUrl = Uuid::v7()->toRfc4122().'-test.bitrix24.com';
+ $newDomainUrl = 'new-'.$oldDomainUrl;
+ $b24MemberId = Uuid::v7()->toRfc4122();
+
+ $bitrix24AccountA = (new Bitrix24AccountBuilder())
+ ->withDomainUrl($oldDomainUrl)
+ ->withMemberId($b24MemberId)
+ ->build();
+ $this->repository->save($bitrix24AccountA);
+
+ $bitrix24AccountB = (new Bitrix24AccountBuilder())
+ ->withDomainUrl($oldDomainUrl)
+ ->withMemberId($b24MemberId)
+ ->build();
+ $this->repository->save($bitrix24AccountB);
+
+ $this->flusher->flush();
+
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\ChangeDomainUrl\Command(
+ new Domain($oldDomainUrl),
+ new Domain($newDomainUrl)
+ )
+ );
+
+ $accounts = $this->repository->findByMemberId($b24MemberId);
+ foreach ($accounts as $account) {
+ $this->assertSame(
+ $account->getDomainUrl(),
+ $newDomainUrl,
+ sprintf(
+ 'domain url «%s» mismatch with expected «%s» for account «%s»',
+ $account->getDomainUrl(),
+ $newDomainUrl,
+ $account->getId()->toRfc4122()
+ )
+ );
+ }
+
+ $this->assertTrue(
+ in_array(
+ Bitrix24AccountDomainUrlChangedEvent::class,
+ $this->eventDispatcher->getOrphanedEvents()
+ ),
+ sprintf(
+ 'Event %s was expected to be in the list of orphan events, but it is missing',
+ Bitrix24AccountDomainUrlChangedEvent::class
+ )
+ );
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php
new file mode 100644
index 0000000..f0d1ac0
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php
@@ -0,0 +1,106 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\InstallFinish;
+
+use Bitrix24\Lib\Bitrix24Accounts;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountApplicationInstalledEvent;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\Uid\Uuid;
+use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24Accounts\UseCase\InstallFinish\Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Bitrix24Accounts\UseCase\InstallFinish\Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+ $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch());
+
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+
+ $this->handler = new Bitrix24Accounts\UseCase\InstallFinish\Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger()
+ );
+ }
+
+ #[Test]
+ #[TestDox('test finish installation application')]
+ public function testFinishInstallationApplication(): void
+ {
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::new)
+ ->build();
+
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $applicationToken = Uuid::v7()->toRfc4122();
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\InstallFinish\Command(
+ $applicationToken,
+ $bitrix24Account->getMemberId(),
+ new Domain($bitrix24Account->getDomainUrl()),
+ $bitrix24Account->getBitrix24UserId()
+ )
+ );
+
+ $updated = $this->repository->getById($bitrix24Account->getId());
+ $this->assertEquals(Bitrix24AccountStatus::active, $updated->getStatus(), 'expected status is active');
+ $this->assertTrue(
+ $updated->isApplicationTokenValid($applicationToken),
+ sprintf(
+ 'failed application token «%s» validation for bitrix24 account with id «%s»',
+ $applicationToken,
+ $bitrix24Account->getId()->toString()
+ )
+ );
+ $this->assertTrue(
+ in_array(Bitrix24AccountApplicationInstalledEvent::class, $this->eventDispatcher->getOrphanedEvents(), true),
+ sprintf(
+ 'emited event «%s» for bitrix24 account wiht id «%s» not found',
+ Bitrix24AccountApplicationInstalledEvent::class,
+ $bitrix24Account->getId()->toString()
+ )
+ );
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php
new file mode 100644
index 0000000..0090188
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php
@@ -0,0 +1,209 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\InstallStart;
+
+use Bitrix24\Lib\Bitrix24Accounts;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountCreatedEvent;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\Bitrix24AccountNotFoundException;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Random\RandomException;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24Accounts\UseCase\InstallStart\Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Bitrix24Accounts\UseCase\InstallStart\Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $this->eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+ $this->handler = new Bitrix24Accounts\UseCase\InstallStart\Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger()
+ );
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ * @throws Bitrix24AccountNotFoundException
+ * @throws UnknownScopeCodeException
+ */
+ #[Test]
+ public function testInstallNewApplication(): void
+ {
+ $bitrix24AccountBuilder = (new Bitrix24AccountBuilder())
+ ->withApplicationScope(new Scope(['crm']))
+ ->build();
+
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\InstallStart\Command(
+ $bitrix24AccountBuilder->getId(),
+ $bitrix24AccountBuilder->getBitrix24UserId(),
+ $bitrix24AccountBuilder->isBitrix24UserAdmin(),
+ $bitrix24AccountBuilder->getMemberId(),
+ new Domain($bitrix24AccountBuilder->getDomainUrl()),
+ $bitrix24AccountBuilder->getAuthToken(),
+ $bitrix24AccountBuilder->getApplicationVersion(),
+ $bitrix24AccountBuilder->getApplicationScope()
+ )
+ );
+
+ $bitrix24Account = $this->repository->getById($bitrix24AccountBuilder->getId());
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getBitrix24UserId(),
+ $bitrix24Account->getBitrix24UserId(),
+ sprintf(
+ 'Expected the property value to be "%s", but got "%s"',
+ $bitrix24AccountBuilder->getBitrix24UserId(),
+ $bitrix24Account->getBitrix24UserId()
+ )
+ );
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->isBitrix24UserAdmin(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ sprintf(
+ 'Expected the property value to be "%s", but got "%s"',
+ $bitrix24AccountBuilder->isBitrix24UserAdmin(),
+ $bitrix24Account->isBitrix24UserAdmin()
+ )
+ );
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getMemberId(),
+ $bitrix24Account->getMemberId(),
+ sprintf(
+ 'Expected the property value to be "%s", but got "%s"',
+ $bitrix24AccountBuilder->getMemberId(),
+ $bitrix24Account->getMemberId()
+ )
+ );
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getDomainUrl(),
+ $bitrix24Account->getDomainUrl(),
+ sprintf(
+ 'Expected the property value to be "%s", but got "%s"',
+ $bitrix24AccountBuilder->getDomainUrl(),
+ $bitrix24Account->getDomainUrl()
+ )
+ );
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getAuthToken(),
+ $bitrix24Account->getAuthToken(),
+ 'Object not equals'
+ );
+
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getApplicationVersion(),
+ $bitrix24Account->getApplicationVersion(),
+ sprintf(
+ 'Expected the property value to be "%s", but got "%s"',
+ $bitrix24AccountBuilder->getApplicationVersion(),
+ $bitrix24Account->getApplicationVersion()
+ )
+ );
+ $this->assertEquals(
+ $bitrix24AccountBuilder->getApplicationScope(),
+ $bitrix24Account->getApplicationScope(),
+ 'Object not equals'
+ );
+
+ $this->assertEquals(Bitrix24AccountStatus::new, $bitrix24Account->getStatus());
+
+ $this->assertContains(
+ Bitrix24AccountCreatedEvent::class,
+ $this->eventDispatcher->getOrphanedEvents(),
+ sprintf(
+ 'not found expected domain event «%s»',
+ Bitrix24AccountCreatedEvent::class
+ )
+ );
+ }
+
+ /**
+ * @throws Bitrix24AccountNotFoundException
+ * @throws InvalidArgumentException
+ * @throws UnknownScopeCodeException
+ */
+ #[Test]
+ public function testInstallExistingAccount(): void
+ {
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withApplicationScope(new Scope(['crm']))
+ ->build();
+
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\InstallStart\Command(
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ new Domain($bitrix24Account->getDomainUrl()),
+ $bitrix24Account->getAuthToken(),
+ $bitrix24Account->getApplicationVersion(),
+ $bitrix24Account->getApplicationScope()
+ )
+ );
+
+ $this->expectException(Bitrix24AccountNotFoundException::class);
+ $this->expectExceptionMessage(
+ sprintf('bitrix24account with uuid "%s" already exists', $bitrix24Account->getId())
+ );
+
+ $this->handler->handle(
+ new Bitrix24Accounts\UseCase\InstallStart\Command(
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ new Domain($bitrix24Account->getDomainUrl()),
+ $bitrix24Account->getAuthToken(),
+ $bitrix24Account->getApplicationVersion(),
+ $bitrix24Account->getApplicationScope()
+ )
+ );
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php
new file mode 100644
index 0000000..31a5855
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php
@@ -0,0 +1,107 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\RenewAuthToken;
+
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Bitrix24Accounts\UseCase\RenewAuthToken\Command;
+use Bitrix24\Lib\Bitrix24Accounts\UseCase\RenewAuthToken\Handler;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\ApplicationStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use Bitrix24\SDK\Core\Credentials\AuthToken;
+use Bitrix24\SDK\Core\Response\DTO\RenewedAuthToken;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+/**
+ * @internal
+ */
+#[CoversClass(Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+ $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch());
+
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+ $this->handler = new Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger()
+ );
+ }
+
+ #[Test]
+ public function testRenewAuthTokenWithoutBitrix24UserId(): void
+ {
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->build();
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $newAuthToken = new AuthToken('new_1', 'new_2', 3600);
+ $this->handler->handle(
+ new Command(
+ new RenewedAuthToken(
+ $newAuthToken,
+ $bitrix24Account->getMemberId(),
+ 'https://client-endpoint.com',
+ 'https://server-endpoint.com',
+ ApplicationStatus::subscription(),
+ $bitrix24Account->getDomainUrl()
+ ),
+ $bitrix24Account->getBitrix24UserId()
+ )
+ );
+ $updated = $this->repository->getById($bitrix24Account->getId());
+ $this->assertEquals(
+ $newAuthToken->accessToken,
+ $updated->getAuthToken()->accessToken,
+ sprintf(
+ 'Expected accessToken %s but got %s',
+ $newAuthToken->accessToken,
+ $updated->getAuthToken()->accessToken
+ )
+ );
+
+ $this->assertEquals(
+ $newAuthToken->refreshToken,
+ $updated->getAuthToken()->refreshToken,
+ sprintf(
+ 'Expected refreshToken %s but got %s',
+ $newAuthToken->refreshToken,
+ $updated->getAuthToken()->refreshToken
+ )
+ );
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php
new file mode 100644
index 0000000..a902791
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php
@@ -0,0 +1,99 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\Uninstall;
+
+use Bitrix24\Lib\Bitrix24Accounts;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountApplicationUninstalledEvent;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\Bitrix24AccountNotFoundException;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\Uid\Uuid;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24Accounts\UseCase\Uninstall\Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Bitrix24Accounts\UseCase\Uninstall\Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+ $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch());
+
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+
+ $this->handler = new Bitrix24Accounts\UseCase\Uninstall\Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger(),
+ );
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ * @throws Bitrix24AccountNotFoundException
+ */
+ #[Test]
+ public function testUninstallApplication(): void
+ {
+ $applicationToken = Uuid::v7()->toRfc4122();
+
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::new)
+ ->withApplicationToken($applicationToken)
+ ->build();
+
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $this->handler->handle(new Bitrix24Accounts\UseCase\Uninstall\Command($applicationToken));
+
+ $this->expectException(Bitrix24AccountNotFoundException::class);
+ $this->repository->getById($bitrix24Account->getId());
+
+ $this->assertTrue(
+ in_array(
+ Bitrix24AccountApplicationUninstalledEvent::class,
+ $this->eventDispatcher->getOrphanedEvents()
+ ),
+ sprintf(
+ 'Event %s was expected to be in the list of orphan events, but it is missing',
+ Bitrix24AccountApplicationUninstalledEvent::class
+ )
+ );
+ }
+}
diff --git a/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php
new file mode 100644
index 0000000..8147815
--- /dev/null
+++ b/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php
@@ -0,0 +1,157 @@
+
+ *
+ * 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\Tests\Functional\Bitrix24Accounts\UseCase\UpdateVersion;
+
+use Bitrix24\Lib\Bitrix24Accounts;
+use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository;
+use Bitrix24\Lib\Services\Flusher;
+use Bitrix24\Lib\Tests\EntityManagerFactory;
+use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\MultipleBitrix24AccountsFoundException;
+use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\Uid\Uuid;
+
+/**
+ * @internal
+ */
+#[CoversClass(Bitrix24Accounts\UseCase\UpdateVersion\Handler::class)]
+class HandlerTest extends TestCase
+{
+ private Bitrix24Accounts\UseCase\UpdateVersion\Handler $handler;
+
+ private Flusher $flusher;
+
+ private Bitrix24AccountRepositoryInterface $repository;
+
+ private TraceableEventDispatcher $eventDispatcher;
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $entityManager = EntityManagerFactory::get();
+ $eventDispatcher = new EventDispatcher();
+ $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch());
+
+ $this->repository = new Bitrix24AccountRepository($entityManager);
+ $this->flusher = new Flusher($entityManager, $this->eventDispatcher);
+ $this->handler = new Bitrix24Accounts\UseCase\UpdateVersion\Handler(
+ $this->repository,
+ $this->flusher,
+ new NullLogger(),);
+
+ }
+
+ #[Test]
+ public function testSuccessUpdateVersion(): void
+ {
+ $applicationToken = Uuid::v7()->toRfc4122();
+
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::active)
+ ->withApplicationToken($applicationToken)
+ ->build();
+
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command(
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getAuthToken(),
+ 2,
+ new Scope(['crm','log'])
+ ));
+
+ $updated = $this->repository->getById($bitrix24Account->getId());
+
+ $this->assertEquals(2,$updated->getApplicationVersion(), 'expected application version is 2');
+ $this->assertEquals(new Scope(['crm','log']),$updated->getApplicationScope(), 'application Scope is not equal');
+ }
+
+ #[Test]
+ public function testNotFoundBitrix24AccountForUpdateVersion(): void
+ {
+ $applicationToken = Uuid::v7()->toRfc4122();
+
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::active)
+ ->withApplicationToken($applicationToken)
+ ->build();
+
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+ $this->expectException(MultipleBitrix24AccountsFoundException::class);
+ $this->expectExceptionMessage(sprintf('bitrix24account not found by memberId %s, status %s and bitrix24UserId %s ',
+ $bitrix24Account->getMemberId(),
+ 'active',
+ 3558)
+ );
+
+ $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command(
+ $bitrix24Account->getId(),
+ 3558,
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getAuthToken(),
+ 2,
+ new Scope(['crm','log'])
+ ));
+ }
+
+ #[Test]
+ public function testNotValidVersionForUpdateVersion(): void
+ {
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::active)
+ ->build();
+
+
+ $this->repository->save($bitrix24Account);
+ $this->flusher->flush();
+
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage(
+ sprintf(
+ 'you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»',
+ $bitrix24Account->getApplicationVersion(),
+ '1'
+ )
+ );
+
+ $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command(
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getAuthToken(),
+ 1,
+ new Scope(['crm','log'])
+ ));
+ }
+}
diff --git a/tests/Functional/FlusherDecorator.php b/tests/Functional/FlusherDecorator.php
new file mode 100644
index 0000000..f68b903
--- /dev/null
+++ b/tests/Functional/FlusherDecorator.php
@@ -0,0 +1,21 @@
+flusher->flush();
+ }
+}
diff --git a/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php b/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php
index d8b83f7..3671674 100644
--- a/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php
+++ b/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php
@@ -11,37 +11,38 @@
declare(strict_types=1);
-namespace Bitrix24\SDK\Lib\Tests\Unit\Bitrix24Accounts\Entity;
+namespace Bitrix24\Lib\Tests\Unit\Bitrix24Accounts\Entity;
+use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account;
use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface;
use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus;
-use Bitrix24\SDK\Lib\Bitrix24Accounts\Entity\Bitrix24Account;
use Bitrix24\SDK\Core\Credentials\AuthToken;
use Bitrix24\SDK\Core\Credentials\Scope;
use Bitrix24\SDK\Tests\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterfaceTest;
use Carbon\CarbonImmutable;
-use Override;
use PHPUnit\Framework\Attributes\CoversClass;
use Symfony\Component\Uid\Uuid;
+/**
+ * @internal
+ */
#[CoversClass(Bitrix24Account::class)]
class Bitrix24AccountTest extends Bitrix24AccountInterfaceTest
{
- #[Override]
+ #[\Override]
protected function createBitrix24AccountImplementation(
- Uuid $uuid,
- int $bitrix24UserId,
- bool $isBitrix24UserAdmin,
- string $memberId,
- string $domainUrl,
+ Uuid $uuid,
+ int $bitrix24UserId,
+ bool $isBitrix24UserAdmin,
+ string $memberId,
+ string $domainUrl,
Bitrix24AccountStatus $bitrix24AccountStatus,
- AuthToken $authToken,
- CarbonImmutable $createdAt,
- CarbonImmutable $updatedAt,
- int $applicationVersion,
- Scope $applicationScope
- ): Bitrix24AccountInterface
- {
+ AuthToken $authToken,
+ CarbonImmutable $createdAt,
+ CarbonImmutable $updatedAt,
+ int $applicationVersion,
+ Scope $applicationScope
+ ): Bitrix24AccountInterface {
return new Bitrix24Account(
$uuid,
$bitrix24UserId,
@@ -56,4 +57,4 @@ protected function createBitrix24AccountImplementation(
$applicationScope
);
}
-}
\ No newline at end of file
+}
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php
new file mode 100644
index 0000000..bb3646f
--- /dev/null
+++ b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php
@@ -0,0 +1,142 @@
+assertTrue(true);
+ }
+
+ #[Test]
+ #[DataProvider('dataForValidateInvalidDomain')]
+ public function testValidateInvalidDomain(
+ string $oldDomain,
+ string $newDomain,
+ ?string $expectedException,
+ ?string $expectedExceptionMessage
+ ): void {
+
+ if ($expectedException !== null) {
+ $this->expectException($expectedException);
+ }
+
+ if ($expectedExceptionMessage !== null) {
+ $this->expectExceptionMessage($expectedExceptionMessage);
+ }
+
+ new Domain($oldDomain);
+ new Domain($newDomain);
+
+ }
+
+ public static function dataForValidateValidDomain(): \Generator
+ {
+
+ // Примеры допустимых доменов
+ /*$arrValidDomains = [
+ ['oldDomain' => 'example.com', 'newDomain' => 'example.org'],
+ ['oldDomain' => 'пример.рф', 'newDomain' => 'пример.рус'],
+ ['oldDomain' => 'test-site.org', 'newDomain' => 'test-site.ru'],
+ ['oldDomain' => 'valid-domain.co.uk', 'newDomain' => 'valid-domain.net'],
+ ['oldDomain' => 'subdomain.example.com', 'newDomain' => 'subdomain2.example.com'],
+ ['oldDomain' => 'тест.рус', 'newDomain' => 'тест2.рус'], // Пример с кириллицей
+ ];*/
+
+ yield 'validDomain1' => [
+ 'example.com',
+ 'example.org',
+ ];
+
+ yield 'validDomain2' => [
+ 'пример.рф',
+ 'пример.рус',
+ ];
+
+ yield 'validDomain3' => [
+ 'test-site.org',
+ 'test-site.ru',
+ ];
+
+ yield 'validDomain4' => [
+ 'valid-domain.co.uk',
+ 'valid-domain.net',
+ ];
+
+ yield 'validDomain5' => [
+ 'subdomain.example.com',
+ 'subdomain2.example.com',
+ ];
+
+ yield 'validDomain6' => [
+ 'тест.рус',
+ 'тест2.рус',
+ ];
+ }
+
+ public static function dataForValidateInvalidDomain(): \Generator
+ {
+ yield 'invalidDomain1' => [
+ 'invalid_domain.com', // Неправильный формат (подчеркивание)
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', 'invalid_domain.com')
+ ];
+
+ yield 'invalidDomain2' => [
+ '-invalid.com', // Домен не может начинаться с дефиса
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', '-invalid.com')
+ ];
+
+ yield 'invalidDomain3' => [
+ 'invalid-.com', // Домен не может заканчиваться на дефис
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', 'invalid-.com')
+ ];
+
+ yield 'invalidDomain4' => [
+ '123.456.789.0', // Неправильный формат (IP-адрес)
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', '123.456.789.0')
+ ];
+
+ yield 'invalidDomain5' => [
+ 'example..com', // Два подряд идущих точки
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', 'example..com')
+ ];
+
+ yield 'invalidDomain6' => [
+ 'example.c', // Слишком короткая доменная зона
+ 'valid.com',
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', 'example.c')
+ ];
+ }
+}
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php
new file mode 100644
index 0000000..44559ef
--- /dev/null
+++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php
@@ -0,0 +1,87 @@
+expectException($expectedException);
+ }
+
+ if (null !== $expectedExceptionMessage) {
+ $this->expectExceptionMessage($expectedExceptionMessage);
+ }
+
+ $domain = new Domain($domainUrl);
+ new Command(
+ $applicationToken,
+ $memberId,
+ $domain,
+ $bitrix24UserId
+ );
+ }
+
+ public static function dataForCommand(): \Generator
+ {
+ $applicationToken = Uuid::v7()->toRfc4122();
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::new)
+ ->build();
+
+ yield 'emptyApplicationToken' => [
+ '',
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getDomainUrl(),
+ $bitrix24Account->getBitrix24UserId(),
+ \InvalidArgumentException::class,
+ 'Application token cannot be empty.'
+ ];
+
+ yield 'emptyMemberId' => [
+ $applicationToken,
+ '',
+ $bitrix24Account->getDomainUrl(),
+ $bitrix24Account->getBitrix24UserId(),
+ \InvalidArgumentException::class,
+ 'Member ID cannot be empty.'
+ ];
+
+ yield 'invalidDomain' => [
+ $applicationToken,
+ $bitrix24Account->getMemberId(),
+ '',
+ $bitrix24Account->getBitrix24UserId(),
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', '')
+ ];
+ }
+}
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php
new file mode 100644
index 0000000..3369550
--- /dev/null
+++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php
@@ -0,0 +1,122 @@
+expectException($expectedException);
+ }
+
+ if (null !== $expectedExceptionMessage) {
+ $this->expectExceptionMessage($expectedExceptionMessage);
+ }
+
+ $domain = new Domain($domainUrl);
+
+ new Command(
+ $uuid,
+ $bitrix24UserId,
+ $isBitrix24UserAdmin,
+ $memberId,
+ $domain,
+ $authToken,
+ $applicationVersion,
+ $applicationScope
+ );
+
+ }
+
+ public static function dataForCommand(): \Generator
+ {
+ $bitrix24Account = (new Bitrix24AccountBuilder())
+ ->withStatus(Bitrix24AccountStatus::new)
+ ->build();
+
+ yield 'emptyMemberId' => [
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ '',
+ $bitrix24Account->getDomainUrl(),
+ $bitrix24Account->getAuthToken(),
+ $bitrix24Account->getApplicationVersion(),
+ $bitrix24Account->getApplicationScope(),
+ \InvalidArgumentException::class,
+ 'Member ID must be a non-empty string.'
+ ];
+
+ yield 'validDomainUrl' => [
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ '',
+ $bitrix24Account->getAuthToken(),
+ $bitrix24Account->getApplicationVersion(),
+ $bitrix24Account->getApplicationScope(),
+ \InvalidArgumentException::class,
+ sprintf('Invalid domain: %s', '')
+ ];
+
+ yield 'validBitrix24UserId' => [
+ $bitrix24Account->getId(),
+ 0,
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getDomainUrl(),
+ $bitrix24Account->getAuthToken(),
+ $bitrix24Account->getApplicationVersion(),
+ $bitrix24Account->getApplicationScope(),
+ \InvalidArgumentException::class,
+ 'Bitrix24 User ID must be a positive integer.'
+ ];
+
+ yield 'validApplicationToken' => [
+ $bitrix24Account->getId(),
+ $bitrix24Account->getBitrix24UserId(),
+ $bitrix24Account->isBitrix24UserAdmin(),
+ $bitrix24Account->getMemberId(),
+ $bitrix24Account->getDomainUrl(),
+ $bitrix24Account->getAuthToken(),
+ 0,
+ $bitrix24Account->getApplicationScope(),
+ \InvalidArgumentException::class,
+ 'Application version must be a positive integer.'
+ ];
+ }
+}
diff --git a/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php
new file mode 100644
index 0000000..257a99d
--- /dev/null
+++ b/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php
@@ -0,0 +1,62 @@
+assertEquals($applicationToken,$command->applicationToken);
+ }
+
+ #[Test]
+ #[DataProvider('dataForCommandEmptyToken')]
+ public function testEmptyTokenForCommand(
+ string $applicationToken,
+ ?string $expectedException,
+ ?string $expectedExceptionMessage,
+ ): void {
+ if (null !== $expectedException) {
+ $this->expectException($expectedException);
+ }
+
+ if (null !== $expectedExceptionMessage) {
+ $this->expectExceptionMessage($expectedExceptionMessage);
+ }
+
+ new Command($applicationToken);
+ }
+
+ public static function dataForCommandValidToken(): \Generator
+ {
+ yield 'validApplicationToken' => [
+ Uuid::v7()->toRfc4122()
+ ];
+ }
+
+ public static function dataForCommandEmptyToken(): \Generator
+ {
+ yield 'emptyApplicationToken' => [
+ '',
+ \InvalidArgumentException::class,
+ 'Application token must be a non-empty string.',
+ ];
+ }
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 94def9c..2f9c358 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -11,4 +11,24 @@
declare(strict_types=1);
-require_once dirname(__DIR__) . '/vendor/autoload.php';
\ No newline at end of file
+use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Dotenv\Dotenv;
+
+require_once dirname(__DIR__).'/vendor/autoload.php';
+
+require_once dirname(__DIR__).'/tests/EntityManagerFactory.php';
+
+if (!class_exists(Dotenv::class)) {
+ throw new LogicException('You need to add "symfony/dotenv" as Composer dependencies.');
+}
+
+$input = new ArgvInput();
+if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
+ putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
+}
+
+if ($input->hasParameterOption('--no-debug', true)) {
+ putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
+}
+
+(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');