From c9dbb8c24056da7acd37515930f246ae71d4ce02 Mon Sep 17 00:00:00 2001 From: Anton Zubariev Date: Fri, 4 Oct 2024 11:31:44 +0300 Subject: [PATCH] ACP-2930: Added merchant app onboarding removal on App disconnection (#11103) ACP-2930 Stripe App Disconnection Criteria --- resources/api/asyncapi.yml | 28 +++++ .../Transfer/merchant_app.transfer.xml | 6 + .../Business/MerchantAppBusinessFactory.php | 10 ++ .../Business/MerchantAppFacade.php | 16 +++ .../Business/MerchantAppFacadeInterface.php | 16 +++ .../AppConfigUpdatedMessageHandler.php | 45 ++++++++ ...ppConfigUpdatedMessageHandlerInterface.php | 20 ++++ ...chantAppOnboardingMessageHandlerPlugin.php | 16 +++ .../Persistence/MerchantAppEntityManager.php | 16 +++ .../MerchantAppEntityManagerInterface.php | 7 ++ .../AppEvents/AppConfigUpdatedTest.php | 105 ++++++++++++++++++ .../_support/Helper/MerchantAppHelper.php | 35 +++++- 12 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandler.php create mode 100644 src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandlerInterface.php create mode 100644 tests/SprykerTest/AsyncApi/MerchantApp/MerchantAppTests/AppEvents/AppConfigUpdatedTest.php diff --git a/resources/api/asyncapi.yml b/resources/api/asyncapi.yml index 2788905..1980b1b 100644 --- a/resources/api/asyncapi.yml +++ b/resources/api/asyncapi.yml @@ -9,6 +9,11 @@ channels: oneOf: - $ref: '#/components/messages/ReadyForMerchantAppOnboarding' - $ref: '#/components/messages/MerchantAppOnboardingStatusChanged' + app-events: + publish: + message: + oneOf: + - $ref: '#/components/messages/AppConfigUpdated' components: messages: @@ -34,6 +39,17 @@ components: payload: $ref: '#/components/schemas/MerchantAppOnboardingStatusChanged' + AppConfigUpdated: + x-spryker: + module: MerchantApp + name: AppConfigUpdated + title: Contains information about an updated App configuration. + summary: 'Contains information about an updated App configuration.' + headers: + $ref: '#/components/schemas/message-broker/components/schemas/headers' + payload: + $ref: '#/components/schemas/AppConfigUpdated' + schemas: ReadyForMerchantAppOnboarding: type: object @@ -118,5 +134,17 @@ components: - status - type + AppConfigUpdated: + type: object + properties: + appIdentifier: + type: string + description: The app identifier to identify the PBC. + isActive: + type: boolean + required: + - appIdentifier + - isActive + message-broker: $ref: 'https://raw.githubusercontent.com/spryker/message-broker/1.6.0/resources/api/template.yml' diff --git a/src/Spryker/Shared/MerchantApp/Transfer/merchant_app.transfer.xml b/src/Spryker/Shared/MerchantApp/Transfer/merchant_app.transfer.xml index f41ba72..2b5c1b5 100644 --- a/src/Spryker/Shared/MerchantApp/Transfer/merchant_app.transfer.xml +++ b/src/Spryker/Shared/MerchantApp/Transfer/merchant_app.transfer.xml @@ -113,4 +113,10 @@ + + + + + + diff --git a/src/Spryker/Zed/MerchantApp/Business/MerchantAppBusinessFactory.php b/src/Spryker/Zed/MerchantApp/Business/MerchantAppBusinessFactory.php index 04aff02..32a879d 100644 --- a/src/Spryker/Zed/MerchantApp/Business/MerchantAppBusinessFactory.php +++ b/src/Spryker/Zed/MerchantApp/Business/MerchantAppBusinessFactory.php @@ -14,6 +14,8 @@ use Spryker\Zed\MerchantApp\Business\MerchantAppOnboarding\MerchantAppOnboardingStatusInterface; use Spryker\Zed\MerchantApp\Business\MerchantAppOnboarding\MerchantAppOnboardingWriter; use Spryker\Zed\MerchantApp\Business\MerchantAppOnboarding\MerchantAppOnboardingWriterInterface; +use Spryker\Zed\MerchantApp\Business\MessageBroker\AppConfigUpdatedMessageHandler; +use Spryker\Zed\MerchantApp\Business\MessageBroker\AppConfigUpdatedMessageHandlerInterface; use Spryker\Zed\MerchantApp\Business\MessageBroker\MerchantAppOnboardingStatusChangedMessageHandler; use Spryker\Zed\MerchantApp\Business\MessageBroker\MerchantAppOnboardingStatusChangedMessageHandlerInterface; use Spryker\Zed\MerchantApp\Business\MessageBroker\ReadyForMerchantAppOnboardingMessageHandler; @@ -63,6 +65,14 @@ public function createMerchantAppOnboardingChangedMessageHandler(): MerchantAppO return new MerchantAppOnboardingStatusChangedMessageHandler($this->createMerchantAppOnboardingStatus()); } + /** + * @return \Spryker\Zed\MerchantApp\Business\MessageBroker\AppConfigUpdatedMessageHandlerInterface + */ + public function createAppConfigUpdatedMessageHandler(): AppConfigUpdatedMessageHandlerInterface + { + return new AppConfigUpdatedMessageHandler($this->getRepository(), $this->getEntityManager()); + } + /** * @return \Spryker\Zed\MerchantApp\Business\MerchantAppOnboarding\MerchantAppOnboardingInterface */ diff --git a/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacade.php b/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacade.php index b2d9d02..0614bb1 100644 --- a/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacade.php +++ b/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacade.php @@ -8,6 +8,7 @@ namespace Spryker\Zed\MerchantApp\Business; use Generated\Shared\Transfer\AcpHttpRequestTransfer; +use Generated\Shared\Transfer\AppConfigUpdatedTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingCollectionTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingCriteriaTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingInitializationRequestTransfer; @@ -51,6 +52,21 @@ public function handleMerchantAppOnboardingStatusChanged(MerchantAppOnboardingSt $this->getFactory()->createMerchantAppOnboardingChangedMessageHandler()->handleMerchantAppOnboardingStatusChanged($merchantOnboardingFailedTransfer); } + /** + * {@inheritDoc} + * + * @api + * + * @param \Generated\Shared\Transfer\AppConfigUpdatedTransfer $appConfigUpdatedTransfer + * + * @return void + */ + public function handleAppConfigUpdatedTransfer(AppConfigUpdatedTransfer $appConfigUpdatedTransfer): void + { + $this->getFactory()->createAppConfigUpdatedMessageHandler() + ->handleAppConfigUpdatedTransfer($appConfigUpdatedTransfer); + } + /** * {@inheritDoc} * diff --git a/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacadeInterface.php b/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacadeInterface.php index 111898f..f63180d 100644 --- a/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacadeInterface.php +++ b/src/Spryker/Zed/MerchantApp/Business/MerchantAppFacadeInterface.php @@ -8,6 +8,7 @@ namespace Spryker\Zed\MerchantApp\Business; use Generated\Shared\Transfer\AcpHttpRequestTransfer; +use Generated\Shared\Transfer\AppConfigUpdatedTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingCollectionTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingCriteriaTransfer; use Generated\Shared\Transfer\MerchantAppOnboardingInitializationRequestTransfer; @@ -43,6 +44,21 @@ public function handleReadyForMerchantAppOnboarding(ReadyForMerchantAppOnboardin */ public function handleMerchantAppOnboardingStatusChanged(MerchantAppOnboardingStatusChangedTransfer $merchantOnboardingFailedTransfer): void; + /** + * Specification: + * - When an App sends the AppConfigUpdatedTransfer message, this handler will be triggered. + * - Requires AppConfigUpdated.isActive to be set. + * - Does nothing if AppConfigUpdated.isActive === true. + * - Otherwise, requires AppConfigUpdated.appIdentifier to be set and removes all the MerchantApp onboarding data by AppConfigUpdated.appIdentifier. + * + * @api + * + * @param \Generated\Shared\Transfer\AppConfigUpdatedTransfer $appConfigUpdatedTransfer + * + * @return void + */ + public function handleAppConfigUpdatedTransfer(AppConfigUpdatedTransfer $appConfigUpdatedTransfer): void; + /** * Specification: * - Retrieves the onboarding details and status if exists. diff --git a/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandler.php b/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandler.php new file mode 100644 index 0000000..8aa74fd --- /dev/null +++ b/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandler.php @@ -0,0 +1,45 @@ +merchantAppRepository = $merchantAppRepository; + $this->merchantAppEntityManager = $merchantAppEntityManager; + } + + /** + * @param \Generated\Shared\Transfer\AppConfigUpdatedTransfer $appConfigUpdatedTransfer + * + * @return void + */ + public function handleAppConfigUpdatedTransfer(AppConfigUpdatedTransfer $appConfigUpdatedTransfer): void + { + if ($appConfigUpdatedTransfer->getIsActiveOrFail()) { + return; + } + + $this->merchantAppEntityManager->deleteMerchantAppOnboardingByAppIdentifier($appConfigUpdatedTransfer->getAppIdentifierOrFail()); + } +} diff --git a/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandlerInterface.php b/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandlerInterface.php new file mode 100644 index 0000000..5e7a63b --- /dev/null +++ b/src/Spryker/Zed/MerchantApp/Business/MessageBroker/AppConfigUpdatedMessageHandlerInterface.php @@ -0,0 +1,20 @@ +getFacade()->handleMerchantAppOnboardingStatusChanged($merchantOnboardingStatusChangedTransfer); } + /** + * {@inheritDoc} + * + * @api + * + * @param \Generated\Shared\Transfer\AppConfigUpdatedTransfer $appConfigUpdatedTransfer + * + * @return void + */ + public function onAppConfigUpdated(AppConfigUpdatedTransfer $appConfigUpdatedTransfer): void + { + $this->getFacade()->handleAppConfigUpdatedTransfer($appConfigUpdatedTransfer); + } + /** * {@inheritDoc} * Return an array where the key is the class name to be handled and the value is the callable that handles the message. @@ -58,5 +73,6 @@ public function handles(): iterable { yield ReadyForMerchantAppOnboardingTransfer::class => [$this, 'onReadyForMerchantAppOnboarding']; yield MerchantAppOnboardingStatusChangedTransfer::class => [$this, 'onMerchantAppOnboardingStatusChanged']; + yield AppConfigUpdatedTransfer::class => [$this, 'onAppConfigUpdated']; } } diff --git a/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManager.php b/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManager.php index bc7a02f..5982e92 100644 --- a/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManager.php +++ b/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManager.php @@ -60,4 +60,20 @@ public function persistAppMerchantAppOnboardingStatus(MerchantAppOnboardingStatu $merchantAppOnboardingStatusEntity->save(); } + + /** + * @param string $appIdentifier + * + * @return void + */ + public function deleteMerchantAppOnboardingByAppIdentifier(string $appIdentifier): void + { + $merchantAppOnboardingEntities = $this->getFactory()->createMerchantAppOnboardingQuery() + ->findByAppIdentifier($appIdentifier); + + foreach ($merchantAppOnboardingEntities as $appOnboardingEntity) { + $appOnboardingEntity->getSpyMerchantAppOnboardingStatuses()->delete(); + $appOnboardingEntity->delete(); + } + } } diff --git a/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManagerInterface.php b/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManagerInterface.php index 361492b..3c55356 100644 --- a/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManagerInterface.php +++ b/src/Spryker/Zed/MerchantApp/Persistence/MerchantAppEntityManagerInterface.php @@ -25,4 +25,11 @@ public function persistAppMerchantAppOnboarding(ReadyForMerchantAppOnboardingTra * @return void */ public function persistAppMerchantAppOnboardingStatus(MerchantAppOnboardingStatusTransfer $merchantAppOnboardingStatus): void; + + /** + * @param string $appIdentifier + * + * @return void + */ + public function deleteMerchantAppOnboardingByAppIdentifier(string $appIdentifier): void; } diff --git a/tests/SprykerTest/AsyncApi/MerchantApp/MerchantAppTests/AppEvents/AppConfigUpdatedTest.php b/tests/SprykerTest/AsyncApi/MerchantApp/MerchantAppTests/AppEvents/AppConfigUpdatedTest.php new file mode 100644 index 0000000..ea97e92 --- /dev/null +++ b/tests/SprykerTest/AsyncApi/MerchantApp/MerchantAppTests/AppEvents/AppConfigUpdatedTest.php @@ -0,0 +1,105 @@ +toString(); + $merchantTransfer = $this->tester->haveMerchant(); + $this->tester->haveAppConfigPersisted([AppConfigTransfer::APP_IDENTIFIER => $appIdentifier]); + + $merchantAppOnboardingTransfer = $this->tester->haveMerchantAppOnboardingPersisted([ + MerchantAppOnboardingTransfer::APP_IDENTIFIER => $appIdentifier, + ]); + + $merchantAppOnboardingStatusTransfer = $this->tester->haveMerchantAppOnboardingStatusPersisted([ + MerchantAppOnboardingStatusTransfer::MERCHANT_REFERENCE => $merchantTransfer->getMerchantReference(), + MerchantAppOnboardingStatusTransfer::MERCHANT_APP_ONBOARDING => [ + MerchantAppOnboardingTransfer::APP_IDENTIFIER => $merchantAppOnboardingTransfer->getAppIdentifier(), + MerchantAppOnboardingTransfer::TYPE => $merchantAppOnboardingTransfer->getType(), + ], + ]); + + $appConfigUpdatedTransfer = $this->tester->haveAppConfigUpdatedTransfer([ + AppConfigUpdatedTransfer::IS_ACTIVE => false, + AppConfigUpdatedTransfer::APP_IDENTIFIER => $appIdentifier, + ]); + + // Act: This will trigger the MessageHandlerPlugin for this message. + $this->tester->runMessageReceiveTest($appConfigUpdatedTransfer, 'app-events'); + + // Assert + $this->tester->dontSeeMerchantAppOnboardingStatusEntityInDatabase($merchantAppOnboardingStatusTransfer); + $this->tester->dontSeeMerchantAppOnboardingEntityInDatabase($merchantAppOnboardingTransfer); + } + + /** + * @return void + */ + public function testMerchantAppOnboardingDataStillExistsWhenAppConfigUpdatedMessageWithActiveStatusIsHandled(): void + { + // Arrange + $appIdentifier = Uuid::uuid4()->toString(); + $merchantTransfer = $this->tester->haveMerchant(); + $this->tester->haveAppConfigPersisted([AppConfigTransfer::APP_IDENTIFIER => $appIdentifier]); + + $merchantAppOnboardingTransfer = $this->tester->haveMerchantAppOnboardingPersisted([ + MerchantAppOnboardingTransfer::APP_IDENTIFIER => $appIdentifier, + ]); + + $merchantAppOnboardingStatusTransfer = $this->tester->haveMerchantAppOnboardingStatusPersisted([ + MerchantAppOnboardingStatusTransfer::MERCHANT_REFERENCE => $merchantTransfer->getMerchantReference(), + MerchantAppOnboardingStatusTransfer::MERCHANT_APP_ONBOARDING => [ + MerchantAppOnboardingTransfer::APP_IDENTIFIER => $merchantAppOnboardingTransfer->getAppIdentifier(), + MerchantAppOnboardingTransfer::TYPE => $merchantAppOnboardingTransfer->getType(), + ], + ]); + + $appConfigUpdatedTransfer = $this->tester->haveAppConfigUpdatedTransfer([ + AppConfigUpdatedTransfer::IS_ACTIVE => true, + AppConfigUpdatedTransfer::APP_IDENTIFIER => $appIdentifier, + ]); + + // Act: This will trigger the MessageHandlerPlugin for this message. + $this->tester->runMessageReceiveTest($appConfigUpdatedTransfer, 'app-events'); + + // Assert + $this->tester->seeMerchantAppOnboardingStatusEntityInDatabase($merchantAppOnboardingStatusTransfer); + $this->tester->seeMerchantAppOnboardingEntityInDatabase($merchantAppOnboardingTransfer); + } +} diff --git a/tests/SprykerTest/Shared/MerchantApp/_support/Helper/MerchantAppHelper.php b/tests/SprykerTest/Shared/MerchantApp/_support/Helper/MerchantAppHelper.php index 5aa87c6..f64e96e 100644 --- a/tests/SprykerTest/Shared/MerchantApp/_support/Helper/MerchantAppHelper.php +++ b/tests/SprykerTest/Shared/MerchantApp/_support/Helper/MerchantAppHelper.php @@ -139,12 +139,13 @@ public function haveMerchantAppOnboardingStatusPersisted(array $seed = []): Merc } /** - * @param \Generated\Shared\Transfer\ReadyForMerchantAppOnboardingTransfer $readyForMerchantAppOnboardingTransfer + * @param \Generated\Shared\Transfer\ReadyForMerchantAppOnboardingTransfer|\Generated\Shared\Transfer\MerchantAppOnboardingTransfer $readyForMerchantAppOnboardingTransfer * * @return void */ - public function seeMerchantAppOnboardingEntityInDatabase(ReadyForMerchantAppOnboardingTransfer $readyForMerchantAppOnboardingTransfer): void - { + public function seeMerchantAppOnboardingEntityInDatabase( + ReadyForMerchantAppOnboardingTransfer|MerchantAppOnboardingTransfer $readyForMerchantAppOnboardingTransfer + ): void { $spyMerchantAppOnboardingEntity = SpyMerchantAppOnboardingQuery::create() ->filterByType($readyForMerchantAppOnboardingTransfer->getType()) ->filterByAppName($readyForMerchantAppOnboardingTransfer->getAppName()) @@ -188,4 +189,32 @@ public function seeMerchantAppOnboardingStatusEntityInDatabase( )); } } + + /** + * @param \Generated\Shared\Transfer\MerchantAppOnboardingStatusTransfer $merchantAppOnboardingStatusTransfer + * + * @return void + */ + public function dontSeeMerchantAppOnboardingStatusEntityInDatabase(MerchantAppOnboardingStatusTransfer $merchantAppOnboardingStatusTransfer): void + { + $spyMerchantAppOnboardingStatusEntity = SpyMerchantAppOnboardingStatusQuery::create() + ->filterByMerchantReference($merchantAppOnboardingStatusTransfer->getMerchantReference()) + ->findOne(); + + $this->assertNull($spyMerchantAppOnboardingStatusEntity, 'Expected not to find MerchantAppOnboardingStatus entity in the database but it was found.'); + } + + /** + * @param \Generated\Shared\Transfer\MerchantAppOnboardingTransfer $merchantAppOnboardingTransfer + * + * @return void + */ + public function dontSeeMerchantAppOnboardingEntityInDatabase(MerchantAppOnboardingTransfer $merchantAppOnboardingTransfer): void + { + $spyMerchantAppOnboardingEntity = SpyMerchantAppOnboardingQuery::create() + ->filterByAppIdentifier($merchantAppOnboardingTransfer->getAppIdentifier()) + ->findOne(); + + $this->assertNull($spyMerchantAppOnboardingEntity, 'Expected not to find MerchantAppOnboarding entity in the database but it was found.'); + } }